From a067ba12286b2db0ad6abd7c836102b48e8074df Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 10:19:39 -0300
Subject: [PATCH 01/15] Add new CoreHelpService and method help docs
Adds all the gRPC server boilerplate, and a simple help service
that serves method help in man page format. Help text is maintained
in text files located in core/src/main/resources/help.
Only some of the method help text files are defined in this
change, more to be added.
---
core/src/main/java/bisq/core/api/CoreApi.java | 13 ++-
.../java/bisq/core/api/CoreHelpService.java | 103 ++++++++++++++++++
.../main/resources/help/createoffer-help.txt | 64 +++++++++++
.../help/getfundingaddresses-help.txt | 17 +++
.../resources/help/getpaymentaccts-help.txt | 17 +++
.../resources/help/getpaymentmethods-help.txt | 17 +++
.../main/resources/help/gettxfeerate-help.txt | 17 +++
.../help/getunusedbsqaddress-help.txt | 17 +++
.../main/resources/help/getversion-help.txt | 17 +++
.../main/resources/help/lockwallet-help.txt | 17 +++
.../main/resources/help/takeoffer-help.txt | 35 ++++++
.../resources/help/unsettxfeerate-help.txt | 17 +++
.../bisq/daemon/grpc/GrpcHelpService.java | 56 ++++++++++
.../java/bisq/daemon/grpc/GrpcServer.java | 2 +
proto/src/main/proto/grpc.proto | 17 +++
15 files changed, 425 insertions(+), 1 deletion(-)
create mode 100644 core/src/main/java/bisq/core/api/CoreHelpService.java
create mode 100644 core/src/main/resources/help/createoffer-help.txt
create mode 100644 core/src/main/resources/help/getfundingaddresses-help.txt
create mode 100644 core/src/main/resources/help/getpaymentaccts-help.txt
create mode 100644 core/src/main/resources/help/getpaymentmethods-help.txt
create mode 100644 core/src/main/resources/help/gettxfeerate-help.txt
create mode 100644 core/src/main/resources/help/getunusedbsqaddress-help.txt
create mode 100644 core/src/main/resources/help/getversion-help.txt
create mode 100644 core/src/main/resources/help/lockwallet-help.txt
create mode 100644 core/src/main/resources/help/takeoffer-help.txt
create mode 100644 core/src/main/resources/help/unsettxfeerate-help.txt
create mode 100644 daemon/src/main/java/bisq/daemon/grpc/GrpcHelpService.java
diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java
index bbd12b7ccbd..bfc814c67a6 100644
--- a/core/src/main/java/bisq/core/api/CoreApi.java
+++ b/core/src/main/java/bisq/core/api/CoreApi.java
@@ -62,6 +62,7 @@ public class CoreApi {
@Getter
private final Config config;
private final CoreDisputeAgentsService coreDisputeAgentsService;
+ private final CoreHelpService coreHelpService;
private final CoreOffersService coreOffersService;
private final CorePaymentAccountsService paymentAccountsService;
private final CorePriceService corePriceService;
@@ -72,7 +73,7 @@ public class CoreApi {
@Inject
public CoreApi(Config config,
CoreDisputeAgentsService coreDisputeAgentsService,
- CoreOffersService coreOffersService,
+ CoreHelpService coreHelpService, CoreOffersService coreOffersService,
CorePaymentAccountsService paymentAccountsService,
CorePriceService corePriceService,
CoreTradesService coreTradesService,
@@ -80,6 +81,7 @@ public CoreApi(Config config,
TradeStatisticsManager tradeStatisticsManager) {
this.config = config;
this.coreDisputeAgentsService = coreDisputeAgentsService;
+ this.coreHelpService = coreHelpService;
this.coreOffersService = coreOffersService;
this.paymentAccountsService = paymentAccountsService;
this.coreTradesService = coreTradesService;
@@ -101,6 +103,15 @@ public void registerDisputeAgent(String disputeAgentType, String registrationKey
coreDisputeAgentsService.registerDisputeAgent(disputeAgentType, registrationKey);
}
+
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Help
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ public String getMethodHelp(String methodName) {
+ return coreHelpService.getMethodHelp(methodName);
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////
// Offers
///////////////////////////////////////////////////////////////////////////////////////////
diff --git a/core/src/main/java/bisq/core/api/CoreHelpService.java b/core/src/main/java/bisq/core/api/CoreHelpService.java
new file mode 100644
index 00000000000..a294dcad627
--- /dev/null
+++ b/core/src/main/java/bisq/core/api/CoreHelpService.java
@@ -0,0 +1,103 @@
+/*
+ * 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.api;
+
+import javax.inject.Inject;
+import javax.inject.Singleton;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import lombok.extern.slf4j.Slf4j;
+
+import static java.io.File.separator;
+import static java.lang.String.format;
+import static java.lang.System.out;
+
+@Singleton
+@Slf4j
+class CoreHelpService {
+
+ @Inject
+ public CoreHelpService() {
+ }
+
+ public String getMethodHelp(String methodName) {
+ switch (methodName) {
+ case "createoffer":
+ case "getfundingaddresses":
+ case "getpaymentaccts":
+ case "getpaymentmethods":
+ case "gettxfeerate":
+ case "getunusedbsqaddress":
+ case "getversion":
+ case "lockwallet":
+ case "takeoffer":
+ case "unsettxfeerate":
+ return getHelpText(methodName);
+ default:
+ throw new IllegalStateException("no help found for " + methodName);
+ }
+ }
+
+ private String getHelpText(String methodName) {
+ String resourceFile = "/help" + separator + methodName + "-" + "help.txt";
+ try {
+ return readHelpFile(resourceFile);
+ } catch (NullPointerException ex) {
+ log.error("", ex);
+ throw new IllegalStateException(format("could not find %s help doc", methodName));
+ } catch (IOException ex) {
+ log.error("", ex);
+ throw new IllegalStateException(format("could not read %s help doc", methodName));
+ }
+ }
+
+ private String readHelpFile(String resourceFile) throws NullPointerException, IOException {
+ // The deployed text file is in the core.jar file, so use
+ // Class.getResourceAsStream to read it.
+ InputStream is = getClass().getResourceAsStream(resourceFile);
+ BufferedReader br = new BufferedReader(new InputStreamReader(is));
+ String line;
+ StringBuilder builder = new StringBuilder();
+ while ((line = br.readLine()) != null)
+ builder.append(line).append("\n");
+
+ return builder.toString();
+ }
+
+ // Main method for devs to view help text without running the server.
+ @SuppressWarnings("CommentedOutCode")
+ public static void main(String[] args) {
+ CoreHelpService coreHelpService = new CoreHelpService();
+ // out.println(coreHelpService.getMethodHelp("getversion"));
+ // out.println(coreHelpService.getMethodHelp("getfundingaddresses"));
+ // out.println(coreHelpService.getMethodHelp("getfundingaddresses"));
+ // out.println(coreHelpService.getMethodHelp("getunusedbsqaddress"));
+ // out.println(coreHelpService.getMethodHelp("unsettxfeerate"));
+ // out.println(coreHelpService.getMethodHelp("getpaymentmethods"));
+ // out.println(coreHelpService.getMethodHelp("getpaymentaccts"));
+ // out.println(coreHelpService.getMethodHelp("lockwallet"));
+ // out.println(coreHelpService.getMethodHelp("gettxfeerate"));
+ out.println(coreHelpService.getMethodHelp("createoffer"));
+ // out.println(coreHelpService.getMethodHelp("takeoffer"));
+ // out.println(coreHelpService.getMethodHelp("garbage"));
+ }
+}
diff --git a/core/src/main/resources/help/createoffer-help.txt b/core/src/main/resources/help/createoffer-help.txt
new file mode 100644
index 00000000000..536e2596b65
--- /dev/null
+++ b/core/src/main/resources/help/createoffer-help.txt
@@ -0,0 +1,64 @@
+createoffer
+
+NAME
+----
+createoffer - create offer to buy or sell BTC
+
+SYNOPSIS
+--------
+createoffer
+ --payment-account=
+ --direction=
+ --currency-code=
+ --market-price-margin= | --fixed-price=
+ --amount=
+ --min-amount=
+ --security-deposit=
+ [--fee-currency=]
+
+DESCRIPTION
+-----------
+Create and place an offer to buy or sell BTC using a fiat account.
+
+OPTIONS
+-------
+--payment-account
+ The ID of the fiat payment account used to send or receive funds during the trade.
+
+--direction
+ The direction of the trade (BUY or SELL).
+
+--currency-code
+ The three letter code for the fiat used to buy or sell BTC, e.g., EUR, USD, BRL, ...
+
+--market-price-margin
+ The % above or below market BTC price, e.g., 1.00 (1%).
+ If --market-price-margin is not present, --fixed-price must be.
+
+--fixed-price
+ The fixed BTC price in fiat used to buy or sell BTC, e.g., 34000 (USD).
+ If --fixed-price is not present, --market-price-margin must be.
+
+--amount
+ The amount of BTC to buy or sell, e.g., 0.125.
+
+--min-amount
+ The minimum amount of BTC to buy or sell, e.g., 0.006.
+ If --min-amount is not present, it defaults to the --amount value.
+
+--security-deposit
+ The percentage of the BTC amount being traded for the security deposit, e.g., 60.0 (60%).
+
+--fee-currency
+ The wallet currency used to pay the Bisq trade maker fee (BSQ|BTC). Default is BTC
+
+EXAMPLES
+--------
+To create a BUY 0.125 BTC with EUR offer
+ at the current market price,
+ using a payment account with ID 7413d263-225a-4f1b-837a-1e3094dc0d77,
+ putting up a 30 percent security deposit,
+ and paying the Bisq maker trading fee in BSQ:
+$ ./bisq-cli --password=xyz --port=9998 createoffer --payment-account=7413d263-225a-4f1b-837a-1e3094dc0d77 --direction=buy --currency-code=eur --amount=0.125 --market-price-margin=0.00 --security-deposit=30.0 --fee-currency=bsq
+
+ (TODO another 3 examples: selling @ mkt price, buying a fixed price, selling at fixed price...)
diff --git a/core/src/main/resources/help/getfundingaddresses-help.txt b/core/src/main/resources/help/getfundingaddresses-help.txt
new file mode 100644
index 00000000000..0d3e7ea6292
--- /dev/null
+++ b/core/src/main/resources/help/getfundingaddresses-help.txt
@@ -0,0 +1,17 @@
+getfundingaddresses
+
+NAME
+----
+getfundingaddresses - list BTC receiving address
+
+SYNOPSIS
+--------
+getfundingaddresses
+
+DESCRIPTION
+-----------
+Returns a list of receiving BTC addresses.
+
+EXAMPLES
+--------
+$ ./bisq-cli --password=xyz --port=9998 getfundingaddresses
diff --git a/core/src/main/resources/help/getpaymentaccts-help.txt b/core/src/main/resources/help/getpaymentaccts-help.txt
new file mode 100644
index 00000000000..f12b5bd4534
--- /dev/null
+++ b/core/src/main/resources/help/getpaymentaccts-help.txt
@@ -0,0 +1,17 @@
+getpaymentaccts
+
+NAME
+----
+getpaymentaccts - list user payment accounts
+
+SYNOPSIS
+--------
+getpaymentaccts
+
+DESCRIPTION
+-----------
+Returns the list of user payment accounts.
+
+EXAMPLES
+--------
+$ ./bisq-cli --password=xyz --port=9998 getpaymentaccts
diff --git a/core/src/main/resources/help/getpaymentmethods-help.txt b/core/src/main/resources/help/getpaymentmethods-help.txt
new file mode 100644
index 00000000000..b7f860548df
--- /dev/null
+++ b/core/src/main/resources/help/getpaymentmethods-help.txt
@@ -0,0 +1,17 @@
+getpaymentmethods
+
+NAME
+----
+getpaymentmethods - list fiat payment methods
+
+SYNOPSIS
+--------
+getpaymentmethods
+
+DESCRIPTION
+-----------
+Returns a list of currently supported fiat payment method IDs.
+
+EXAMPLES
+--------
+$ ./bisq-cli --password=xyz --port=9998 getpaymentmethods
diff --git a/core/src/main/resources/help/gettxfeerate-help.txt b/core/src/main/resources/help/gettxfeerate-help.txt
new file mode 100644
index 00000000000..3582d5dcf09
--- /dev/null
+++ b/core/src/main/resources/help/gettxfeerate-help.txt
@@ -0,0 +1,17 @@
+gettxfeerate
+
+NAME
+----
+gettxfeerate - get transaction fee rate
+
+SYNOPSIS
+--------
+gettxfeerate
+
+DESCRIPTION
+-----------
+Returns the most recent bitcoin network transaction fee the Bisq server could find.
+
+EXAMPLES
+--------
+$ ./bisq-cli --password=xyz --port=9998 gettxfeerate
diff --git a/core/src/main/resources/help/getunusedbsqaddress-help.txt b/core/src/main/resources/help/getunusedbsqaddress-help.txt
new file mode 100644
index 00000000000..308ad01ba7b
--- /dev/null
+++ b/core/src/main/resources/help/getunusedbsqaddress-help.txt
@@ -0,0 +1,17 @@
+getunusedbsqaddress
+
+NAME
+----
+getunusedbsqaddress - get BSQ receiving address
+
+SYNOPSIS
+--------
+getunusedbsqaddress
+
+DESCRIPTION
+-----------
+Returns an unused BSQ receiving address.
+
+EXAMPLES
+--------
+$ ./bisq-cli --password=xyz --port=9998 getunusedbsqaddress
diff --git a/core/src/main/resources/help/getversion-help.txt b/core/src/main/resources/help/getversion-help.txt
new file mode 100644
index 00000000000..ce3b801db4b
--- /dev/null
+++ b/core/src/main/resources/help/getversion-help.txt
@@ -0,0 +1,17 @@
+getversion
+
+NAME
+----
+getversion - get server version
+
+SYNOPSIS
+--------
+getversion
+
+DESCRIPTION
+-----------
+Returns the Bisq server version.
+
+EXAMPLES
+--------
+$ ./bisq-cli --password=xyz --port=9998 getversion
diff --git a/core/src/main/resources/help/lockwallet-help.txt b/core/src/main/resources/help/lockwallet-help.txt
new file mode 100644
index 00000000000..6639dcef5ea
--- /dev/null
+++ b/core/src/main/resources/help/lockwallet-help.txt
@@ -0,0 +1,17 @@
+lockwallet
+
+NAME
+----
+lockwallet - lock Bisq wallet
+
+SYNOPSIS
+--------
+lockwallet
+
+DESCRIPTION
+-----------
+Locks an unlocked wallet before an unlockwallet timeout expires.
+
+EXAMPLES
+--------
+$ ./bisq-cli --password=xyz --port=9998 lockwallet
diff --git a/core/src/main/resources/help/takeoffer-help.txt b/core/src/main/resources/help/takeoffer-help.txt
new file mode 100644
index 00000000000..89ab21fc9c0
--- /dev/null
+++ b/core/src/main/resources/help/takeoffer-help.txt
@@ -0,0 +1,35 @@
+takeoffer
+
+NAME
+----
+takeoffer - take an offer to buy or sell BTC
+
+SYNOPSIS
+--------
+takeoffer
+ --offer-id=
+ --payment-account=
+ --fee-currency=
+
+DESCRIPTION
+-----------
+Take an existing offer using a matching payment method. The Bisq trade fee can be paid in BSQ or BTC.
+
+OPTIONS
+-------
+--offer-id
+ The ID of the buy or sell offer to take.
+
+--payment-account
+ The ID of the fiat payment account used to send or receive funds during the trade.
+ The payment account's payment method must match that of the offer.
+
+--fee-currency
+ The wallet currency used to pay the Bisq trade taker fee (BSQ|BTC). Default is BTC
+
+EXAMPLES
+--------
+To take an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea
+ using a payment account with ID fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e,
+ and paying the Bisq trading fee in BSQ:
+$ ./bisq-cli --password=xyz --port=9998 takeoffer -offer-id=83e8b2e2-51b6-4f39-a748-3ebd29c22aea -payment-account=fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e -fee-currency=bsq
diff --git a/core/src/main/resources/help/unsettxfeerate-help.txt b/core/src/main/resources/help/unsettxfeerate-help.txt
new file mode 100644
index 00000000000..36767e6f1f0
--- /dev/null
+++ b/core/src/main/resources/help/unsettxfeerate-help.txt
@@ -0,0 +1,17 @@
+unsettxfeerate
+
+NAME
+----
+unsettxfeerate - unset transaction fee rate preference
+
+SYNOPSIS
+--------
+unsettxfeerate
+
+DESCRIPTION
+-----------
+Unsets (removes) the transaction fee rate user preference.
+
+EXAMPLES
+--------
+$ ./bisq-cli --password=xyz --port=9998 unsettxfeerate
diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcHelpService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcHelpService.java
new file mode 100644
index 00000000000..1a62ed6f9f6
--- /dev/null
+++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcHelpService.java
@@ -0,0 +1,56 @@
+/*
+ * 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.grpc;
+
+import bisq.core.api.CoreApi;
+
+import bisq.proto.grpc.GetMethodHelpReply;
+import bisq.proto.grpc.GetMethodHelpRequest;
+import bisq.proto.grpc.HelpGrpc;
+
+import io.grpc.stub.StreamObserver;
+
+import javax.inject.Inject;
+
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+class GrpcHelpService extends HelpGrpc.HelpImplBase {
+
+ private final CoreApi coreApi;
+ private final GrpcExceptionHandler exceptionHandler;
+
+ @Inject
+ public GrpcHelpService(CoreApi coreApi, GrpcExceptionHandler exceptionHandler) {
+ this.coreApi = coreApi;
+ this.exceptionHandler = exceptionHandler;
+ }
+
+ @Override
+ public void getMethodHelp(GetMethodHelpRequest req,
+ StreamObserver responseObserver) {
+ try {
+ String helpText = coreApi.getMethodHelp(req.getMethodName());
+ var reply = GetMethodHelpReply.newBuilder().setMethodHelp(helpText).build();
+ responseObserver.onNext(reply);
+ responseObserver.onCompleted();
+ } catch (Throwable cause) {
+ exceptionHandler.handleException(cause, responseObserver);
+ }
+ }
+}
diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcServer.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcServer.java
index 589645d77dd..8e50a98aa1f 100644
--- a/daemon/src/main/java/bisq/daemon/grpc/GrpcServer.java
+++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcServer.java
@@ -50,6 +50,7 @@ public GrpcServer(CoreContext coreContext,
Config config,
PasswordAuthInterceptor passwordAuthInterceptor,
GrpcDisputeAgentsService disputeAgentsService,
+ GrpcHelpService helpService,
GrpcOffersService offersService,
GrpcPaymentAccountsService paymentAccountsService,
GrpcPriceService priceService,
@@ -60,6 +61,7 @@ public GrpcServer(CoreContext coreContext,
this.server = ServerBuilder.forPort(config.apiPort)
.executor(UserThread.getExecutor())
.addService(disputeAgentsService)
+ .addService(helpService)
.addService(offersService)
.addService(paymentAccountsService)
.addService(priceService)
diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto
index 3ac2ea4bf61..085c3f90741 100644
--- a/proto/src/main/proto/grpc.proto
+++ b/proto/src/main/proto/grpc.proto
@@ -40,6 +40,23 @@ message RegisterDisputeAgentRequest {
message RegisterDisputeAgentReply {
}
+///////////////////////////////////////////////////////////////////////////////////////////
+// Help
+///////////////////////////////////////////////////////////////////////////////////////////
+
+service Help {
+ rpc GetMethodHelp (GetMethodHelpRequest) returns (GetMethodHelpReply) {
+ }
+}
+
+message GetMethodHelpRequest {
+ string methodName = 1;
+}
+
+message GetMethodHelpReply {
+ string methodHelp = 1;
+}
+
///////////////////////////////////////////////////////////////////////////////////////////
// Offers
///////////////////////////////////////////////////////////////////////////////////////////
From 49a3b46960df6de01d6d94d026d7fde692823d15 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 10:22:14 -0300
Subject: [PATCH 02/15] Add CoreHelpService gRPC stubs and test case
---
apitest/scripts/mainnet-test.sh | 26 ++++----
.../apitest/method/GetMethodHelpTest.java | 65 +++++++++++++++++++
.../java/bisq/apitest/method/MethodTest.java | 10 +++
.../bisq/apitest/scenario/StartupTest.java | 8 +++
cli/src/main/java/bisq/cli/GrpcStubs.java | 3 +
5 files changed, 99 insertions(+), 13 deletions(-)
create mode 100644 apitest/src/test/java/bisq/apitest/method/GetMethodHelpTest.java
diff --git a/apitest/scripts/mainnet-test.sh b/apitest/scripts/mainnet-test.sh
index 48fe4023bf1..d643ee716e0 100755
--- a/apitest/scripts/mainnet-test.sh
+++ b/apitest/scripts/mainnet-test.sh
@@ -27,7 +27,7 @@
run ./bisq-cli --bogus getversion
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
- [ "$output" = "Error: bogus is not a recognized option" ]
+ [ "$output" = "Error: missing required 'password' option" ]
}
@test "test missing required password option error" {
@@ -61,7 +61,7 @@
}
@test "test setwalletpassword \"a b c\"" {
- run ./bisq-cli --password=xyz setwalletpassword "a b c"
+ run ./bisq-cli --password=xyz setwalletpassword --wallet-password="a b c"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet encrypted" ]
@@ -76,7 +76,7 @@
}
@test "test unlockwallet without timeout arg" {
- run ./bisq-cli --password=xyz unlockwallet "a b c"
+ run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c"
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: no unlock timeout specified" ]
@@ -84,7 +84,7 @@
@test "test unlockwallet \"a b c\" 8" {
- run ./bisq-cli --password=xyz unlockwallet "a b c" 8
+ run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c" --timeout=8
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet unlocked" ]
@@ -97,7 +97,7 @@
}
@test "test unlockwallet \"a b c\" 6" {
- run ./bisq-cli --password=xyz unlockwallet "a b c" 6
+ run ./bisq-cli --password=xyz unlockwallet --wallet-password="a b c" --timeout=6
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet unlocked" ]
@@ -111,14 +111,14 @@
}
@test "test setwalletpassword incorrect old pwd error" {
- run ./bisq-cli --password=xyz setwalletpassword "z z z" "d e f"
+ run ./bisq-cli --password=xyz setwalletpassword --wallet-password="z z z" --new-wallet-password="d e f"
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: incorrect old password" ]
}
@test "test setwalletpassword oldpwd newpwd" {
- run ./bisq-cli --password=xyz setwalletpassword "a b c" "d e f"
+ run ./bisq-cli --password=xyz setwalletpassword --wallet-password="a b c" --new-wallet-password="d e f"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet encrypted with new password" ]
@@ -133,7 +133,7 @@
}
@test "test removewalletpassword" {
- run ./bisq-cli --password=xyz removewalletpassword "d e f"
+ run ./bisq-cli --password=xyz removewalletpassword --wallet-password="d e f"
[ "$status" -eq 0 ]
echo "actual output: $output" >&2
[ "$output" = "wallet decrypted" ]
@@ -163,7 +163,7 @@
}
@test "test getaddressbalance bogus address argument" {
- run ./bisq-cli --password=xyz getaddressbalance bogus
+ run ./bisq-cli --password=xyz getaddressbalance --address=bogus
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
[ "$output" = "Error: address bogus not found in wallet" ]
@@ -183,21 +183,21 @@
run ./bisq-cli --password=xyz getoffers
[ "$status" -eq 1 ]
echo "actual output: $output" >&2
- [ "$output" = "Error: incorrect parameter count, expecting direction (buy|sell), currency code" ]
+ [ "$output" = "Error: no direction (buy|sell) specified" ]
}
@test "test getoffers sell eur check return status" {
- run ./bisq-cli --password=xyz getoffers sell eur
+ run ./bisq-cli --password=xyz getoffers --direction=sell --currency-code=eur
[ "$status" -eq 0 ]
}
@test "test getoffers buy eur check return status" {
- run ./bisq-cli --password=xyz getoffers buy eur
+ run ./bisq-cli --password=xyz getoffers --direction=buy --currency-code=eur
[ "$status" -eq 0 ]
}
@test "test getoffers sell gbp check return status" {
- run ./bisq-cli --password=xyz getoffers sell gbp
+ run ./bisq-cli --password=xyz getoffers --direction=sell --currency-code=gbp
[ "$status" -eq 0 ]
}
diff --git a/apitest/src/test/java/bisq/apitest/method/GetMethodHelpTest.java b/apitest/src/test/java/bisq/apitest/method/GetMethodHelpTest.java
new file mode 100644
index 00000000000..99e7b4873f2
--- /dev/null
+++ b/apitest/src/test/java/bisq/apitest/method/GetMethodHelpTest.java
@@ -0,0 +1,65 @@
+/*
+ * 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.apitest.method;
+
+import bisq.proto.grpc.GetMethodHelpRequest;
+
+import lombok.extern.slf4j.Slf4j;
+
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Disabled;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+import static bisq.apitest.config.BisqAppConfig.alicedaemon;
+import static bisq.cli.Method.createoffer;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.fail;
+import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
+
+@Disabled
+@Slf4j
+@TestMethodOrder(OrderAnnotation.class)
+public class GetMethodHelpTest extends MethodTest {
+
+ @BeforeAll
+ public static void setUp() {
+ try {
+ setUpScaffold(alicedaemon);
+ } catch (Exception ex) {
+ fail(ex);
+ }
+ }
+
+ @Test
+ @Order(1)
+ public void testGetCreateOfferHelp() {
+ var help = grpcStubs(alicedaemon).helpService
+ .getMethodHelp(GetMethodHelpRequest.newBuilder()
+ .setMethodName(createoffer.name()).build())
+ .getMethodHelp();
+ assertNotNull(help);
+ }
+
+ @AfterAll
+ public static void tearDown() {
+ tearDownScaffold();
+ }
+}
diff --git a/apitest/src/test/java/bisq/apitest/method/MethodTest.java b/apitest/src/test/java/bisq/apitest/method/MethodTest.java
index f4fd94b517a..26fa8c422cb 100644
--- a/apitest/src/test/java/bisq/apitest/method/MethodTest.java
+++ b/apitest/src/test/java/bisq/apitest/method/MethodTest.java
@@ -35,6 +35,7 @@
import bisq.proto.grpc.GetAddressBalanceRequest;
import bisq.proto.grpc.GetBalancesRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
+import bisq.proto.grpc.GetMethodHelpRequest;
import bisq.proto.grpc.GetMyOfferRequest;
import bisq.proto.grpc.GetOfferRequest;
import bisq.proto.grpc.GetPaymentAccountFormRequest;
@@ -271,6 +272,10 @@ protected final WithdrawFundsRequest createWithdrawFundsRequest(String tradeId,
.build();
}
+ protected final GetMethodHelpRequest createGetMethodHelpRequest(String methodName) {
+ return GetMethodHelpRequest.newBuilder().setMethodName(methodName).build();
+ }
+
// Convenience methods for calling frequently used & thoroughly tested gRPC services.
protected final BalancesInfo getBalances(BisqAppConfig bisqAppConfig, String currencyCode) {
return grpcStubs(bisqAppConfig).walletsService.getBalances(
@@ -490,6 +495,11 @@ public bisq.core.payment.PaymentAccount createDummyF2FAccount(BisqAppConfig bisq
return f2FAccount;
}
+ protected final String getMethodHelp(BisqAppConfig bisqAppConfig, String methodName) {
+ var req = createGetMethodHelpRequest(methodName);
+ return grpcStubs(bisqAppConfig).helpService.getMethodHelp(req).getMethodHelp();
+ }
+
// Static conveniences for test methods and test case fixture setups.
protected static RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {
diff --git a/apitest/src/test/java/bisq/apitest/scenario/StartupTest.java b/apitest/src/test/java/bisq/apitest/scenario/StartupTest.java
index 5c017e8e4f8..3010a6568da 100644
--- a/apitest/src/test/java/bisq/apitest/scenario/StartupTest.java
+++ b/apitest/src/test/java/bisq/apitest/scenario/StartupTest.java
@@ -38,6 +38,7 @@
import bisq.apitest.method.CallRateMeteringInterceptorTest;
+import bisq.apitest.method.GetMethodHelpTest;
import bisq.apitest.method.GetVersionTest;
import bisq.apitest.method.MethodTest;
import bisq.apitest.method.RegisterDisputeAgentsTest;
@@ -92,6 +93,13 @@ public void testRegisterDisputeAgents() {
test.testRegisterRefundAgent();
}
+ @Test
+ @Order(4)
+ public void testGetCreateOfferHelp() {
+ GetMethodHelpTest test = new GetMethodHelpTest();
+ test.testGetCreateOfferHelp();
+ }
+
@AfterAll
public static void tearDown() {
tearDownScaffold();
diff --git a/cli/src/main/java/bisq/cli/GrpcStubs.java b/cli/src/main/java/bisq/cli/GrpcStubs.java
index 2db33fcbaa9..2094eb743c4 100644
--- a/cli/src/main/java/bisq/cli/GrpcStubs.java
+++ b/cli/src/main/java/bisq/cli/GrpcStubs.java
@@ -19,6 +19,7 @@
import bisq.proto.grpc.DisputeAgentsGrpc;
import bisq.proto.grpc.GetVersionGrpc;
+import bisq.proto.grpc.HelpGrpc;
import bisq.proto.grpc.OffersGrpc;
import bisq.proto.grpc.PaymentAccountsGrpc;
import bisq.proto.grpc.PriceGrpc;
@@ -33,6 +34,7 @@
public class GrpcStubs {
public final DisputeAgentsGrpc.DisputeAgentsBlockingStub disputeAgentsService;
+ public final HelpGrpc.HelpBlockingStub helpService;
public final GetVersionGrpc.GetVersionBlockingStub versionService;
public final OffersGrpc.OffersBlockingStub offersService;
public final PaymentAccountsGrpc.PaymentAccountsBlockingStub paymentAccountsService;
@@ -53,6 +55,7 @@ public GrpcStubs(String apiHost, int apiPort, String apiPassword) {
}));
this.disputeAgentsService = DisputeAgentsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
+ this.helpService = HelpGrpc.newBlockingStub(channel).withCallCredentials(credentials);
this.versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
this.offersService = OffersGrpc.newBlockingStub(channel).withCallCredentials(credentials);
this.paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
From 37ad73d4f423f749b0fd03eea277e9422f84f083 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 10:23:38 -0300
Subject: [PATCH 03/15] Add posix-style api method option parsers
---
.../cli/opts/AbstractMethodOptionParser.java | 59 ++++++++
.../main/java/bisq/cli/opts/ArgumentList.java | 123 +++++++++++++++
.../cli/opts/CancelOfferOptionParser.java | 52 +++++++
.../cli/opts/CreateOfferOptionParser.java | 140 ++++++++++++++++++
.../opts/CreatePaymentAcctOptionParser.java | 63 ++++++++
.../opts/GetAddressBalanceOptionParser.java | 52 +++++++
.../bisq/cli/opts/GetBalanceOptionParser.java | 43 ++++++
.../bisq/cli/opts/GetOfferOptionParser.java | 52 +++++++
.../bisq/cli/opts/GetOffersOptionParser.java | 64 ++++++++
.../opts/GetPaymentAcctFormOptionParser.java | 53 +++++++
.../bisq/cli/opts/GetTradeOptionParser.java | 62 ++++++++
.../cli/opts/GetTransactionOptionParser.java | 52 +++++++
.../main/java/bisq/cli/opts/MethodOpts.java | 26 ++++
cli/src/main/java/bisq/cli/opts/OptLabel.java | 51 +++++++
.../RegisterDisputeAgentOptionParser.java | 64 ++++++++
.../RemoveWalletPasswordOptionParser.java | 52 +++++++
.../bisq/cli/opts/SendBsqOptionParser.java | 73 +++++++++
.../bisq/cli/opts/SendBtcOptionParser.java | 82 ++++++++++
.../cli/opts/SetTxFeeRateOptionParser.java | 53 +++++++
.../opts/SetWalletPasswordOptionParser.java | 61 ++++++++
.../cli/opts/SimpleMethodOptionParser.java | 30 ++++
.../bisq/cli/opts/TakeOfferOptionParser.java | 73 +++++++++
.../cli/opts/UnlockWalletOptionParser.java | 65 ++++++++
.../cli/opts/WithdrawFundsOptionParser.java | 70 +++++++++
24 files changed, 1515 insertions(+)
create mode 100644 cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/ArgumentList.java
create mode 100644 cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/CreatePaymentAcctOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/GetAddressBalanceOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/GetOffersOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/GetPaymentAcctFormOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/GetTransactionOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/MethodOpts.java
create mode 100644 cli/src/main/java/bisq/cli/opts/OptLabel.java
create mode 100644 cli/src/main/java/bisq/cli/opts/RegisterDisputeAgentOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/RemoveWalletPasswordOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/SendBtcOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/SetTxFeeRateOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/SetWalletPasswordOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/SimpleMethodOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/UnlockWalletOptionParser.java
create mode 100644 cli/src/main/java/bisq/cli/opts/WithdrawFundsOptionParser.java
diff --git a/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java b/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java
new file mode 100644
index 00000000000..4c495c06e5e
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java
@@ -0,0 +1,59 @@
+/*
+ * 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.opts;
+
+import joptsimple.OptionParser;
+import joptsimple.OptionSet;
+import joptsimple.OptionSpec;
+
+import java.util.List;
+
+import lombok.Getter;
+
+import static bisq.cli.opts.OptLabel.OPT_HELP;
+
+abstract class AbstractMethodOptionParser implements MethodOpts {
+
+ // The full command line args passed to CliMain.main(String[] args).
+ // CLI and Method level arguments are derived from args by an ArgumentList(args).
+ protected final String[] args;
+
+ protected final OptionParser parser = new OptionParser();
+
+ // The help option for a specific api method, e.g., takeoffer -help.
+ protected final OptionSpec helpOpt = parser.accepts(OPT_HELP, "Print method help").forHelp();
+
+ @Getter
+ protected OptionSet options;
+ @Getter
+ protected List nonOptionArguments;
+
+ protected AbstractMethodOptionParser(String[] args) {
+ this.args = args;
+ }
+
+ public AbstractMethodOptionParser parse() {
+ options = parser.parse(new ArgumentList(args).getMethodArguments());
+ nonOptionArguments = (List) options.nonOptionArguments();
+ return this;
+ }
+
+ public boolean isForHelp() {
+ return options.has(helpOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/ArgumentList.java b/cli/src/main/java/bisq/cli/opts/ArgumentList.java
new file mode 100644
index 00000000000..3b52fb34a90
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/ArgumentList.java
@@ -0,0 +1,123 @@
+/*
+ * 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.opts;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Optional;
+import java.util.function.Predicate;
+
+/**
+ * Wrapper for an array of command line arguments.
+ *
+ * Used to extract CLI connection and authentication arguments, or method arguments
+ * before parsing CLI or method opts
+ *
+ */
+public class ArgumentList {
+
+ private final Predicate isCliOpt = (o) ->
+ o.startsWith("--password") || o.startsWith("-password")
+ || o.startsWith("--port") || o.startsWith("-port")
+ || o.startsWith("--host") || o.startsWith("-host");
+
+
+ // The method name is the only positional opt in a command (easy to identify).
+ // If the positional argument does not match a Method, or there are more than one
+ // positional arguments, the joptsimple parser or CLI will fail as expected.
+ private final Predicate isMethodNameOpt = (o) -> !o.startsWith("-");
+
+ private final Predicate isHelpOpt = (o) -> o.startsWith("--help") || o.startsWith("-help");
+
+ private final String[] arguments;
+ private int currentIndex;
+
+ public ArgumentList(String... arguments) {
+ this.arguments = arguments.clone();
+ }
+
+ /**
+ * Returns only the CLI connection & authentication, and method name args
+ * (--password, --host, --port, --help, method name) contained in the original
+ * String[] args; excludes the method specific arguments.
+ *
+ * If String[] args contains both a method name (the only positional opt) and a help
+ * argument (--help, -help), it is assumed the user wants method help, not CLI help,
+ * and the help argument is not included in the returned String[].
+ */
+ public String[] getCLIArguments() {
+ currentIndex = 0;
+ Optional methodNameArgument = Optional.empty();
+ Optional helpArgument = Optional.empty();
+ List prunedArguments = new ArrayList<>();
+
+ while (hasMore()) {
+ String arg = peek();
+ if (isMethodNameOpt.test(arg)) {
+ methodNameArgument = Optional.of(arg);
+ prunedArguments.add(arg);
+ }
+
+ if (isCliOpt.test(arg))
+ prunedArguments.add(arg);
+
+ if (isHelpOpt.test(arg))
+ helpArgument = Optional.of(arg);
+
+ next();
+ }
+
+ // Include the saved CLI help argument if the positional method name argument
+ // was not found.
+ if (!methodNameArgument.isPresent() && helpArgument.isPresent())
+ prunedArguments.add(helpArgument.get());
+
+ return prunedArguments.toArray(new String[0]);
+ }
+
+ /**
+ * Returns only the method args contained in the original String[] args; excludes the
+ * CLI connection & authentication opts (--password, --host, --port), plus the
+ * positional method name arg.
+ */
+ public String[] getMethodArguments() {
+ List prunedArguments = new ArrayList<>();
+ currentIndex = 0;
+ while (hasMore()) {
+ String arg = peek();
+ if (!isCliOpt.test(arg) && !isMethodNameOpt.test(arg)) {
+ prunedArguments.add(arg);
+ }
+ next();
+ }
+ return prunedArguments.toArray(new String[0]);
+ }
+
+
+ boolean hasMore() {
+ return currentIndex < arguments.length;
+ }
+
+ String next() {
+ return arguments[currentIndex++];
+ }
+
+ String peek() {
+ return arguments[currentIndex];
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java
new file mode 100644
index 00000000000..24ebc744211
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class CancelOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to cancel")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public CancelOfferOptionParser(String[] args) {
+ super(args);
+ }
+
+ public CancelOfferOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(offerIdOpt))
+ throw new IllegalArgumentException("no offer id specified");
+
+ return this;
+ }
+
+ public String getOfferId() {
+ return options.valueOf(offerIdOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
new file mode 100644
index 00000000000..4cb6a24a4cc
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
@@ -0,0 +1,140 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import java.math.BigDecimal;
+
+import static bisq.cli.opts.OptLabel.*;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class CreateOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT,
+ "id of payment account used for offer")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (eur|usd|...)")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to buy or sell")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec minAmountOpt = parser.accepts(OPT_MIN_AMOUNT, "minimum amount of btc to buy or sell")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec securityDepositOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec makerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "maker fee currency code (bsq|btc)")
+ .withOptionalArg()
+ .defaultsTo("btc");
+
+ public CreateOfferOptionParser(String[] args) {
+ super(args);
+ }
+
+ public CreateOfferOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(paymentAccountIdOpt))
+ throw new IllegalArgumentException("no payment account id specified");
+
+ if (!options.has(directionOpt))
+ throw new IllegalArgumentException("no direction (buy|sell) specified");
+
+ if (!options.has(amountOpt))
+ throw new IllegalArgumentException("no btc amount specified");
+
+ if (!options.has(mktPriceMarginOpt) && !options.has(fixedPriceOpt))
+ throw new IllegalArgumentException("no market price margin or fixed price specified");
+
+ if (!options.has(securityDepositOpt))
+ throw new IllegalArgumentException("no security deposit specified");
+
+ return this;
+ }
+
+ public String getPaymentAccountId() {
+ return options.valueOf(paymentAccountIdOpt);
+ }
+
+ public String getDirection() {
+ return options.valueOf(directionOpt);
+ }
+
+ public String getCurrencyCode() {
+ return options.valueOf(currencyCodeOpt);
+ }
+
+ public String getAmount() {
+ return options.valueOf(amountOpt);
+ }
+
+ public String getMinAmount() {
+ return options.has(minAmountOpt) ? options.valueOf(minAmountOpt) : getAmount();
+ }
+
+ public boolean isUsingMktPriceMargin() {
+ return options.has(mktPriceMarginOpt);
+ }
+
+ @SuppressWarnings("unused")
+ public String getMktPriceMargin() {
+ return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "";
+ }
+
+ public BigDecimal getMktPriceMarginAsBigDecimal() {
+ return isUsingMktPriceMargin() ? new BigDecimal(options.valueOf(mktPriceMarginOpt)) : BigDecimal.ZERO;
+ }
+
+ public String getFixedPrice() {
+ return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "";
+ }
+
+ public String getSecurityDeposit() {
+ return options.valueOf(securityDepositOpt);
+ }
+
+ public String getMakerFeeCurrencyCode() {
+ return options.has(makerFeeCurrencyCodeOpt) ? options.valueOf(makerFeeCurrencyCodeOpt) : "btc";
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/CreatePaymentAcctOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreatePaymentAcctOptionParser.java
new file mode 100644
index 00000000000..21fb01bcec5
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/CreatePaymentAcctOptionParser.java
@@ -0,0 +1,63 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_FORM;
+import static java.lang.String.format;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class CreatePaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec paymentAcctFormPathOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_FORM,
+ "path to json payment account form")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public CreatePaymentAcctOptionParser(String[] args) {
+ super(args);
+ }
+
+ public CreatePaymentAcctOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(paymentAcctFormPathOpt))
+ throw new IllegalArgumentException("no path to json payment account form specified");
+
+ Path path = Paths.get(options.valueOf(paymentAcctFormPathOpt));
+ if (!path.toFile().exists())
+ throw new IllegalStateException(
+ format("json payment account form '%s' could not be found",
+ path.toString()));
+
+ return this;
+ }
+
+ public Path getPaymentAcctForm() {
+ return Paths.get(options.valueOf(paymentAcctFormPathOpt));
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/GetAddressBalanceOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetAddressBalanceOptionParser.java
new file mode 100644
index 00000000000..80537ffc89d
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/GetAddressBalanceOptionParser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class GetAddressBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "wallet btc address")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public GetAddressBalanceOptionParser(String[] args) {
+ super(args);
+ }
+
+ public GetAddressBalanceOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(addressOpt))
+ throw new IllegalArgumentException("no address specified");
+
+ return this;
+ }
+
+ public String getAddress() {
+ return options.valueOf(addressOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java
new file mode 100644
index 00000000000..206e590c3d2
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/GetBalanceOptionParser.java
@@ -0,0 +1,43 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class GetBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "wallet currency code (bsq|btc)")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ public GetBalanceOptionParser(String[] args) {
+ super(args);
+ }
+
+ public GetBalanceOptionParser parse() {
+ return (GetBalanceOptionParser) super.parse();
+ }
+
+ public String getCurrencyCode() {
+ return options.has(currencyCodeOpt) ? options.valueOf(currencyCodeOpt) : "";
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java
new file mode 100644
index 00000000000..600e7672c45
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class GetOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to get")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public GetOfferOptionParser(String[] args) {
+ super(args);
+ }
+
+ public GetOfferOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(offerIdOpt))
+ throw new IllegalArgumentException("no offer id specified");
+
+ return this;
+ }
+
+ public String getOfferId() {
+ return options.valueOf(offerIdOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/GetOffersOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetOffersOptionParser.java
new file mode 100644
index 00000000000..29360886f87
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/GetOffersOptionParser.java
@@ -0,0 +1,64 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
+import static bisq.cli.opts.OptLabel.OPT_DIRECTION;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class GetOffersOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (eur|usd|...)")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public GetOffersOptionParser(String[] args) {
+ super(args);
+ }
+
+ public GetOffersOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(directionOpt))
+ throw new IllegalArgumentException("no direction (buy|sell) specified");
+
+ if (!options.has(currencyCodeOpt))
+ throw new IllegalArgumentException("no currency code specified");
+
+ return this;
+ }
+
+ public String getDirection() {
+ return options.valueOf(directionOpt);
+ }
+
+ public String getCurrencyCode() {
+ return options.valueOf(currencyCodeOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/GetPaymentAcctFormOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetPaymentAcctFormOptionParser.java
new file mode 100644
index 00000000000..ef5bd5b454c
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/GetPaymentAcctFormOptionParser.java
@@ -0,0 +1,53 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_PAYMENT_METHOD_ID;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class GetPaymentAcctFormOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec paymentMethodIdOpt = parser.accepts(OPT_PAYMENT_METHOD_ID,
+ "id of payment method type used by a payment account")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public GetPaymentAcctFormOptionParser(String[] args) {
+ super(args);
+ }
+
+ public GetPaymentAcctFormOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(paymentMethodIdOpt))
+ throw new IllegalArgumentException("no payment method id specified");
+
+ return this;
+ }
+
+ public String getPaymentMethodId() {
+ return options.valueOf(paymentMethodIdOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java
new file mode 100644
index 00000000000..d1edf181063
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java
@@ -0,0 +1,62 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_SHOW_CONTRACT;
+import static bisq.cli.opts.OptLabel.OPT_TRADE_ID;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class GetTradeOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade to get")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec showContractOpt = parser.accepts(OPT_SHOW_CONTRACT, "show trade's json contract")
+ .withOptionalArg()
+ .ofType(boolean.class)
+ .defaultsTo(Boolean.FALSE);
+
+ public GetTradeOptionParser(String[] args) {
+ super(args);
+ }
+
+ public GetTradeOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(tradeIdOpt))
+ throw new IllegalArgumentException("no trade id specified");
+
+ return this;
+ }
+
+ public String getTradeId() {
+ return options.valueOf(tradeIdOpt);
+ }
+
+ public boolean getShowContract() {
+ return options.has(showContractOpt) ? options.valueOf(showContractOpt) : false;
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/GetTransactionOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetTransactionOptionParser.java
new file mode 100644
index 00000000000..d4266eb9ff7
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/GetTransactionOptionParser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_TRANSACTION_ID;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class GetTransactionOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec txIdOpt = parser.accepts(OPT_TRANSACTION_ID, "id of transaction")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public GetTransactionOptionParser(String[] args) {
+ super(args);
+ }
+
+ public GetTransactionOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(txIdOpt))
+ throw new IllegalArgumentException("no tx id specified");
+
+ return this;
+ }
+
+ public String getTxId() {
+ return options.valueOf(txIdOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/MethodOpts.java b/cli/src/main/java/bisq/cli/opts/MethodOpts.java
new file mode 100644
index 00000000000..da639857522
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/MethodOpts.java
@@ -0,0 +1,26 @@
+/*
+ * 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.opts;
+
+public interface MethodOpts {
+
+ MethodOpts parse();
+
+ boolean isForHelp();
+
+}
diff --git a/cli/src/main/java/bisq/cli/opts/OptLabel.java b/cli/src/main/java/bisq/cli/opts/OptLabel.java
new file mode 100644
index 00000000000..5cbd068ce53
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/OptLabel.java
@@ -0,0 +1,51 @@
+/*
+ * 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.opts;
+
+/**
+ * CLI opt label definitions.
+ */
+public class OptLabel {
+ public final static String OPT_ADDRESS = "address";
+ public final static String OPT_AMOUNT = "amount";
+ public final static String OPT_CURRENCY_CODE = "currency-code";
+ public final static String OPT_DIRECTION = "direction";
+ public final static String OPT_DISPUTE_AGENT_TYPE = "dispute-agent-type";
+ public final static String OPT_FEE_CURRENCY = "fee-currency";
+ public final static String OPT_FIXED_PRICE = "fixed-price";
+ public final static String OPT_HELP = "help";
+ public final static String OPT_HOST = "host";
+ public final static String OPT_MEMO = "memo";
+ public final static String OPT_MKT_PRICE_MARGIN = "market-price-margin";
+ public final static String OPT_MIN_AMOUNT = "min-amount";
+ public final static String OPT_OFFER_ID = "offer-id";
+ public final static String OPT_PASSWORD = "password";
+ public final static String OPT_PAYMENT_ACCOUNT = "payment-account";
+ public final static String OPT_PAYMENT_ACCOUNT_FORM = "payment-account-form";
+ public final static String OPT_PAYMENT_METHOD_ID = "payment-method-id";
+ public final static String OPT_PORT = "port";
+ public final static String OPT_REGISTRATION_KEY = "registration-key";
+ public final static String OPT_SECURITY_DEPOSIT = "security-deposit";
+ public final static String OPT_SHOW_CONTRACT = "show-contract";
+ public final static String OPT_TRADE_ID = "trade-id";
+ public final static String OPT_TIMEOUT = "timeout";
+ public final static String OPT_TRANSACTION_ID = "transaction-id";
+ public final static String OPT_TX_FEE_RATE = "tx-fee-rate";
+ public final static String OPT_WALLET_PASSWORD = "wallet-password";
+ public final static String OPT_NEW_WALLET_PASSWORD = "new-wallet-password";
+}
diff --git a/cli/src/main/java/bisq/cli/opts/RegisterDisputeAgentOptionParser.java b/cli/src/main/java/bisq/cli/opts/RegisterDisputeAgentOptionParser.java
new file mode 100644
index 00000000000..428555a3493
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/RegisterDisputeAgentOptionParser.java
@@ -0,0 +1,64 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_DISPUTE_AGENT_TYPE;
+import static bisq.cli.opts.OptLabel.OPT_REGISTRATION_KEY;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class RegisterDisputeAgentOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec disputeAgentTypeOpt = parser.accepts(OPT_DISPUTE_AGENT_TYPE, "dispute agent type")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec registrationKeyOpt = parser.accepts(OPT_REGISTRATION_KEY, "registration key")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public RegisterDisputeAgentOptionParser(String[] args) {
+ super(args);
+ }
+
+ public RegisterDisputeAgentOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(disputeAgentTypeOpt))
+ throw new IllegalArgumentException("no dispute agent type specified");
+
+ if (!options.has(registrationKeyOpt))
+ throw new IllegalArgumentException("no registration key specified");
+
+ return this;
+ }
+
+ public String getDisputeAgentType() {
+ return options.valueOf(disputeAgentTypeOpt);
+ }
+
+ public String getRegistrationKey() {
+ return options.valueOf(registrationKeyOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/RemoveWalletPasswordOptionParser.java b/cli/src/main/java/bisq/cli/opts/RemoveWalletPasswordOptionParser.java
new file mode 100644
index 00000000000..5b9a3915941
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/RemoveWalletPasswordOptionParser.java
@@ -0,0 +1,52 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class RemoveWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public RemoveWalletPasswordOptionParser(String[] args) {
+ super(args);
+ }
+
+ public RemoveWalletPasswordOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(passwordOpt))
+ throw new IllegalArgumentException("no password specified");
+
+ return this;
+ }
+
+ public String getPassword() {
+ return options.valueOf(passwordOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java b/cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java
new file mode 100644
index 00000000000..3bffce785c4
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/SendBsqOptionParser.java
@@ -0,0 +1,73 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
+import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
+import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class SendBsqOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "destination bsq address")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of bsq to send")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ public SendBsqOptionParser(String[] args) {
+ super(args);
+ }
+
+ public SendBsqOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(addressOpt))
+ throw new IllegalArgumentException("no bsq address specified");
+
+ if (!options.has(amountOpt))
+ throw new IllegalArgumentException("no bsq amount specified");
+
+ return this;
+ }
+
+ public String getAddress() {
+ return options.valueOf(addressOpt);
+ }
+
+ public String getAmount() {
+ return options.valueOf(amountOpt);
+ }
+
+ public String getFeeRate() {
+ return options.has(feeRateOpt) ? options.valueOf(feeRateOpt) : "";
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/SendBtcOptionParser.java b/cli/src/main/java/bisq/cli/opts/SendBtcOptionParser.java
new file mode 100644
index 00000000000..8c3f9762019
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/SendBtcOptionParser.java
@@ -0,0 +1,82 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
+import static bisq.cli.opts.OptLabel.OPT_AMOUNT;
+import static bisq.cli.opts.OptLabel.OPT_MEMO;
+import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class SendBtcOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to send")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, "optional tx fee rate (sats/byte)")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec memoOpt = parser.accepts(OPT_MEMO, "optional tx memo")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ public SendBtcOptionParser(String[] args) {
+ super(args);
+ }
+
+ public SendBtcOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(addressOpt))
+ throw new IllegalArgumentException("no btc address specified");
+
+ if (!options.has(amountOpt))
+ throw new IllegalArgumentException("no btc amount specified");
+
+ return this;
+ }
+
+ public String getAddress() {
+ return options.valueOf(addressOpt);
+ }
+
+ public String getAmount() {
+ return options.valueOf(amountOpt);
+ }
+
+ public String getFeeRate() {
+ return options.has(feeRateOpt) ? options.valueOf(feeRateOpt) : "";
+ }
+
+ public String getMemo() {
+ return options.has(memoOpt) ? options.valueOf(memoOpt) : "";
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/SetTxFeeRateOptionParser.java b/cli/src/main/java/bisq/cli/opts/SetTxFeeRateOptionParser.java
new file mode 100644
index 00000000000..9d4b5e71b3e
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/SetTxFeeRateOptionParser.java
@@ -0,0 +1,53 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_TX_FEE_RATE;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class SetTxFeeRateOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec feeRateOpt = parser.accepts(OPT_TX_FEE_RATE,
+ "tx fee rate preference (sats/byte)")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ public SetTxFeeRateOptionParser(String[] args) {
+ super(args);
+ }
+
+ public SetTxFeeRateOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(feeRateOpt))
+ throw new IllegalArgumentException("no tx fee rate specified");
+
+ return this;
+ }
+
+ public String getFeeRate() {
+ return options.valueOf(feeRateOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/SetWalletPasswordOptionParser.java b/cli/src/main/java/bisq/cli/opts/SetWalletPasswordOptionParser.java
new file mode 100644
index 00000000000..d55b1bf33b4
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/SetWalletPasswordOptionParser.java
@@ -0,0 +1,61 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_NEW_WALLET_PASSWORD;
+import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class SetWalletPasswordOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec newPasswordOpt = parser.accepts(OPT_NEW_WALLET_PASSWORD, "new bisq wallet password")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ public SetWalletPasswordOptionParser(String[] args) {
+ super(args);
+ }
+
+ public SetWalletPasswordOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(passwordOpt))
+ throw new IllegalArgumentException("no password specified");
+
+ return this;
+ }
+
+ public String getPassword() {
+ return options.valueOf(passwordOpt);
+ }
+
+ public String getNewPassword() {
+ return options.has(newPasswordOpt) ? options.valueOf(newPasswordOpt) : "";
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/SimpleMethodOptionParser.java b/cli/src/main/java/bisq/cli/opts/SimpleMethodOptionParser.java
new file mode 100644
index 00000000000..a0e396d63af
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/SimpleMethodOptionParser.java
@@ -0,0 +1,30 @@
+/*
+ * 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.opts;
+
+
+public class SimpleMethodOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ public SimpleMethodOptionParser(String[] args) {
+ super(args);
+ }
+
+ public SimpleMethodOptionParser parse() {
+ return (SimpleMethodOptionParser) super.parse();
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java
new file mode 100644
index 00000000000..75ef2885b04
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java
@@ -0,0 +1,73 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_FEE_CURRENCY;
+import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
+import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class TakeOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to take")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT, "id of payment account used for trade")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec takerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "taker fee currency code (bsq|btc)")
+ .withOptionalArg()
+ .defaultsTo("btc");
+
+ public TakeOfferOptionParser(String[] args) {
+ super(args);
+ }
+
+ public TakeOfferOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(offerIdOpt))
+ throw new IllegalArgumentException("no offer id specified");
+
+ if (!options.has(paymentAccountIdOpt))
+ throw new IllegalArgumentException("no payment account id specified");
+
+ return this;
+ }
+
+ public String getOfferId() {
+ return options.valueOf(offerIdOpt);
+ }
+
+ public String getPaymentAccountId() {
+ return options.valueOf(paymentAccountIdOpt);
+ }
+
+ public String getTakerFeeCurrencyCode() {
+ return options.has(takerFeeCurrencyCodeOpt) ? options.valueOf(takerFeeCurrencyCodeOpt) : "btc";
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/UnlockWalletOptionParser.java b/cli/src/main/java/bisq/cli/opts/UnlockWalletOptionParser.java
new file mode 100644
index 00000000000..4446138dd37
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/UnlockWalletOptionParser.java
@@ -0,0 +1,65 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_TIMEOUT;
+import static bisq.cli.opts.OptLabel.OPT_WALLET_PASSWORD;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class UnlockWalletOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec passwordOpt = parser.accepts(OPT_WALLET_PASSWORD, "bisq wallet password")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec unlockTimeoutOpt = parser.accepts(OPT_TIMEOUT, "wallet unlock timeout (s)")
+ .withRequiredArg()
+ .ofType(long.class)
+ .defaultsTo(0L);
+
+ public UnlockWalletOptionParser(String[] args) {
+ super(args);
+ }
+
+ public UnlockWalletOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(passwordOpt))
+ throw new IllegalArgumentException("no password specified");
+
+ if (!options.has(unlockTimeoutOpt) || options.valueOf(unlockTimeoutOpt) <= 0)
+ throw new IllegalArgumentException("no unlock timeout specified");
+
+ return this;
+ }
+
+ public String getPassword() {
+ return options.valueOf(passwordOpt);
+ }
+
+ public long getUnlockTimeout() {
+ return options.valueOf(unlockTimeoutOpt);
+ }
+}
diff --git a/cli/src/main/java/bisq/cli/opts/WithdrawFundsOptionParser.java b/cli/src/main/java/bisq/cli/opts/WithdrawFundsOptionParser.java
new file mode 100644
index 00000000000..382fe2c883a
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/opts/WithdrawFundsOptionParser.java
@@ -0,0 +1,70 @@
+/*
+ * 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.opts;
+
+
+import joptsimple.OptionSpec;
+
+import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
+import static bisq.cli.opts.OptLabel.OPT_MEMO;
+import static bisq.cli.opts.OptLabel.OPT_TRADE_ID;
+import static joptsimple.internal.Strings.EMPTY;
+
+public class WithdrawFundsOptionParser extends AbstractMethodOptionParser implements MethodOpts {
+
+ final OptionSpec tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade to get")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec addressOpt = parser.accepts(OPT_ADDRESS, "destination btc address")
+ .withRequiredArg()
+ .defaultsTo(EMPTY);
+
+ final OptionSpec memoOpt = parser.accepts(OPT_MEMO, "optional tx memo")
+ .withOptionalArg()
+ .defaultsTo(EMPTY);
+
+ public WithdrawFundsOptionParser(String[] args) {
+ super(args);
+ }
+
+ public WithdrawFundsOptionParser parse() {
+ super.parse();
+
+ // Short circuit opt validation if user just wants help.
+ if (options.has(helpOpt))
+ return this;
+
+ if (!options.has(tradeIdOpt))
+ throw new IllegalArgumentException("no trade id specified");
+
+ return this;
+ }
+
+ public String getTradeId() {
+ return options.valueOf(tradeIdOpt);
+ }
+
+ public String getAddress() {
+ return options.valueOf(addressOpt);
+ }
+
+ public String getMemo() {
+ return options.has(memoOpt) ? options.valueOf(memoOpt) : "";
+ }
+}
From f4e735faec3233c0a0e892382bb6f4f4ab42239e Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 10:25:08 -0300
Subject: [PATCH 04/15] Move CLI method enum to it's own class
This helps reduce size of growing CLI class file.
---
cli/src/main/java/bisq/cli/Method.java | 56 ++++++++++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 cli/src/main/java/bisq/cli/Method.java
diff --git a/cli/src/main/java/bisq/cli/Method.java b/cli/src/main/java/bisq/cli/Method.java
new file mode 100644
index 00000000000..037468b9bd0
--- /dev/null
+++ b/cli/src/main/java/bisq/cli/Method.java
@@ -0,0 +1,56 @@
+/*
+ * 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;
+
+/**
+ * Currently supported api methods.
+ */
+public enum Method {
+ createoffer,
+ canceloffer,
+ getoffer,
+ getmyoffer,
+ getoffers,
+ getmyoffers,
+ takeoffer,
+ gettrade,
+ confirmpaymentstarted,
+ confirmpaymentreceived,
+ keepfunds,
+ withdrawfunds,
+ getpaymentmethods,
+ getpaymentacctform,
+ createpaymentacct,
+ getpaymentaccts,
+ getversion,
+ getbalance,
+ getaddressbalance,
+ getfundingaddresses,
+ getunusedbsqaddress,
+ sendbsq,
+ sendbtc,
+ gettxfeerate,
+ settxfeerate,
+ unsettxfeerate,
+ gettransaction,
+ lockwallet,
+ unlockwallet,
+ removewalletpassword,
+ setwalletpassword,
+ registerdisputeagent
+}
From 9f0f083cf7f29d1820c6b7326d6e25ae0e3ad230 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 10:26:11 -0300
Subject: [PATCH 05/15] Change CLI opts to posix-style
---
cli/src/main/java/bisq/cli/CliMain.java | 628 +++++++++++++-----------
1 file changed, 351 insertions(+), 277 deletions(-)
diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java
index 6df23f9b730..6c3e796942f 100644
--- a/cli/src/main/java/bisq/cli/CliMain.java
+++ b/cli/src/main/java/bisq/cli/CliMain.java
@@ -25,6 +25,7 @@
import bisq.proto.grpc.GetAddressBalanceRequest;
import bisq.proto.grpc.GetBalancesRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
+import bisq.proto.grpc.GetMethodHelpRequest;
import bisq.proto.grpc.GetMyOfferRequest;
import bisq.proto.grpc.GetMyOffersRequest;
import bisq.proto.grpc.GetOfferRequest;
@@ -69,8 +70,6 @@
import java.io.PrintStream;
import java.io.PrintWriter;
-import java.math.BigDecimal;
-
import java.util.Date;
import java.util.List;
@@ -79,15 +78,43 @@
import static bisq.cli.CurrencyFormat.formatTxFeeRateInfo;
import static bisq.cli.CurrencyFormat.toSatoshis;
import static bisq.cli.CurrencyFormat.toSecurityDepositAsPct;
-import static bisq.cli.NegativeNumberOptions.hasNegativeNumberOptions;
+import static bisq.cli.Method.*;
import static bisq.cli.TableFormat.*;
+import static bisq.cli.opts.OptLabel.OPT_HELP;
+import static bisq.cli.opts.OptLabel.OPT_HOST;
+import static bisq.cli.opts.OptLabel.OPT_PASSWORD;
+import static bisq.cli.opts.OptLabel.OPT_PORT;
+import static bisq.proto.grpc.HelpGrpc.HelpBlockingStub;
import static java.lang.String.format;
import static java.lang.System.err;
import static java.lang.System.exit;
import static java.lang.System.out;
-import static java.math.BigDecimal.ZERO;
import static java.util.Collections.singletonList;
+
+
+import bisq.cli.opts.ArgumentList;
+import bisq.cli.opts.CancelOfferOptionParser;
+import bisq.cli.opts.CreateOfferOptionParser;
+import bisq.cli.opts.CreatePaymentAcctOptionParser;
+import bisq.cli.opts.GetAddressBalanceOptionParser;
+import bisq.cli.opts.GetBalanceOptionParser;
+import bisq.cli.opts.GetOfferOptionParser;
+import bisq.cli.opts.GetOffersOptionParser;
+import bisq.cli.opts.GetPaymentAcctFormOptionParser;
+import bisq.cli.opts.GetTradeOptionParser;
+import bisq.cli.opts.GetTransactionOptionParser;
+import bisq.cli.opts.RegisterDisputeAgentOptionParser;
+import bisq.cli.opts.RemoveWalletPasswordOptionParser;
+import bisq.cli.opts.SendBsqOptionParser;
+import bisq.cli.opts.SendBtcOptionParser;
+import bisq.cli.opts.SetTxFeeRateOptionParser;
+import bisq.cli.opts.SetWalletPasswordOptionParser;
+import bisq.cli.opts.SimpleMethodOptionParser;
+import bisq.cli.opts.TakeOfferOptionParser;
+import bisq.cli.opts.UnlockWalletOptionParser;
+import bisq.cli.opts.WithdrawFundsOptionParser;
+
/**
* A command-line client for the Bisq gRPC API.
*/
@@ -95,41 +122,6 @@
@Slf4j
public class CliMain {
- private enum Method {
- createoffer,
- canceloffer,
- getoffer,
- getmyoffer,
- getoffers,
- getmyoffers,
- takeoffer,
- gettrade,
- confirmpaymentstarted,
- confirmpaymentreceived,
- keepfunds,
- withdrawfunds,
- getpaymentmethods,
- getpaymentacctform,
- createpaymentacct,
- getpaymentaccts,
- getversion,
- getbalance,
- getaddressbalance,
- getfundingaddresses,
- getunusedbsqaddress,
- sendbsq,
- sendbtc,
- gettxfeerate,
- settxfeerate,
- unsettxfeerate,
- gettransaction,
- lockwallet,
- unlockwallet,
- removewalletpassword,
- setwalletpassword,
- registerdisputeagent
- }
-
public static void main(String[] args) {
try {
run(args);
@@ -142,48 +134,47 @@ public static void main(String[] args) {
public static void run(String[] args) {
var parser = new OptionParser();
- var helpOpt = parser.accepts("help", "Print this help text")
+ var helpOpt = parser.accepts(OPT_HELP, "Print this help text")
.forHelp();
- var hostOpt = parser.accepts("host", "rpc server hostname or IP")
+ var hostOpt = parser.accepts(OPT_HOST, "rpc server hostname or ip")
.withRequiredArg()
.defaultsTo("localhost");
- var portOpt = parser.accepts("port", "rpc server port")
+ var portOpt = parser.accepts(OPT_PORT, "rpc server port")
.withRequiredArg()
.ofType(Integer.class)
.defaultsTo(9998);
- var passwordOpt = parser.accepts("password", "rpc server password")
+ var passwordOpt = parser.accepts(OPT_PASSWORD, "rpc server password")
.withRequiredArg();
- var negativeNumberOpts = hasNegativeNumberOptions(args)
- ? new NegativeNumberOptions()
- : null;
-
- // Cache any negative number params that will not be accepted by the parser.
- if (negativeNumberOpts != null)
- args = negativeNumberOpts.removeNegativeNumberOptions(args);
-
- // Parse the options after temporarily removing any negative number params we
- // do not want the parser recognizing as invalid option arguments, e.g., -0.05.
- OptionSet options = parser.parse(args);
-
- if (options.has(helpOpt)) {
- printHelp(parser, out);
- return;
- }
-
+ // Parse the CLI opts host, port, password, method name, and help. The help opt
+ // may indicate the user is asking for method level help, and will be excluded
+ // from the parsed options if a method opt is present in String[] args.
+ OptionSet options = parser.parse(new ArgumentList(args).getCLIArguments());
@SuppressWarnings("unchecked")
var nonOptionArgs = (List) options.nonOptionArguments();
- if (nonOptionArgs.isEmpty()) {
+
+ // If neither the help opt nor a method name is present, print CLI level help
+ // to stderr and throw an exception.
+ if (!options.has(helpOpt) && nonOptionArgs.isEmpty()) {
printHelp(parser, err);
throw new IllegalArgumentException("no method specified");
}
- // Restore any cached negative number params to the nonOptionArgs list.
- if (negativeNumberOpts != null)
- nonOptionArgs = negativeNumberOpts.restoreNegativeNumberOptions(nonOptionArgs);
+ // If the help opt is present, but not a method name, print CLI level help
+ // to stdout.
+ if (options.has(helpOpt) && nonOptionArgs.isEmpty()) {
+ printHelp(parser, out);
+ return;
+ }
+
+ var host = options.valueOf(hostOpt);
+ var port = options.valueOf(portOpt);
+ var password = options.valueOf(passwordOpt);
+ if (password == null)
+ throw new IllegalArgumentException("missing required 'password' option");
var methodName = nonOptionArgs.get(0);
Method method;
@@ -193,14 +184,9 @@ public static void run(String[] args) {
throw new IllegalArgumentException(format("'%s' is not a supported method", methodName));
}
- var host = options.valueOf(hostOpt);
- var port = options.valueOf(portOpt);
- var password = options.valueOf(passwordOpt);
- if (password == null)
- throw new IllegalArgumentException("missing required 'password' option");
-
GrpcStubs grpcStubs = new GrpcStubs(host, port, password);
var disputeAgentsService = grpcStubs.disputeAgentsService;
+ var helpService = grpcStubs.helpService;
var offersService = grpcStubs.offersService;
var paymentAccountsService = grpcStubs.paymentAccountsService;
var tradesService = grpcStubs.tradesService;
@@ -210,15 +196,22 @@ public static void run(String[] args) {
try {
switch (method) {
case getversion: {
+ if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
var request = GetVersionRequest.newBuilder().build();
var version = versionService.getVersion(request).getVersion();
out.println(version);
return;
}
case getbalance: {
- var currencyCode = nonOptionArgs.size() == 2
- ? nonOptionArgs.get(1)
- : "";
+ var opts = new GetBalanceOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var currencyCode = opts.getCurrencyCode();
var request = GetBalancesRequest.newBuilder()
.setCurrencyCode(currencyCode)
.build();
@@ -238,41 +231,50 @@ public static void run(String[] args) {
return;
}
case getaddressbalance: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("no address specified");
-
+ var opts = new GetAddressBalanceOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var address = opts.getAddress();
var request = GetAddressBalanceRequest.newBuilder()
- .setAddress(nonOptionArgs.get(1)).build();
+ .setAddress(address).build();
var reply = walletsService.getAddressBalance(request);
out.println(formatAddressBalanceTbl(singletonList(reply.getAddressBalanceInfo())));
return;
}
case getfundingaddresses: {
+ if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
var request = GetFundingAddressesRequest.newBuilder().build();
var reply = walletsService.getFundingAddresses(request);
out.println(formatAddressBalanceTbl(reply.getAddressBalanceInfoList()));
return;
}
case getunusedbsqaddress: {
+ if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
var request = GetUnusedBsqAddressRequest.newBuilder().build();
var reply = walletsService.getUnusedBsqAddress(request);
out.println(reply.getAddress());
return;
}
case sendbsq: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("no bsq address specified");
-
- var address = nonOptionArgs.get(1);
-
- if (nonOptionArgs.size() < 3)
- throw new IllegalArgumentException("no bsq amount specified");
-
- var amount = nonOptionArgs.get(2);
+ var opts = new SendBsqOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var address = opts.getAddress();
+ var amount = opts.getAmount();
verifyStringIsValidDecimal(amount);
- var txFeeRate = nonOptionArgs.size() == 4 ? nonOptionArgs.get(3) : "";
- if (!txFeeRate.isEmpty())
+ var txFeeRate = opts.getFeeRate();
+ if (txFeeRate.isEmpty())
verifyStringIsValidLong(txFeeRate);
var request = SendBsqRequest.newBuilder()
@@ -289,24 +291,20 @@ public static void run(String[] args) {
return;
}
case sendbtc: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("no btc address specified");
-
- var address = nonOptionArgs.get(1);
-
- if (nonOptionArgs.size() < 3)
- throw new IllegalArgumentException("no btc amount specified");
-
- var amount = nonOptionArgs.get(2);
+ var opts = new SendBtcOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var address = opts.getAddress();
+ var amount = opts.getAmount();
verifyStringIsValidDecimal(amount);
- // TODO Find a better way to handle the two optional parameters.
- var txFeeRate = nonOptionArgs.size() >= 4 ? nonOptionArgs.get(3) : "";
- if (!txFeeRate.isEmpty())
+ var txFeeRate = opts.getFeeRate();
+ if (txFeeRate.isEmpty())
verifyStringIsValidLong(txFeeRate);
- var memo = nonOptionArgs.size() == 5 ? nonOptionArgs.get(4) : "";
-
+ var memo = opts.getMemo();
var request = SendBtcRequest.newBuilder()
.setAddress(address)
.setAmount(amount)
@@ -322,16 +320,22 @@ public static void run(String[] args) {
return;
}
case gettxfeerate: {
+ if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
var request = GetTxFeeRateRequest.newBuilder().build();
var reply = walletsService.getTxFeeRate(request);
out.println(formatTxFeeRateInfo(reply.getTxFeeRateInfo()));
return;
}
case settxfeerate: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("no tx fee rate specified");
-
- var txFeeRate = toLong(nonOptionArgs.get(2));
+ var opts = new SetTxFeeRateOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var txFeeRate = toLong(opts.getFeeRate());
var request = SetTxFeeRatePreferenceRequest.newBuilder()
.setTxFeeRatePreference(txFeeRate)
.build();
@@ -340,16 +344,22 @@ public static void run(String[] args) {
return;
}
case unsettxfeerate: {
+ if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
var request = UnsetTxFeeRatePreferenceRequest.newBuilder().build();
var reply = walletsService.unsetTxFeeRatePreference(request);
out.println(formatTxFeeRateInfo(reply.getTxFeeRateInfo()));
return;
}
case gettransaction: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("no tx id specified");
-
- var txId = nonOptionArgs.get(1);
+ var opts = new GetTransactionOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var txId = opts.getTxId();
var request = GetTransactionRequest.newBuilder()
.setTxId(txId)
.build();
@@ -358,30 +368,21 @@ public static void run(String[] args) {
return;
}
case createoffer: {
- if (nonOptionArgs.size() < 9)
- throw new IllegalArgumentException("incorrect parameter count,"
- + " expecting payment acct id, buy | sell, currency code, amount, min amount,"
- + " use-market-based-price, fixed-price | mkt-price-margin, security-deposit"
- + " [,maker-fee-currency-code = bsq|btc]");
-
- var paymentAcctId = nonOptionArgs.get(1);
- var direction = nonOptionArgs.get(2);
- var currencyCode = nonOptionArgs.get(3);
- var amount = toSatoshis(nonOptionArgs.get(4));
- var minAmount = toSatoshis(nonOptionArgs.get(5));
- var useMarketBasedPrice = Boolean.parseBoolean(nonOptionArgs.get(6));
- var fixedPrice = ZERO.toString();
- var marketPriceMargin = ZERO;
- if (useMarketBasedPrice)
- marketPriceMargin = new BigDecimal(nonOptionArgs.get(7));
- else
- fixedPrice = nonOptionArgs.get(7);
-
- var securityDeposit = toSecurityDepositAsPct(nonOptionArgs.get(8));
- var makerFeeCurrencyCode = nonOptionArgs.size() == 10
- ? nonOptionArgs.get(9)
- : "btc";
-
+ var opts = new CreateOfferOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var paymentAcctId = opts.getPaymentAccountId();
+ var direction = opts.getDirection();
+ var currencyCode = opts.getCurrencyCode();
+ var amount = toSatoshis(opts.getAmount());
+ var minAmount = toSatoshis(opts.getMinAmount());
+ var useMarketBasedPrice = opts.isUsingMktPriceMargin();
+ var fixedPrice = opts.getFixedPrice();
+ var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal();
+ var securityDeposit = toSecurityDepositAsPct(opts.getSecurityDeposit());
+ var makerFeeCurrencyCode = opts.getMakerFeeCurrencyCode();
var request = CreateOfferRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode)
@@ -399,10 +400,12 @@ public static void run(String[] args) {
return;
}
case canceloffer: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("incorrect parameter count, expecting offer id");
-
- var offerId = nonOptionArgs.get(1);
+ var opts = new CancelOfferOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var offerId = opts.getOfferId();
var request = CancelOfferRequest.newBuilder()
.setId(offerId)
.build();
@@ -411,10 +414,12 @@ public static void run(String[] args) {
return;
}
case getoffer: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("incorrect parameter count, expecting offer id");
-
- var offerId = nonOptionArgs.get(1);
+ var opts = new GetOfferOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var offerId = opts.getOfferId();
var request = GetOfferRequest.newBuilder()
.setId(offerId)
.build();
@@ -424,10 +429,12 @@ public static void run(String[] args) {
return;
}
case getmyoffer: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("incorrect parameter count, expecting offer id");
-
- var offerId = nonOptionArgs.get(1);
+ var opts = new GetOfferOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var offerId = opts.getOfferId();
var request = GetMyOfferRequest.newBuilder()
.setId(offerId)
.build();
@@ -437,13 +444,13 @@ public static void run(String[] args) {
return;
}
case getoffers: {
- if (nonOptionArgs.size() < 3)
- throw new IllegalArgumentException("incorrect parameter count,"
- + " expecting direction (buy|sell), currency code");
-
- var direction = nonOptionArgs.get(1);
- var currencyCode = nonOptionArgs.get(2);
-
+ var opts = new GetOffersOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var direction = opts.getDirection();
+ var currencyCode = opts.getCurrencyCode();
var request = GetOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode)
@@ -459,13 +466,13 @@ public static void run(String[] args) {
return;
}
case getmyoffers: {
- if (nonOptionArgs.size() < 3)
- throw new IllegalArgumentException("incorrect parameter count,"
- + " expecting direction (buy|sell), currency code");
-
- var direction = nonOptionArgs.get(1);
- var currencyCode = nonOptionArgs.get(2);
-
+ var opts = new GetOffersOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var direction = opts.getDirection();
+ var currencyCode = opts.getCurrencyCode();
var request = GetMyOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode)
@@ -481,16 +488,14 @@ public static void run(String[] args) {
return;
}
case takeoffer: {
- if (nonOptionArgs.size() < 3)
- throw new IllegalArgumentException("incorrect parameter count, " +
- " expecting offer id, payment acct id [,taker fee currency code = bsq|btc]");
-
- var offerId = nonOptionArgs.get(1);
- var paymentAccountId = nonOptionArgs.get(2);
- var takerFeeCurrencyCode = nonOptionArgs.size() == 4
- ? nonOptionArgs.get(3)
- : "btc";
-
+ var opts = new TakeOfferOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var offerId = opts.getOfferId();
+ var paymentAccountId = opts.getPaymentAccountId();
+ var takerFeeCurrencyCode = opts.getTakerFeeCurrencyCode();
var request = TakeOfferRequest.newBuilder()
.setOfferId(offerId)
.setPaymentAccountId(paymentAccountId)
@@ -502,30 +507,32 @@ public static void run(String[] args) {
}
case gettrade: {
// TODO make short-id a valid argument?
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("incorrect parameter count, "
- + " expecting trade id [,showcontract = true|false]");
-
- var tradeId = nonOptionArgs.get(1);
- var showContract = false;
- if (nonOptionArgs.size() == 3)
- showContract = Boolean.getBoolean(nonOptionArgs.get(2));
-
+ var opts = new GetTradeOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var tradeId = opts.getTradeId();
+ var showContract = opts.getShowContract();
var request = GetTradeRequest.newBuilder()
.setTradeId(tradeId)
.build();
var reply = tradesService.getTrade(request);
+
if (showContract)
out.println(reply.getTrade().getContractAsJson());
else
out.println(TradeFormat.format(reply.getTrade()));
+
return;
}
case confirmpaymentstarted: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("incorrect parameter count, expecting trade id");
-
- var tradeId = nonOptionArgs.get(1);
+ var opts = new GetTradeOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var tradeId = opts.getTradeId();
var request = ConfirmPaymentStartedRequest.newBuilder()
.setTradeId(tradeId)
.build();
@@ -534,10 +541,12 @@ public static void run(String[] args) {
return;
}
case confirmpaymentreceived: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("incorrect parameter count, expecting trade id");
-
- var tradeId = nonOptionArgs.get(1);
+ var opts = new GetTradeOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var tradeId = opts.getTradeId();
var request = ConfirmPaymentReceivedRequest.newBuilder()
.setTradeId(tradeId)
.build();
@@ -546,10 +555,12 @@ public static void run(String[] args) {
return;
}
case keepfunds: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("incorrect parameter count, expecting trade id");
-
- var tradeId = nonOptionArgs.get(1);
+ var opts = new GetTradeOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var tradeId = opts.getTradeId();
var request = KeepFundsRequest.newBuilder()
.setTradeId(tradeId)
.build();
@@ -558,16 +569,15 @@ public static void run(String[] args) {
return;
}
case withdrawfunds: {
- if (nonOptionArgs.size() < 3)
- throw new IllegalArgumentException("incorrect parameter count, "
- + " expecting trade id, bitcoin wallet address [,\"memo\"]");
-
- var tradeId = nonOptionArgs.get(1);
- var address = nonOptionArgs.get(2);
- // A multi-word memo must be double quoted.
- var memo = nonOptionArgs.size() == 4
- ? nonOptionArgs.get(3)
- : "";
+ var opts = new WithdrawFundsOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var tradeId = opts.getTradeId();
+ var address = opts.getAddress();
+ // Multi-word memos must be double quoted.
+ var memo = opts.getMemo();
var request = WithdrawFundsRequest.newBuilder()
.setTradeId(tradeId)
.setAddress(address)
@@ -578,16 +588,22 @@ public static void run(String[] args) {
return;
}
case getpaymentmethods: {
+ if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
var request = GetPaymentMethodsRequest.newBuilder().build();
var reply = paymentAccountsService.getPaymentMethods(request);
reply.getPaymentMethodsList().forEach(p -> out.println(p.getId()));
return;
}
case getpaymentacctform: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("incorrect parameter count, expecting payment method id");
-
- var paymentMethodId = nonOptionArgs.get(1);
+ var opts = new GetPaymentAcctFormOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var paymentMethodId = opts.getPaymentMethodId();
var request = GetPaymentAccountFormRequest.newBuilder()
.setPaymentMethodId(paymentMethodId)
.build();
@@ -602,22 +618,18 @@ public static void run(String[] args) {
return;
}
case createpaymentacct: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException(
- "incorrect parameter count, expecting path to payment account form");
-
- var paymentAccountFormPath = Paths.get(nonOptionArgs.get(1));
- if (!paymentAccountFormPath.toFile().exists())
- throw new IllegalStateException(
- format("payment account form '%s' could not be found",
- paymentAccountFormPath.toString()));
-
+ var opts = new CreatePaymentAcctOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var paymentAccountForm = opts.getPaymentAcctForm();
String jsonString;
try {
- jsonString = new String(Files.readAllBytes(paymentAccountFormPath));
+ jsonString = new String(Files.readAllBytes(paymentAccountForm));
} catch (IOException e) {
throw new IllegalStateException(
- format("could not read %s", paymentAccountFormPath.toString()));
+ format("could not read %s", paymentAccountForm.toString()));
}
var request = CreatePaymentAccountRequest.newBuilder()
@@ -629,6 +641,10 @@ public static void run(String[] args) {
return;
}
case getpaymentaccts: {
+ if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
var request = GetPaymentAccountsRequest.newBuilder().build();
var reply = paymentAccountsService.getPaymentAccounts(request);
@@ -641,56 +657,66 @@ public static void run(String[] args) {
return;
}
case lockwallet: {
+ if (new SimpleMethodOptionParser(args).parse().isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
var request = LockWalletRequest.newBuilder().build();
walletsService.lockWallet(request);
out.println("wallet locked");
return;
}
case unlockwallet: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("no password specified");
-
- if (nonOptionArgs.size() < 3)
- throw new IllegalArgumentException("no unlock timeout specified");
-
- var timeout = toLong(nonOptionArgs.get(2));
+ var opts = new UnlockWalletOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var walletPassword = opts.getPassword();
+ var timeout = opts.getUnlockTimeout();
var request = UnlockWalletRequest.newBuilder()
- .setPassword(nonOptionArgs.get(1))
+ .setPassword(walletPassword)
.setTimeout(timeout).build();
walletsService.unlockWallet(request);
out.println("wallet unlocked");
return;
}
case removewalletpassword: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("no password specified");
-
+ var opts = new RemoveWalletPasswordOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var walletPassword = opts.getPassword();
var request = RemoveWalletPasswordRequest.newBuilder()
- .setPassword(nonOptionArgs.get(1)).build();
+ .setPassword(walletPassword).build();
walletsService.removeWalletPassword(request);
out.println("wallet decrypted");
return;
}
case setwalletpassword: {
- if (nonOptionArgs.size() < 2)
- throw new IllegalArgumentException("no password specified");
-
+ var opts = new SetWalletPasswordOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var walletPassword = opts.getPassword();
+ var newWalletPassword = opts.getNewPassword();
var requestBuilder = SetWalletPasswordRequest.newBuilder()
- .setPassword(nonOptionArgs.get(1));
- var hasNewPassword = nonOptionArgs.size() == 3;
- if (hasNewPassword)
- requestBuilder.setNewPassword(nonOptionArgs.get(2));
+ .setPassword(walletPassword)
+ .setNewPassword(newWalletPassword);
walletsService.setWalletPassword(requestBuilder.build());
- out.println("wallet encrypted" + (hasNewPassword ? " with new password" : ""));
+ out.println("wallet encrypted" + (!newWalletPassword.isEmpty() ? " with new password" : ""));
return;
}
case registerdisputeagent: {
- if (nonOptionArgs.size() < 3)
- throw new IllegalArgumentException(
- "incorrect parameter count, expecting dispute agent type, registration key");
-
- var disputeAgentType = nonOptionArgs.get(1);
- var registrationKey = nonOptionArgs.get(2);
+ var opts = new RegisterDisputeAgentOptionParser(args).parse();
+ if (opts.isForHelp()) {
+ out.println(getMethodHelp(helpService, method));
+ return;
+ }
+ var disputeAgentType = opts.getDisputeAgentType();
+ var registrationKey = opts.getRegistrationKey();
var requestBuilder = RegisterDisputeAgentRequest.newBuilder()
.setDisputeAgentType(disputeAgentType).setRegistrationKey(registrationKey);
disputeAgentsService.registerDisputeAgent(requestBuilder.build());
@@ -759,7 +785,7 @@ private static File saveFileToDisk(String prefix,
}
}
- private static void printHelp(OptionParser parser, PrintStream stream) {
+ private static void printHelp(OptionParser parser, @SuppressWarnings("SameParameterValue") PrintStream stream) {
try {
stream.println("Bisq RPC Client");
stream.println();
@@ -770,45 +796,93 @@ private static void printHelp(OptionParser parser, PrintStream stream) {
String rowFormat = "%-24s%-52s%s%n";
stream.format(rowFormat, "Method", "Params", "Description");
stream.format(rowFormat, "------", "------", "------------");
- stream.format(rowFormat, "getversion", "", "Get server version");
- stream.format(rowFormat, "getbalance", "[currency code = bsq|btc]", "Get server wallet balances");
- stream.format(rowFormat, "getaddressbalance", "address", "Get server wallet address balance");
- stream.format(rowFormat, "getfundingaddresses", "", "Get BTC funding addresses");
- stream.format(rowFormat, "getunusedbsqaddress", "", "Get unused BSQ address");
- stream.format(rowFormat, "sendbsq", "address, amount [,tx fee rate (sats/byte)]", "Send BSQ");
- stream.format(rowFormat, "sendbtc", "address, amount [,tx fee rate (sats/byte), \"memo\"]", "Send BTC");
- stream.format(rowFormat, "gettxfeerate", "", "Get current tx fee rate in sats/byte");
- stream.format(rowFormat, "settxfeerate", "satoshis (per byte)", "Set custom tx fee rate in sats/byte");
- stream.format(rowFormat, "unsettxfeerate", "", "Unset custom tx fee rate");
- stream.format(rowFormat, "gettransaction", "transaction id", "Get transaction with id");
- stream.format(rowFormat, "createoffer", "payment acct id, buy | sell, currency code, \\", "Create and place an offer");
- stream.format(rowFormat, "", "amount (btc), min amount, use mkt based price, \\", "");
- stream.format(rowFormat, "", "fixed price (btc) | mkt price margin (%), security deposit (%) \\", "");
- stream.format(rowFormat, "", "[,maker fee currency code = bsq|btc]", "");
- stream.format(rowFormat, "canceloffer", "offer id", "Cancel offer with id");
- stream.format(rowFormat, "getoffer", "offer id", "Get current offer with id");
- stream.format(rowFormat, "getmyoffer", "offer id", "Get my current offer with id");
- stream.format(rowFormat, "getoffers", "buy | sell, currency code", "Get current offers");
- stream.format(rowFormat, "getmyoffers", "buy | sell, currency code", "Get my current offers");
- stream.format(rowFormat, "takeoffer", "offer id [,taker fee currency code = bsq|btc]", "Take offer with id");
- stream.format(rowFormat, "gettrade", "trade id [,showcontract = true|false]", "Get trade summary or full contract");
- stream.format(rowFormat, "confirmpaymentstarted", "trade id", "Confirm payment started");
- stream.format(rowFormat, "confirmpaymentreceived", "trade id", "Confirm payment received");
- stream.format(rowFormat, "keepfunds", "trade id", "Keep received funds in Bisq wallet");
- stream.format(rowFormat, "withdrawfunds", "trade id, bitcoin wallet address [,\"memo\"]",
+ stream.format(rowFormat, getversion.name(), "", "Get server version");
+ stream.println();
+ stream.format(rowFormat, getbalance.name(), "[--currency-code=]", "Get server wallet balances");
+ stream.println();
+ stream.format(rowFormat, getaddressbalance.name(), "--address=", "Get server wallet address balance");
+ stream.println();
+ stream.format(rowFormat, getfundingaddresses.name(), "", "Get BTC funding addresses");
+ stream.println();
+ stream.format(rowFormat, getunusedbsqaddress.name(), "", "Get unused BSQ address");
+ stream.println();
+ stream.format(rowFormat, sendbsq.name(), "--address= --amount= \\", "Send BSQ");
+ stream.format(rowFormat, "", "[--tx-fee-rate=]", "");
+ stream.println();
+ stream.format(rowFormat, sendbtc.name(), "--address= --amount= \\", "Send BTC");
+ stream.format(rowFormat, "", "[--tx-fee-rate=]", "");
+ stream.println();
+ stream.format(rowFormat, gettxfeerate.name(), "", "Get current tx fee rate in sats/byte");
+ stream.println();
+ stream.format(rowFormat, settxfeerate.name(), "--tx-fee-rate=", "Set custom tx fee rate in sats/byte");
+ stream.println();
+ stream.format(rowFormat, unsettxfeerate.name(), "", "Unset custom tx fee rate");
+ stream.println();
+ stream.format(rowFormat, gettransaction.name(), "--transaction-id=", "Get transaction with id");
+ stream.println();
+ stream.format(rowFormat, createoffer.name(), "--payment-account= \\", "Create and place an offer");
+ stream.format(rowFormat, "", "--direction= \\", "");
+ stream.format(rowFormat, "", "--currency-code= \\", "");
+ stream.format(rowFormat, "", "--amount= \\", "");
+ stream.format(rowFormat, "", "[--min-amount=] \\", "");
+ stream.format(rowFormat, "", "--fixed-price= | --market-price=margin= \\", "");
+ stream.format(rowFormat, "", "--security-deposit= \\", "");
+ stream.format(rowFormat, "", "[--fee-currency=]", "");
+ stream.println();
+ stream.format(rowFormat, canceloffer.name(), "--offer-id=", "Cancel offer with id");
+ stream.println();
+ stream.format(rowFormat, getoffer.name(), "--offer-id=", "Get current offer with id");
+ stream.println();
+ stream.format(rowFormat, getmyoffer.name(), "--offer-id=", "Get my current offer with id");
+ stream.println();
+ stream.format(rowFormat, getoffers.name(), "--direction= \\", "Get current offers");
+ stream.format(rowFormat, "", "--currency-code=", "");
+ stream.println();
+ stream.format(rowFormat, getmyoffers.name(), "--direction= \\", "Get my current offers");
+ stream.format(rowFormat, "", "--currency-code=", "");
+ stream.println();
+ stream.format(rowFormat, takeoffer.name(), "--offer-id= [--fee-currency=]", "Take offer with id");
+ stream.println();
+ stream.format(rowFormat, gettrade.name(), "--trade-id= \\", "Get trade summary or full contract");
+ stream.format(rowFormat, "", "[--show-contract=]", "");
+ stream.println();
+ stream.format(rowFormat, confirmpaymentstarted.name(), "--trade-id=", "Confirm payment started");
+ stream.println();
+ stream.format(rowFormat, confirmpaymentreceived.name(), "--trade-id=", "Confirm payment received");
+ stream.println();
+ stream.format(rowFormat, keepfunds.name(), "--trade-id=", "Keep received funds in Bisq wallet");
+ stream.println();
+ stream.format(rowFormat, withdrawfunds.name(), "--trade-id= --address= \\",
"Withdraw received funds to external wallet address");
- stream.format(rowFormat, "getpaymentmethods", "", "Get list of supported payment account method ids");
- stream.format(rowFormat, "getpaymentacctform", "payment method id", "Get a new payment account form");
- stream.format(rowFormat, "createpaymentacct", "path to payment account form", "Create a new payment account");
- stream.format(rowFormat, "getpaymentaccts", "", "Get user payment accounts");
- stream.format(rowFormat, "lockwallet", "", "Remove wallet password from memory, locking the wallet");
- stream.format(rowFormat, "unlockwallet", "password timeout",
+ stream.format(rowFormat, "", "[--memo=<\"memo\">]", "");
+ stream.println();
+ stream.format(rowFormat, getpaymentmethods.name(), "", "Get list of supported payment account method ids");
+ stream.println();
+ stream.format(rowFormat, getpaymentacctform.name(), "--payment-method-id=", "Get a new payment account form");
+ stream.println();
+ stream.format(rowFormat, createpaymentacct.name(), "--payment-account-form=", "Create a new payment account");
+ stream.println();
+ stream.format(rowFormat, getpaymentaccts.name(), "", "Get user payment accounts");
+ stream.println();
+ stream.format(rowFormat, lockwallet.name(), "", "Remove wallet password from memory, locking the wallet");
+ stream.println();
+ stream.format(rowFormat, unlockwallet.name(), "--wallet-password= --timeout=",
"Store wallet password in memory for timeout seconds");
- stream.format(rowFormat, "setwalletpassword", "password [newpassword]",
+ stream.println();
+ stream.format(rowFormat, setwalletpassword.name(), "--wallet-password= \\",
"Encrypt wallet with password, or set new password on encrypted wallet");
+ stream.format(rowFormat, "", "[--new-wallet-password=]", "");
+ stream.println();
+ stream.println("Method Help Usage: bisq-cli [options] --help");
stream.println();
} catch (IOException ex) {
ex.printStackTrace(stream);
}
}
+
+ private static String getMethodHelp(HelpBlockingStub helpService, Method method) {
+ var request = GetMethodHelpRequest.newBuilder().setMethodName(method.name()).build();
+ var reply = helpService.getMethodHelp(request);
+ return reply.getMethodHelp();
+ }
}
From acf2c7c50ef1bf7c3f03f35c71a81ebc3b0e98eb Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 10:27:47 -0300
Subject: [PATCH 06/15] Remove deprecated NegativeNumberOptions
Not needed anymore, and all method opts are posix style.
(The opts parsing lib used to treat negative numbers as opt labels.)
---
.../java/bisq/cli/NegativeNumberOptions.java | 97 -------------------
1 file changed, 97 deletions(-)
delete mode 100644 cli/src/main/java/bisq/cli/NegativeNumberOptions.java
diff --git a/cli/src/main/java/bisq/cli/NegativeNumberOptions.java b/cli/src/main/java/bisq/cli/NegativeNumberOptions.java
deleted file mode 100644
index 6623f9ad150..00000000000
--- a/cli/src/main/java/bisq/cli/NegativeNumberOptions.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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;
-
-import java.math.BigDecimal;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Predicate;
-
-import static java.util.Arrays.stream;
-import static java.util.stream.IntStream.range;
-
-class NegativeNumberOptions {
-
- private final Map negativeNumberParams = new HashMap<>();
-
- String[] removeNegativeNumberOptions(String[] args) {
- // Cache any negative number params that will be rejected by the parser.
- // This should be called before command line parsing.
- int skipped = getIndexOfMethodInArgs(args);
- for (int i = skipped; i < args.length; i++) {
- if (isNegativeNumber.test(args[i])) {
- String param = args[i];
- negativeNumberParams.put(i - skipped, new BigDecimal(param).toString());
- // Substitute a zero placeholder at the index containing the
- // negative number positional option value.
- args[i] = "0";
- }
- }
- return args;
- }
-
- List restoreNegativeNumberOptions(List nonOptionArgs) {
- // Put cached negative number params into a clone of the nonOptionArgs list.
- // This should be called after command line parsing.
- if (!negativeNumberParams.isEmpty()) {
- List nonOptionArgsClone = new ArrayList<>(nonOptionArgs);
- negativeNumberParams.forEach((k, v) -> {
- int idx = k;
- nonOptionArgsClone.set(idx, v);
- });
- return Collections.unmodifiableList(nonOptionArgsClone);
- } else {
- // This should never happen. Instances of this class should not be created
- // if there are no negative number options.
- return nonOptionArgs;
- }
- }
-
- static boolean hasNegativeNumberOptions(String[] args) {
- return stream(args).anyMatch(isNegativeNumber);
- }
-
- private static final Predicate isNegativeNumber = (param) -> {
- if (param.length() > 1 && param.startsWith("-")) {
- try {
- new BigDecimal(param);
- return true;
- } catch (NumberFormatException ignored) {
- // empty
- }
- }
- return false;
- };
-
- private int getIndexOfMethodInArgs(String[] args) {
- // The first argument that does not start with '-' or '--' is the method name.
- // Skip over the --password=xyz [--host=s --port=n] options.
- int skipped = range(0, args.length)
- .filter(i -> !args[i].startsWith("-"))
- .findFirst()
- .orElse(-1);
- if (skipped >= 0)
- return skipped;
- else
- throw new IllegalArgumentException("required --password option not found");
- }
-}
From baf79e2b50024998e2d3bda8886cfa01ada1f041 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 19:03:49 -0300
Subject: [PATCH 07/15] Make opt description more generic
---
cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java
index d1edf181063..2b7681f3c69 100644
--- a/cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java
+++ b/cli/src/main/java/bisq/cli/opts/GetTradeOptionParser.java
@@ -26,7 +26,7 @@
public class GetTradeOptionParser extends AbstractMethodOptionParser implements MethodOpts {
- final OptionSpec tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade to get")
+ final OptionSpec tradeIdOpt = parser.accepts(OPT_TRADE_ID, "id of trade")
.withRequiredArg()
.defaultsTo(EMPTY);
From f2a899917c19aa8337cb46c238dff9a8ca2387c6 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 19:05:27 -0300
Subject: [PATCH 08/15] Fix default opt values
---
.../main/java/bisq/cli/opts/CreateOfferOptionParser.java | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
index 4cb6a24a4cc..d4d7c05c7ad 100644
--- a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
+++ b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
@@ -50,7 +50,7 @@ public class CreateOfferOptionParser extends AbstractMethodOptionParser implemen
final OptionSpec mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)")
.withOptionalArg()
- .defaultsTo(EMPTY);
+ .defaultsTo("0.00");
final OptionSpec fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price")
.withOptionalArg()
@@ -119,7 +119,7 @@ public boolean isUsingMktPriceMargin() {
@SuppressWarnings("unused")
public String getMktPriceMargin() {
- return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "";
+ return isUsingMktPriceMargin() ? options.valueOf(mktPriceMarginOpt) : "0.00";
}
public BigDecimal getMktPriceMarginAsBigDecimal() {
@@ -127,7 +127,7 @@ public BigDecimal getMktPriceMarginAsBigDecimal() {
}
public String getFixedPrice() {
- return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "";
+ return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0.00";
}
public String getSecurityDeposit() {
From d756d3dcf377418daf1523bf095921c26e56053a Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Thu, 14 Jan 2021 19:15:16 -0300
Subject: [PATCH 09/15] Add simple api trading script
Emulates Bob and Alice trading with each other with regtest BTC.
---
apitest/scripts/simple-trading-script.sh | 209 ++++++++++++++++++
apitest/scripts/trading-script-env.sh | 256 +++++++++++++++++++++++
2 files changed, 465 insertions(+)
create mode 100755 apitest/scripts/simple-trading-script.sh
create mode 100755 apitest/scripts/trading-script-env.sh
diff --git a/apitest/scripts/simple-trading-script.sh b/apitest/scripts/simple-trading-script.sh
new file mode 100755
index 00000000000..0a156758de1
--- /dev/null
+++ b/apitest/scripts/simple-trading-script.sh
@@ -0,0 +1,209 @@
+#! /bin/bash
+
+# Script for running simple fiat <-> btc trading scenarios using the API CLI on regtest.
+#
+# Prerequisites:
+#
+# - Linux or OSX with bash, Java 10, or Java 11-12 (JDK language compatibility 10).
+#
+# - Bisq must be fully built with apitest dao setup files installed.
+# Build command: `./gradlew clean build :apitest:installDaoSetup`
+#
+# - All supporting nodes must be run locally, in dev/dao/regtest mode:
+# bitcoind, seednode, arbdaemon, alicedaemon, bobdaemon
+#
+# These should be run using the apitest harness. From the root project dir, run:
+# `./bisq-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon --shutdownAfterTests=false`
+#
+# - Only regtest btc can be bought or sold with the default dummy eur account.
+# The CLI supports creating other fiat payment accts but this script doesn't (todo).
+#
+# Usage:
+#
+# This script must be run from the root of the project, e.g.:
+#
+# `apitest/scripts/simple-trading-script.sh -d buy -c eur -m 3.00 -a 0.125`
+#
+# Script options: -d -c -m - f -a
+#
+# Examples:
+#
+# Create a buy/eur offer to buy 0.125 btc at a mkt-price-margin of 0%:
+#
+# `apitest/scripts/simple-trading-script.sh -d buy -c usd -m 0.00 -a 0.125`
+#
+# Create a sell/eur offer to sell 0.125 btc at a fixed-price of 23,000 euros:
+#
+# `apitest/scripts/simple-trading-script.sh -d sell -c usd -f 23000 -a 0.125`
+
+APP_BASE_NAME=`basename "$0"`
+APP_HOME="`pwd -P`"
+APITEST_SCRIPTS_HOME="${APP_HOME}/apitest/scripts"
+
+# Source the env and some helper functions.
+. ${APITEST_SCRIPTS_HOME}/trading-script-env.sh
+
+checksetup
+parseopts "$@"
+
+printdate "Started ${APP_BASE_NAME} with parameters:"
+printscriptparams
+
+registerdisputeagents
+
+# Get balances.
+printdate "Bob & Alice's balances before trade:"
+printdate_sameline "ALICE CLI:"
+printbalances $ALICE_PORT
+printbreak
+
+printdate_sameline "BOB CLI:"
+printbalances $BOB_PORT
+printbreak
+
+printdate_sameline "ALICE CLI:"
+getpaymentaccts ${ALICE_PORT}
+
+ALICE_ACCT_ID=$(getdummyacctid ${ALICE_PORT})
+printdate "ALICE ${ALICE_ROLE}: Fiat Acct ID: ${ALICE_ACCT_ID}"
+printbreak
+
+
+printdate_sameline "BOB CLI:"
+getpaymentaccts ${BOB_PORT}
+
+BOB_ACCT_ID=$(getdummyacctid ${BOB_PORT})
+printdate "Bob's Fiat Acct ID: ${BOB_ACCT_ID}"
+printbreak
+
+printdate "ALICE ${ALICE_ROLE}: Creating ${DIRECTION} ${CURRENCY_CODE} offer with payment acct ${ALICE_ACCT_ID}."
+CMD="$CLI_BASE --port=${ALICE_PORT} createoffer"
+CMD+=" --payment-account=${ALICE_ACCT_ID}"
+CMD+=" --direction=${DIRECTION}"
+CMD+=" --currency-code=${CURRENCY_CODE}"
+CMD+=" --amount=${AMOUNT}"
+if [ -z "${MKT_PRICE_MARGIN}" ]; then
+ CMD+=" --fixed-price=${FIXED_PRICE}"
+else
+ CMD+=" --market-price-margin=${MKT_PRICE_MARGIN}"
+fi
+CMD+=" --security-deposit=15.0"
+CMD+=" --fee-currency=BSQ"
+printdate_sameline "ALICE CLI:"
+printcmd "$CMD"
+
+OFFER_ID=$(createoffer "${CMD}")
+printdate "ALICE ${ALICE_ROLE}: Created offer with id: ${OFFER_ID}."
+printbreak
+sleeptraced 10
+
+# Generate some btc blocks.
+printdate "Generating btc blocks after publishing Alice's offer."
+genbtcblocks 3 5
+printbreak
+sleeptraced 10
+
+# List offers.
+printdate "BOB ${BOB_ROLE}: Looking at ${DIRECTION} ${CURRENCY_CODE} offers."
+CMD="$CLI_BASE --port=${BOB_PORT} getoffers --direction=${DIRECTION} --currency-code=${CURRENCY_CODE}"
+printdate_sameline "BOB CLI:"
+printcmd "$CMD"
+OFFERS=$($CMD)
+echo "${OFFERS}"
+printbreak
+sleeptraced 3
+
+# Take offer.
+printdate "BOB ${BOB_ROLE}: Taking offer ${OFFER_ID} with payment acct ${BOB_ACCT_ID}."
+CMD="$CLI_BASE --port=${BOB_PORT} takeoffer --offer-id=${OFFER_ID} --payment-account=${BOB_ACCT_ID} --fee-currency=bsq"
+printdate_sameline "BOB CLI:"
+printcmd "$CMD"
+TRADE=$($CMD)
+echo "${TRADE}"
+printbreak
+sleeptraced 10
+
+# Generating some btc blocks
+printdate "Generating btc blocks after Bob takes Alice's offer."
+genbtcblocks 3 3
+printbreak
+sleeptraced 6
+
+# Send payment sent and received messages.
+if [ $DIRECTION = "BUY" ]
+then
+ PAYER="ALICE ${ALICE_ROLE}"
+ PAYER_PORT=${ALICE_PORT}
+ PAYER_CLI="ALICE CLI"
+ PAYEE="BOB ${BOB_ROLE}"
+ PAYEE_PORT=${BOB_PORT}
+ PAYEE_CLI="BOB CLI"
+else
+ PAYER="BOB ${BOB_ROLE}"
+ PAYER_PORT=${BOB_PORT}
+ PAYER_CLI="BOB CLI"
+ PAYEE="ALICE ${ALICE_ROLE}"
+ PAYEE_PORT=${ALICE_PORT}
+ PAYEE_CLI="ALICE CLI"
+fi
+
+# Confirm payment started.
+printdate "${PAYER}: Sending fiat payment sent msg."
+CMD="$CLI_BASE --port=${PAYER_PORT} confirmpaymentstarted --trade-id=${OFFER_ID}"
+printdate_sameline "${PAYER_CLI}:"
+printcmd "$CMD"
+SENT_MSG=$($CMD)
+printdate "${SENT_MSG}"
+printbreak
+
+sleeptraced 2
+printdate "Generating btc blocks after fiat payment sent msg."
+genbtcblocks 3 5
+sleeptraced 2
+
+# Confirm payment received.
+printdate "${PAYEE}: Sending fiat payment received msg."
+CMD="$CLI_BASE --port=${PAYEE_PORT} confirmpaymentreceived --trade-id=${OFFER_ID}"
+printdate_sameline "${PAYEE_CLI}:"
+printcmd "$CMD"
+RCVD_MSG=$($CMD)
+printdate "${RCVD_MSG}"
+printbreak
+sleeptraced 4
+
+
+# Generate some btc blocks
+printdate "Generating btc blocks after fiat transfer."
+genbtcblocks 3 5
+printbreak
+sleeptraced 3
+
+# Complete the trade on the seller side.
+if [ $DIRECTION = "BUY" ]
+then
+ printdate "BOB ${BOB_ROLE}: Closing trade by keeping funds in Bisq wallet."
+ CMD="$CLI_BASE --port=${BOB_PORT} keepfunds --trade-id=${OFFER_ID}"
+ printdate_sameline "BOB CLI:"
+ printcmd "$CMD"
+else
+ printdate "ALICE (taker): Closing trade by keeping funds in Bisq wallet."
+ CMD="$CLI_BASE --port=${ALICE_PORT} keepfunds --trade-id=${OFFER_ID}"
+ printdate_sameline "ALICE CLI:"
+ printcmd "$CMD"
+fi
+KEEP_FUNDS_MSG=$($CMD)
+printdate "${KEEP_FUNDS_MSG}"
+sleeptraced 5
+printbreak
+
+
+# Get balances after trade completion.
+printdate "Bob & Alice's balances after trade:"
+printdate_sameline "ALICE CLI:"
+printbalances $ALICE_PORT
+printbreak
+printdate_sameline "BOB CLI:"
+printbalances $BOB_PORT
+printbreak
+
+exit 0
diff --git a/apitest/scripts/trading-script-env.sh b/apitest/scripts/trading-script-env.sh
new file mode 100755
index 00000000000..65ea614da2e
--- /dev/null
+++ b/apitest/scripts/trading-script-env.sh
@@ -0,0 +1,256 @@
+#! /bin/bash
+
+# This file must be sourced by the main driver.
+
+export CLI_BASE="./bisq-cli --password=xyz"
+export ALICE_PORT=9998
+export BOB_PORT=9999
+
+printdate() {
+ echo "[`date`] $@"
+}
+
+printdate "Started ${APP_HOME}/${APP_BASE_NAME}."
+
+checksetup() {
+ apitestusage() {
+ echo "The apitest harness must be running a local bitcoin regtest node, a seednode, arbitration node, and Bob & Alice daemons."
+ echo ""
+ echo "From the project's root dir, start all supporting nodes from a terminal:"
+ echo "./bisq-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon --shutdownAfterTests=false"
+ echo ""
+ echo "Register dispute agents in the arbitration daemon after it initializes."
+ echo "./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=mediator \
+# --registration-key=6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
+ echo "./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=refundagent \
+# --registration-key=6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
+ exit 1;
+ }
+ printdate "Checking ${APP_HOME} for some expected directories and files."
+ if [ -d "${APP_HOME}/apitest" ]; then
+ printdate "Subproject apitest exists.";
+ else
+ printdate "Error: Subproject apitest not found, maybe because you are not running the script from the project root dir."
+ exit 1
+ fi
+ if [ -f "${APP_HOME}/bisq-cli" ]; then
+ printdate "The bisq-cli script exists.";
+ else
+ printdate "Error: The bisq-cli script not found, maybe because you are not running the script from the project root dir."
+ exit 1
+ fi
+ printdate "Checking to see local bitcoind is running."
+ checkbitcoindrunning
+ printdate "Checking to see bisq servers are running."
+ if pgrep -f "bisq.seednode.SeedNodeMain" > /dev/null ; then
+ printdate "The seednode is running on host."
+ else
+ printdate "Error: seed is not running on host, exiting."
+ apitestusage
+ fi
+ if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Arb_dao" > /dev/null ; then
+ printdate "The arbitration node is running on host."
+ else
+ printdate "Error: arbitration node is not running on host, exiting."
+ apitestusage
+ fi
+ if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Alice_dao" > /dev/null ; then
+ printdate "Alice's daemon node is running on host."
+ else
+ printdate "Error: Alice's daemon node is not running on host, exiting."
+ apitestusage
+ fi
+ if pgrep -f "bisq.daemon.app.BisqDaemonMain --appName=bisq-BTC_REGTEST_Bob_dao" > /dev/null ; then
+ printdate "Bob's daemon node is running on host."
+ else
+ printdate "Error: Bob's daemon node is not running on host, exiting."
+ apitestusage
+ fi
+}
+
+parseopts() {
+ usage() {
+ echo "Usage: $0 [-d buy|sell] [-c ] [-f || -m ] [-a ]" 1>&2
+ exit 1;
+ }
+
+ local OPTIND o d c f m a
+ while getopts "d:c:f:m:a:" o; do
+ case "${o}" in
+ d) d=$(echo ${OPTARG} | tr '[:lower:]' '[:upper:]')
+ ((d == "BUY" || d == "SELL")) || usage
+ export DIRECTION=${d}
+ ;;
+ c) c=$(echo ${OPTARG} | tr '[:lower:]' '[:upper:]')
+ export CURRENCY_CODE=${c}
+ ;;
+ f) f=${OPTARG}
+ export FIXED_PRICE=${f}
+ ;;
+ m) m=${OPTARG}
+ export MKT_PRICE_MARGIN=${m}
+ ;;
+ a) a=${OPTARG}
+ export AMOUNT=${a}
+ ;;
+ *) usage ;;
+ esac
+ done
+ shift $((OPTIND-1))
+
+ if [ -z "${d}" ] || [ -z "${c}" ] || [ -z "${a}" ]; then
+ usage
+ fi
+
+ if [ -z "${f}" ] && [ -z "${m}" ]; then
+ usage
+ fi
+
+ if [ -n "${f}" ] && [ -n "${m}" ]; then
+ printdate "Must use margin-from-price param (-m) or fixed-price param (-f), not both."
+ usage
+ fi
+
+ if [ $DIRECTION = "SELL" ]
+ then
+ export BOB_ROLE="Bob (taker/buyer)"
+ export ALICE_ROLE="Alice (maker/seller)"
+ else
+ export BOB_ROLE="Bob (taker/seller)"
+ export ALICE_ROLE="Alice (maker/buyer)"
+ fi
+}
+
+printscriptparams() {
+ echo " DIRECTION = ${DIRECTION}"
+ echo " CURRENCY_CODE = ${CURRENCY_CODE}"
+ echo " FIXED_PRICE = ${FIXED_PRICE}"
+ echo " MKT_PRICE_MARGIN = ${MKT_PRICE_MARGIN}"
+ echo " AMOUNT = ${AMOUNT}"
+ echo " BOB_ROLE = ${BOB_ROLE}"
+ echo " ALICE_ROLE = ${ALICE_ROLE}"
+}
+
+checkbitcoindrunning() {
+ # There may be a '+' char in the path and we have to escape it for pgrep.
+ if [[ ${APP_HOME} == *"+"* ]]; then
+ ESCAPED_APP_HOME=$(escapepluschar ${APP_HOME})
+ else
+ ESCAPED_APP_HOME=${APP_HOME}
+ fi
+ if pgrep -f "bitcoind -datadir=${ESCAPED_APP_HOME}/apitest/build/resources/main/Bitcoin-regtest" > /dev/null ; then
+ printdate "The regtest bitcoind node is running on host."
+ else
+ printdate "Error: regtest bitcoind node is not running on host, exiting."
+ apitestusage
+ fi
+}
+
+registerdisputeagents() {
+ # Silently register dev dispute agents. It's easy to forget.
+ REG_KEY="6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
+ SILENT=$(./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=mediator --registration-key=${REG_KEY})
+ SILENT=$(./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=refundagent --registration-key=${REG_KEY})
+}
+
+
+printbreak() {
+ echo ""
+ echo ""
+}
+
+printcmd() {
+ echo -en "$@\n"
+}
+
+printdate_sameline() {
+ echo -n "[`date`] $@ "
+}
+
+sleeptraced() {
+ PERIOD=$1
+ printdate "sleeping for $PERIOD"
+ sleep $PERIOD
+}
+
+printbalances() {
+ PORT=$1
+ printcmd "${CLI_BASE} --port=${PORT} getbalance"
+ $CLI_BASE --port=${PORT} getbalance
+}
+
+getpaymentaccts() {
+ PORT=$1
+ printcmd "${CLI_BASE} --port=${PORT} getpaymentaccts"
+ $CLI_BASE --port=${PORT} getpaymentaccts
+}
+
+getdummyacctid() {
+ PORT=$1
+ PAYMENT_ACCTS=$(${CLI_BASE} --port=${PORT} getpaymentaccts)
+ DUMMY_ACCT_1=$(echo -e "${PAYMENT_ACCTS}" | sed -n '2p')
+ DUMMY_ACCT_2=$(echo -e "${PAYMENT_ACCTS}" | sed -n '3p')
+ if [[ "$DUMMY_ACCT_1=" == *"PerfectMoney dummy"* ]]; then
+ DUMMY_ACCT=$DUMMY_ACCT_1
+ else
+ DUMMY_ACCT=$DUMMY_ACCT_2
+ fi
+ ACCT_ID=$(echo -e $DUMMY_ACCT | awk '{print $NF}')
+ echo "${ACCT_ID}"
+}
+
+createoffer() {
+ CREATE_OFFER_CMD=$1
+ OFFER_DESC=$($CREATE_OFFER_CMD)
+
+ ### This is an effort to handling any createoffer error. If error, should echo Error, and the calling script
+ ### should sleep awhile then exit 1.
+ if [[ "$OFFER_DESC" != "Buy/Sell"* ]]; then
+ echo "=========================================================="
+ echo "Error: ${OFFER_DESC}"
+ echo "=========================================================="
+ sleep 5
+ fi
+
+ OFFER_DETAIL=$(echo -e "${OFFER_DESC}" | sed -n '2p')
+ NEW_OFFER_ID=$(echo -e ${OFFER_DETAIL} | awk '{print $NF}')
+ echo "${NEW_OFFER_ID}"
+}
+
+genbtcblocks() {
+ NUM_BLOCKS=$1
+ SECONDS_BETWEEN_BLOCKS=$2
+ for i in $(seq -f "%02g" 1 ${NUM_BLOCKS})
+ do
+ genbtcblock
+ sleep ${SECONDS_BETWEEN_BLOCKS}
+ done
+}
+
+genbtcblock() {
+ # TODO use bitcoin-cli to get new address; cannot assume same address exists on other regtest bitcoin-core hosts.
+ CMD='bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1 "2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv"'
+ printcmd "$CMD"
+ bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1 "2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv"
+}
+
+escapepluschar() {
+ STRING=$1
+ NEW_STRING=$(sed 's/+/\\&/g' <<< ${STRING})
+ echo "${NEW_STRING}"
+}
+
+# Keep this in case there is a need to ready user input from stdin.
+readYesOrNo() {
+ question=$1
+ echo -n "$question Yes or No: "
+ read answer
+ answer=`echo $answer | tr [a-z] [A-Z]`
+ if [ $answer = Y ]
+ then
+ echo You answered yes: $answer
+ else
+ echo You answered no: $answer
+ fi
+ return 0
+}
From 9a935d289d40d5a367a5b67085921cb5e59f7e5b Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 15 Jan 2021 13:08:16 -0300
Subject: [PATCH 10/15] Fix bash syntax problems (codacy)
Get new btc core address before generating btc blocks.
---
apitest/scripts/simple-trading-script.sh | 23 ++++++-----
apitest/scripts/trading-script-env.sh | 51 ++++++++++++++----------
2 files changed, 42 insertions(+), 32 deletions(-)
diff --git a/apitest/scripts/simple-trading-script.sh b/apitest/scripts/simple-trading-script.sh
index 0a156758de1..5eb62026e50 100755
--- a/apitest/scripts/simple-trading-script.sh
+++ b/apitest/scripts/simple-trading-script.sh
@@ -36,43 +36,44 @@
#
# `apitest/scripts/simple-trading-script.sh -d sell -c usd -f 23000 -a 0.125`
-APP_BASE_NAME=`basename "$0"`
-APP_HOME="`pwd -P`"
+
+APP_BASE_NAME=$(basename "$0")
+APP_HOME=$(pwd -P)
APITEST_SCRIPTS_HOME="${APP_HOME}/apitest/scripts"
# Source the env and some helper functions.
-. ${APITEST_SCRIPTS_HOME}/trading-script-env.sh
+. "${APITEST_SCRIPTS_HOME}/trading-script-env.sh"
checksetup
parseopts "$@"
printdate "Started ${APP_BASE_NAME} with parameters:"
printscriptparams
+printbreak
registerdisputeagents
# Get balances.
printdate "Bob & Alice's balances before trade:"
printdate_sameline "ALICE CLI:"
-printbalances $ALICE_PORT
+printbalances "$ALICE_PORT"
printbreak
printdate_sameline "BOB CLI:"
-printbalances $BOB_PORT
+printbalances "$BOB_PORT"
printbreak
printdate_sameline "ALICE CLI:"
-getpaymentaccts ${ALICE_PORT}
+getpaymentaccts "$ALICE_PORT"
-ALICE_ACCT_ID=$(getdummyacctid ${ALICE_PORT})
+ALICE_ACCT_ID=$(getdummyacctid "$ALICE_PORT")
printdate "ALICE ${ALICE_ROLE}: Fiat Acct ID: ${ALICE_ACCT_ID}"
printbreak
-
printdate_sameline "BOB CLI:"
getpaymentaccts ${BOB_PORT}
-BOB_ACCT_ID=$(getdummyacctid ${BOB_PORT})
+BOB_ACCT_ID=$(getdummyacctid "$BOB_PORT")
printdate "Bob's Fiat Acct ID: ${BOB_ACCT_ID}"
printbreak
@@ -130,7 +131,7 @@ printbreak
sleeptraced 6
# Send payment sent and received messages.
-if [ $DIRECTION = "BUY" ]
+if [ "${DIRECTION}" = "BUY" ]
then
PAYER="ALICE ${ALICE_ROLE}"
PAYER_PORT=${ALICE_PORT}
@@ -179,7 +180,7 @@ printbreak
sleeptraced 3
# Complete the trade on the seller side.
-if [ $DIRECTION = "BUY" ]
+if [ "${DIRECTION}" = "BUY" ]
then
printdate "BOB ${BOB_ROLE}: Closing trade by keeping funds in Bisq wallet."
CMD="$CLI_BASE --port=${BOB_PORT} keepfunds --trade-id=${OFFER_ID}"
diff --git a/apitest/scripts/trading-script-env.sh b/apitest/scripts/trading-script-env.sh
index 65ea614da2e..a660b91086e 100755
--- a/apitest/scripts/trading-script-env.sh
+++ b/apitest/scripts/trading-script-env.sh
@@ -7,7 +7,7 @@ export ALICE_PORT=9998
export BOB_PORT=9999
printdate() {
- echo "[`date`] $@"
+ echo "[$(date)] $@"
}
printdate "Started ${APP_HOME}/${APP_BASE_NAME}."
@@ -21,9 +21,9 @@ checksetup() {
echo ""
echo "Register dispute agents in the arbitration daemon after it initializes."
echo "./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=mediator \
-# --registration-key=6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
+ --registration-key=6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
echo "./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=refundagent \
-# --registration-key=6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
+ --registration-key=6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
exit 1;
}
printdate "Checking ${APP_HOME} for some expected directories and files."
@@ -164,30 +164,30 @@ printcmd() {
}
printdate_sameline() {
- echo -n "[`date`] $@ "
+ echo -n "[$(date)] $@ "
}
sleeptraced() {
PERIOD=$1
printdate "sleeping for $PERIOD"
- sleep $PERIOD
+ sleep "$PERIOD"
}
printbalances() {
PORT=$1
printcmd "${CLI_BASE} --port=${PORT} getbalance"
- $CLI_BASE --port=${PORT} getbalance
+ $CLI_BASE --port="$PORT" getbalance
}
getpaymentaccts() {
PORT=$1
printcmd "${CLI_BASE} --port=${PORT} getpaymentaccts"
- $CLI_BASE --port=${PORT} getpaymentaccts
+ $CLI_BASE --port="$PORT" getpaymentaccts
}
getdummyacctid() {
PORT=$1
- PAYMENT_ACCTS=$(${CLI_BASE} --port=${PORT} getpaymentaccts)
+ PAYMENT_ACCTS=$(${CLI_BASE} --port="$PORT" getpaymentaccts)
DUMMY_ACCT_1=$(echo -e "${PAYMENT_ACCTS}" | sed -n '2p')
DUMMY_ACCT_2=$(echo -e "${PAYMENT_ACCTS}" | sed -n '3p')
if [[ "$DUMMY_ACCT_1=" == *"PerfectMoney dummy"* ]]; then
@@ -195,7 +195,7 @@ getdummyacctid() {
else
DUMMY_ACCT=$DUMMY_ACCT_2
fi
- ACCT_ID=$(echo -e $DUMMY_ACCT | awk '{print $NF}')
+ ACCT_ID=$(echo -e "$DUMMY_ACCT" | awk '{print $NF}')
echo "${ACCT_ID}"
}
@@ -213,30 +213,39 @@ createoffer() {
fi
OFFER_DETAIL=$(echo -e "${OFFER_DESC}" | sed -n '2p')
- NEW_OFFER_ID=$(echo -e ${OFFER_DETAIL} | awk '{print $NF}')
+ NEW_OFFER_ID=$(echo -e "${OFFER_DETAIL}" | awk '{print $NF}')
echo "${NEW_OFFER_ID}"
}
+getbtcoreaddress() {
+ CMD="bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest getnewaddress"
+ NEW_ADDRESS=$(${CMD})
+ echo "${NEW_ADDRESS}"
+}
+
genbtcblocks() {
NUM_BLOCKS=$1
SECONDS_BETWEEN_BLOCKS=$2
+ CMD="bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1 "
+ CMD+="$(getbtcoreaddress)"
+ printdate "$CMD"
for i in $(seq -f "%02g" 1 ${NUM_BLOCKS})
do
- genbtcblock
- sleep ${SECONDS_BETWEEN_BLOCKS}
+ NEW_BLOCK_HASH=$(genbtcblock "$CMD")
+ printdate "Block Hash #$i:${NEW_BLOCK_HASH}"
+ sleep "$SECONDS_BETWEEN_BLOCKS"
done
}
genbtcblock() {
- # TODO use bitcoin-cli to get new address; cannot assume same address exists on other regtest bitcoin-core hosts.
- CMD='bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1 "2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv"'
- printcmd "$CMD"
- bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1 "2N5J6MyjAsWnashimGiNwoRzUXThsQzRmbv"
+ CMD=$1
+ NEW_BLOCK_HASH=$(${CMD} | sed -n '2p')
+ echo "$NEW_BLOCK_HASH"
}
escapepluschar() {
STRING=$1
- NEW_STRING=$(sed 's/+/\\&/g' <<< ${STRING})
+ NEW_STRING=$(echo "${STRING//+/\\+}")
echo "${NEW_STRING}"
}
@@ -245,12 +254,12 @@ readYesOrNo() {
question=$1
echo -n "$question Yes or No: "
read answer
- answer=`echo $answer | tr [a-z] [A-Z]`
- if [ $answer = Y ]
+ answer=$(echo $answer | tr [a-z] [A-Z])
+ if [ "$answer" = "Y" ]
then
- echo You answered yes: $answer
+ echo You answered yes: "$answer"
else
- echo You answered no: $answer
+ echo You answered no: "$answer"
fi
return 0
}
From 240167861073d0fdb2e323fffba0a540132ced5c Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 15 Jan 2021 13:18:42 -0300
Subject: [PATCH 11/15] Fix ROLE defs
---
apitest/scripts/trading-script-env.sh | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/apitest/scripts/trading-script-env.sh b/apitest/scripts/trading-script-env.sh
index a660b91086e..38e0536f587 100755
--- a/apitest/scripts/trading-script-env.sh
+++ b/apitest/scripts/trading-script-env.sh
@@ -113,11 +113,11 @@ parseopts() {
if [ $DIRECTION = "SELL" ]
then
- export BOB_ROLE="Bob (taker/buyer)"
- export ALICE_ROLE="Alice (maker/seller)"
+ export BOB_ROLE="(taker/buyer)"
+ export ALICE_ROLE="(maker/seller)"
else
- export BOB_ROLE="Bob (taker/seller)"
- export ALICE_ROLE="Alice (maker/buyer)"
+ export BOB_ROLE="(taker/seller)"
+ export ALICE_ROLE="(maker/buyer)"
fi
}
From 9de014d941711ec110c4ec556dadedefd7f935c9 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 15 Jan 2021 13:40:50 -0300
Subject: [PATCH 12/15] Fix more syntax problems for codacy
---
apitest/scripts/simple-trading-script.sh | 6 +++---
apitest/scripts/trading-script-env.sh | 25 ++++++++++++++----------
2 files changed, 18 insertions(+), 13 deletions(-)
diff --git a/apitest/scripts/simple-trading-script.sh b/apitest/scripts/simple-trading-script.sh
index 5eb62026e50..188f140a278 100755
--- a/apitest/scripts/simple-trading-script.sh
+++ b/apitest/scripts/simple-trading-script.sh
@@ -71,7 +71,7 @@ printdate "ALICE ${ALICE_ROLE}: Fiat Acct ID: ${ALICE_ACCT_ID}"
printbreak
printdate_sameline "BOB CLI:"
-getpaymentaccts ${BOB_PORT}
+getpaymentaccts "$BOB_PORT"
BOB_ACCT_ID=$(getdummyacctid "$BOB_PORT")
printdate "Bob's Fiat Acct ID: ${BOB_ACCT_ID}"
@@ -201,10 +201,10 @@ printbreak
# Get balances after trade completion.
printdate "Bob & Alice's balances after trade:"
printdate_sameline "ALICE CLI:"
-printbalances $ALICE_PORT
+printbalances "$ALICE_PORT"
printbreak
printdate_sameline "BOB CLI:"
-printbalances $BOB_PORT
+printbalances "$BOB_PORT"
printbreak
exit 0
diff --git a/apitest/scripts/trading-script-env.sh b/apitest/scripts/trading-script-env.sh
index 38e0536f587..d078c8cb00c 100755
--- a/apitest/scripts/trading-script-env.sh
+++ b/apitest/scripts/trading-script-env.sh
@@ -77,11 +77,11 @@ parseopts() {
local OPTIND o d c f m a
while getopts "d:c:f:m:a:" o; do
case "${o}" in
- d) d=$(echo ${OPTARG} | tr '[:lower:]' '[:upper:]')
+ d) d=$(echo "${OPTARG}" | tr '[:lower:]' '[:upper:]')
((d == "BUY" || d == "SELL")) || usage
export DIRECTION=${d}
;;
- c) c=$(echo ${OPTARG} | tr '[:lower:]' '[:upper:]')
+ c) c=$(echo "${OPTARG}"| tr '[:lower:]' '[:upper:]')
export CURRENCY_CODE=${c}
;;
f) f=${OPTARG}
@@ -111,7 +111,7 @@ parseopts() {
usage
fi
- if [ $DIRECTION = "SELL" ]
+ if [ "$DIRECTION" = "SELL" ]
then
export BOB_ROLE="(taker/buyer)"
export ALICE_ROLE="(maker/seller)"
@@ -134,9 +134,9 @@ printscriptparams() {
checkbitcoindrunning() {
# There may be a '+' char in the path and we have to escape it for pgrep.
if [[ ${APP_HOME} == *"+"* ]]; then
- ESCAPED_APP_HOME=$(escapepluschar ${APP_HOME})
+ ESCAPED_APP_HOME=$(escapepluschar "${APP_HOME}")
else
- ESCAPED_APP_HOME=${APP_HOME}
+ ESCAPED_APP_HOME="${APP_HOME}"
fi
if pgrep -f "bitcoind -datadir=${ESCAPED_APP_HOME}/apitest/build/resources/main/Bitcoin-regtest" > /dev/null ; then
printdate "The regtest bitcoind node is running on host."
@@ -151,6 +151,8 @@ registerdisputeagents() {
REG_KEY="6ac43ea1df2a290c1c8391736aa42e4339c5cb4f110ff0257a13b63211977b7a"
SILENT=$(./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=mediator --registration-key=${REG_KEY})
SILENT=$(./bisq-cli --password=xyz --port=9997 registerdisputeagent --dispute-agent-type=refundagent --registration-key=${REG_KEY})
+ # Do something with $SILENT to keep codacy happy.
+ echo "$SILENT" > /dev/null
}
@@ -226,10 +228,13 @@ getbtcoreaddress() {
genbtcblocks() {
NUM_BLOCKS=$1
SECONDS_BETWEEN_BLOCKS=$2
- CMD="bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1 "
- CMD+="$(getbtcoreaddress)"
- printdate "$CMD"
- for i in $(seq -f "%02g" 1 ${NUM_BLOCKS})
+ ADDR_PARAM="$(getbtcoreaddress)"
+ CMD_PREFIX="bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest -rpcpassword=apitest generatetoaddress 1"
+ # Print the generatetoaddress command with double quoted address param, to make it cut & pastable from the console.
+ printdate "$CMD_PREFIX \"$ADDR_PARAM\""
+ # Now create the full generatetoaddress command to be run now.
+ CMD="$CMD_PREFIX $ADDR_PARAM"
+ for i in $(seq -f "%02g" 1 "$NUM_BLOCKS")
do
NEW_BLOCK_HASH=$(genbtcblock "$CMD")
printdate "Block Hash #$i:${NEW_BLOCK_HASH}"
@@ -254,7 +259,7 @@ readYesOrNo() {
question=$1
echo -n "$question Yes or No: "
read answer
- answer=$(echo $answer | tr [a-z] [A-Z])
+ answer=$(echo "$answer" | tr [a-z] [A-Z])
if [ "$answer" = "Y" ]
then
echo You answered yes: "$answer"
From bb48a9d075ac316c01f7a23f3cbe550e154c5d13 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Fri, 15 Jan 2021 13:48:25 -0300
Subject: [PATCH 13/15] Fix log statement
---
apitest/scripts/simple-trading-script.sh | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/apitest/scripts/simple-trading-script.sh b/apitest/scripts/simple-trading-script.sh
index 188f140a278..b631cce3ebb 100755
--- a/apitest/scripts/simple-trading-script.sh
+++ b/apitest/scripts/simple-trading-script.sh
@@ -74,7 +74,7 @@ printdate_sameline "BOB CLI:"
getpaymentaccts "$BOB_PORT"
BOB_ACCT_ID=$(getdummyacctid "$BOB_PORT")
-printdate "Bob's Fiat Acct ID: ${BOB_ACCT_ID}"
+printdate "BOB ${BOB_ROLE}: Fiat Acct ID: ${BOB_ACCT_ID}"
printbreak
printdate "ALICE ${ALICE_ROLE}: Creating ${DIRECTION} ${CURRENCY_CODE} offer with payment acct ${ALICE_ACCT_ID}."
From dcbb4b716b1557c258cb19160e1fc522fdca179a Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Mon, 18 Jan 2021 11:33:47 -0300
Subject: [PATCH 14/15] Fix inconsistent indendation problems
---
apitest/scripts/trading-script-env.sh | 44 ++++++++++++---------------
1 file changed, 20 insertions(+), 24 deletions(-)
diff --git a/apitest/scripts/trading-script-env.sh b/apitest/scripts/trading-script-env.sh
index d078c8cb00c..6ef8733f505 100755
--- a/apitest/scripts/trading-script-env.sh
+++ b/apitest/scripts/trading-script-env.sh
@@ -188,35 +188,31 @@ getpaymentaccts() {
}
getdummyacctid() {
- PORT=$1
- PAYMENT_ACCTS=$(${CLI_BASE} --port="$PORT" getpaymentaccts)
- DUMMY_ACCT_1=$(echo -e "${PAYMENT_ACCTS}" | sed -n '2p')
- DUMMY_ACCT_2=$(echo -e "${PAYMENT_ACCTS}" | sed -n '3p')
- if [[ "$DUMMY_ACCT_1=" == *"PerfectMoney dummy"* ]]; then
- DUMMY_ACCT=$DUMMY_ACCT_1
- else
- DUMMY_ACCT=$DUMMY_ACCT_2
- fi
- ACCT_ID=$(echo -e "$DUMMY_ACCT" | awk '{print $NF}')
- echo "${ACCT_ID}"
+ PORT=$1
+ PAYMENT_ACCTS=$(${CLI_BASE} --port="$PORT" getpaymentaccts)
+ DUMMY_ACCT_1=$(echo -e "${PAYMENT_ACCTS}" | sed -n '2p')
+ DUMMY_ACCT_2=$(echo -e "${PAYMENT_ACCTS}" | sed -n '3p')
+ if [[ "$DUMMY_ACCT_1=" == *"PerfectMoney dummy"* ]]; then
+ DUMMY_ACCT=$DUMMY_ACCT_1
+ else
+ DUMMY_ACCT=$DUMMY_ACCT_2
+ fi
+ ACCT_ID=$(echo -e "$DUMMY_ACCT" | awk '{print $NF}')
+ echo "${ACCT_ID}"
}
createoffer() {
- CREATE_OFFER_CMD=$1
+ CREATE_OFFER_CMD=$1
OFFER_DESC=$($CREATE_OFFER_CMD)
-
- ### This is an effort to handling any createoffer error. If error, should echo Error, and the calling script
- ### should sleep awhile then exit 1.
- if [[ "$OFFER_DESC" != "Buy/Sell"* ]]; then
- echo "=========================================================="
+ if [[ "$OFFER_DESC" != "Buy/Sell"* ]]; then
+ echo "=========================================================="
echo "Error: ${OFFER_DESC}"
echo "=========================================================="
- sleep 5
+ else
+ OFFER_DETAIL=$(echo -e "${OFFER_DESC}" | sed -n '2p')
+ NEW_OFFER_ID=$(echo -e "${OFFER_DETAIL}" | awk '{print $NF}')
+ echo "${NEW_OFFER_ID}"
fi
-
- OFFER_DETAIL=$(echo -e "${OFFER_DESC}" | sed -n '2p')
- NEW_OFFER_ID=$(echo -e "${OFFER_DETAIL}" | awk '{print $NF}')
- echo "${NEW_OFFER_ID}"
}
getbtcoreaddress() {
@@ -249,8 +245,8 @@ genbtcblock() {
}
escapepluschar() {
- STRING=$1
- NEW_STRING=$(echo "${STRING//+/\\+}")
+ STRING=$1
+ NEW_STRING=$(echo "${STRING//+/\\+}")
echo "${NEW_STRING}"
}
From be2fb31bf3957899a193dd5674edb5148c095cb0 Mon Sep 17 00:00:00 2001
From: ghubstan <36207203+ghubstan@users.noreply.github.com>
Date: Mon, 18 Jan 2021 14:10:52 -0300
Subject: [PATCH 15/15] Fail fast if the CLI exits with a non-zero status code
---
apitest/scripts/simple-trading-script.sh | 9 ++++++
apitest/scripts/trading-script-env.sh | 37 +++++++++++++++++-------
2 files changed, 36 insertions(+), 10 deletions(-)
diff --git a/apitest/scripts/simple-trading-script.sh b/apitest/scripts/simple-trading-script.sh
index b631cce3ebb..5a87f10d70d 100755
--- a/apitest/scripts/simple-trading-script.sh
+++ b/apitest/scripts/simple-trading-script.sh
@@ -94,6 +94,7 @@ printdate_sameline "ALICE CLI:"
printcmd "$CMD"
OFFER_ID=$(createoffer "${CMD}")
+exitoncommandalert $?
printdate "ALICE ${ALICE_ROLE}: Created offer with id: ${OFFER_ID}."
printbreak
sleeptraced 10
@@ -120,6 +121,9 @@ CMD="$CLI_BASE --port=${BOB_PORT} takeoffer --offer-id=${OFFER_ID} --payment-acc
printdate_sameline "BOB CLI:"
printcmd "$CMD"
TRADE=$($CMD)
+# Will exit if takeoffer cmd fails.
+commandalert $? "Take offer command"
+
echo "${TRADE}"
printbreak
sleeptraced 10
@@ -154,6 +158,9 @@ CMD="$CLI_BASE --port=${PAYER_PORT} confirmpaymentstarted --trade-id=${OFFER_ID}
printdate_sameline "${PAYER_CLI}:"
printcmd "$CMD"
SENT_MSG=$($CMD)
+# Will exit if confirmpaymentstarted cmd fails.
+commandalert $? "The confirmpaymentstarted command"
+
printdate "${SENT_MSG}"
printbreak
@@ -168,6 +175,7 @@ CMD="$CLI_BASE --port=${PAYEE_PORT} confirmpaymentreceived --trade-id=${OFFER_ID
printdate_sameline "${PAYEE_CLI}:"
printcmd "$CMD"
RCVD_MSG=$($CMD)
+commandalert $? "The confirmpaymentreceived command"
printdate "${RCVD_MSG}"
printbreak
sleeptraced 4
@@ -193,6 +201,7 @@ else
printcmd "$CMD"
fi
KEEP_FUNDS_MSG=$($CMD)
+commandalert $? "The keepfunds command"
printdate "${KEEP_FUNDS_MSG}"
sleeptraced 5
printbreak
diff --git a/apitest/scripts/trading-script-env.sh b/apitest/scripts/trading-script-env.sh
index 6ef8733f505..b75ff0a91a0 100755
--- a/apitest/scripts/trading-script-env.sh
+++ b/apitest/scripts/trading-script-env.sh
@@ -6,6 +6,25 @@ export CLI_BASE="./bisq-cli --password=xyz"
export ALICE_PORT=9998
export BOB_PORT=9999
+commandalert() {
+ # Used in a script function when it needs to fail early with an error message, & pass the error code to the caller.
+ # usage: commandalert <$?>
+ if [ "$1" -ne 0 ]
+ then
+ printdate "Error: $2 did not complete successfully." >&2
+ exit $1
+ fi
+}
+
+exitoncommandalert() {
+ # Used in a parent script when you need it to fail immediately, with no error message.
+ # usage: exitoncommandalert <$?>
+ if [ "$1" -ne 0 ]
+ then
+ exit $1
+ fi
+}
+
printdate() {
echo "[$(date)] $@"
}
@@ -155,7 +174,6 @@ registerdisputeagents() {
echo "$SILENT" > /dev/null
}
-
printbreak() {
echo ""
echo ""
@@ -204,15 +222,14 @@ getdummyacctid() {
createoffer() {
CREATE_OFFER_CMD=$1
OFFER_DESC=$($CREATE_OFFER_CMD)
- if [[ "$OFFER_DESC" != "Buy/Sell"* ]]; then
- echo "=========================================================="
- echo "Error: ${OFFER_DESC}"
- echo "=========================================================="
- else
- OFFER_DETAIL=$(echo -e "${OFFER_DESC}" | sed -n '2p')
- NEW_OFFER_ID=$(echo -e "${OFFER_DETAIL}" | awk '{print $NF}')
- echo "${NEW_OFFER_ID}"
- fi
+
+ # If the CLI command exited with an error, print the CLI error, and
+ # return from this function now, passing the error status code to the caller.
+ commandalert $? "Create offer command"
+
+ OFFER_DETAIL=$(echo -e "${OFFER_DESC}" | sed -n '2p')
+ NEW_OFFER_ID=$(echo -e "${OFFER_DETAIL}" | awk '{print $NF}')
+ echo "${NEW_OFFER_ID}"
}
getbtcoreaddress() {