Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add rpc method 'getoffers' #4329

Merged
merged 36 commits into from
Jun 25, 2020
Merged
Show file tree
Hide file tree
Changes from 35 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b1146fd
Rename CoreWalletService -> CoreWalletsService
ghubstan Jun 12, 2020
ec66b14
Add rpc wallet(s) protection tests
ghubstan Jun 12, 2020
85c9676
Add rpc method 'getfundingaddresses'
ghubstan Jun 13, 2020
2e415de
Replace duplicate code in getFundingAddresses
ghubstan Jun 14, 2020
b1228e5
Add rpc method 'getaddressbalance'
ghubstan Jun 14, 2020
a7542e9
Add rpc method 'createpaymentacct'
ghubstan Jun 15, 2020
bac3ed5
Call core wallets service methods from CoreApi
ghubstan Jun 15, 2020
258d180
Factor duplicate unlocked wallet checks into new method
ghubstan Jun 16, 2020
c5134e1
Replace Tuple3 with memoization
dmos62 Jun 18, 2020
9db9ee2
Merge pull request #2 from dmos62/Z-getfundingaddresses-patch
ghubstan Jun 18, 2020
b0e278f
Refactor getFundingAddresses to use memoization
ghubstan Jun 18, 2020
1930411
Rmove blank line
ghubstan Jun 18, 2020
435672a
Add rpc method 'getpaymentaccts'
ghubstan Jun 19, 2020
331f488
Return protos from funding address methods
ghubstan Jun 19, 2020
612bafe
Refactor AddressBalanceInfo display logic
ghubstan Jun 20, 2020
37fb606
Add license comment
ghubstan Jun 20, 2020
855ac0f
Add license comment
ghubstan Jun 20, 2020
bfcc693
Add license comment
ghubstan Jun 20, 2020
d06807b
Wrap Exception from core in gRPC StatusRuntimeException
ghubstan Jun 20, 2020
7c073c6
Revert "Add license comment"
ghubstan Jun 20, 2020
d6ea0ea
Re-add license comment
ghubstan Jun 20, 2020
41f1add
Remove try catch block
ghubstan Jun 20, 2020
88cb90e
Add rpc method 'getoffers'
ghubstan Jun 20, 2020
1756258
Do not use protobuf.OfferPayload.Direction in client
ghubstan Jun 20, 2020
4778976
Fix comments
ghubstan Jun 20, 2020
b25abf1
Refactor CLI output table formatting
ghubstan Jun 21, 2020
a48af7c
Add 'getoffers' unit tests
ghubstan Jun 22, 2020
61285a7
Do not change case of input params in client
ghubstan Jun 22, 2020
0d9bdef
Add 'getoffers' smoke test
ghubstan Jun 22, 2020
8dcfa50
Define reusable headers from balance-info tbl
ghubstan Jun 22, 2020
52529a9
Move getpaymentaccts tbl formatting to TableFormat
ghubstan Jun 22, 2020
9691f35
Check param count only, not param order correctness
ghubstan Jun 23, 2020
e1fddfa
Remove duplication in wallets availability checks
ghubstan Jun 23, 2020
6979209
Check param count only, not param order correctness
ghubstan Jun 23, 2020
51d82b1
Fix offer list filter bug due to direction flip
ghubstan Jun 23, 2020
f820897
Format dates ISO 8601 in UTC
ghubstan Jun 24, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 95 additions & 24 deletions cli/src/main/java/bisq/cli/CliMain.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,47 +17,61 @@

package bisq.cli;

import bisq.proto.grpc.CreatePaymentAccountRequest;
import bisq.proto.grpc.GetAddressBalanceRequest;
import bisq.proto.grpc.GetBalanceRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetOffersRequest;
import bisq.proto.grpc.GetPaymentAccountsRequest;
import bisq.proto.grpc.GetVersionGrpc;
import bisq.proto.grpc.GetVersionRequest;
import bisq.proto.grpc.LockWalletRequest;
import bisq.proto.grpc.OffersGrpc;
import bisq.proto.grpc.PaymentAccountsGrpc;
import bisq.proto.grpc.RemoveWalletPasswordRequest;
import bisq.proto.grpc.SetWalletPasswordRequest;
import bisq.proto.grpc.UnlockWalletRequest;
import bisq.proto.grpc.WalletGrpc;
import bisq.proto.grpc.WalletsGrpc;

import io.grpc.ManagedChannelBuilder;
import io.grpc.StatusRuntimeException;

import joptsimple.OptionParser;
import joptsimple.OptionSet;

import java.text.DecimalFormat;

import java.io.IOException;
import java.io.PrintStream;

import java.math.BigDecimal;

import java.util.List;
import java.util.concurrent.TimeUnit;

import lombok.extern.slf4j.Slf4j;

import static bisq.cli.CurrencyFormat.formatSatoshis;
import static bisq.cli.TableFormat.formatAddressBalanceTbl;
import static bisq.cli.TableFormat.formatOfferTable;
import static bisq.cli.TableFormat.formatPaymentAcctTbl;
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.util.Collections.singletonList;

/**
* A command-line client for the Bisq gRPC API.
*/
@SuppressWarnings("ResultOfMethodCallIgnored")
@Slf4j
public class CliMain {

private enum Method {
getoffers,
createpaymentacct,
getpaymentaccts,
getversion,
getbalance,
getaddressbalance,
getfundingaddresses,
lockwallet,
unlockwallet,
removewalletpassword,
Expand Down Expand Up @@ -131,7 +145,9 @@ public static void run(String[] args) {
}));

var versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials);
var walletService = WalletGrpc.newBlockingStub(channel).withCallCredentials(credentials);
var offersService = OffersGrpc.newBlockingStub(channel).withCallCredentials(credentials);
var paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials);
var walletsService = WalletsGrpc.newBlockingStub(channel).withCallCredentials(credentials);

try {
switch (method) {
Expand All @@ -143,18 +159,68 @@ public static void run(String[] args) {
}
case getbalance: {
var request = GetBalanceRequest.newBuilder().build();
var reply = walletService.getBalance(request);
var satoshiBalance = reply.getBalance();
var satoshiDivisor = new BigDecimal(100000000);
var btcFormat = new DecimalFormat("###,##0.00000000");
@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
var btcBalance = btcFormat.format(BigDecimal.valueOf(satoshiBalance).divide(satoshiDivisor));
var reply = walletsService.getBalance(request);
var btcBalance = formatSatoshis(reply.getBalance());
out.println(btcBalance);
return;
}
case getaddressbalance: {
if (nonOptionArgs.size() < 2)
throw new IllegalArgumentException("no address specified");

var request = GetAddressBalanceRequest.newBuilder()
.setAddress(nonOptionArgs.get(1)).build();
var reply = walletsService.getAddressBalance(request);
out.println(formatAddressBalanceTbl(singletonList(reply.getAddressBalanceInfo())));
return;
}
case getfundingaddresses: {
var request = GetFundingAddressesRequest.newBuilder().build();
var reply = walletsService.getFundingAddresses(request);
out.println(formatAddressBalanceTbl(reply.getAddressBalanceInfoList()));
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 fiatCurrency = nonOptionArgs.get(2);

var request = GetOffersRequest.newBuilder()
.setDirection(direction)
.setFiatCurrencyCode(fiatCurrency)
.build();
var reply = offersService.getOffers(request);
out.println(formatOfferTable(reply.getOffersList(), fiatCurrency));
return;
}
case createpaymentacct: {
if (nonOptionArgs.size() < 4)
throw new IllegalArgumentException(
"incorrect parameter count, expecting account name, account number, currency code");

var accountName = nonOptionArgs.get(1);
var accountNumber = nonOptionArgs.get(2);
var fiatCurrencyCode = nonOptionArgs.get(3);

var request = CreatePaymentAccountRequest.newBuilder()
.setAccountName(accountName)
.setAccountNumber(accountNumber)
.setFiatCurrencyCode(fiatCurrencyCode).build();
paymentAccountsService.createPaymentAccount(request);
out.println(format("payment account %s saved", accountName));
return;
}
case getpaymentaccts: {
var request = GetPaymentAccountsRequest.newBuilder().build();
var reply = paymentAccountsService.getPaymentAccounts(request);
out.println(formatPaymentAcctTbl(reply.getPaymentAccountsList()));
return;
}
case lockwallet: {
var request = LockWalletRequest.newBuilder().build();
walletService.lockWallet(request);
walletsService.lockWallet(request);
out.println("wallet locked");
return;
}
Expand All @@ -174,7 +240,7 @@ public static void run(String[] args) {
var request = UnlockWalletRequest.newBuilder()
.setPassword(nonOptionArgs.get(1))
.setTimeout(timeout).build();
walletService.unlockWallet(request);
walletsService.unlockWallet(request);
out.println("wallet unlocked");
return;
}
Expand All @@ -183,7 +249,7 @@ public static void run(String[] args) {
throw new IllegalArgumentException("no password specified");

var request = RemoveWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)).build();
walletService.removeWalletPassword(request);
walletsService.removeWalletPassword(request);
out.println("wallet decrypted");
return;
}
Expand All @@ -195,13 +261,13 @@ public static void run(String[] args) {
var hasNewPassword = nonOptionArgs.size() == 3;
if (hasNewPassword)
requestBuilder.setNewPassword(nonOptionArgs.get(2));
walletService.setWalletPassword(requestBuilder.build());
walletsService.setWalletPassword(requestBuilder.build());
out.println("wallet encrypted" + (hasNewPassword ? " with new password" : ""));
return;
}
default: {
throw new RuntimeException(format("unhandled method '%s'", method));
}
}
}
} catch (StatusRuntimeException ex) {
// Remove the leading gRPC status code (e.g. "UNKNOWN: ") from the message
Expand All @@ -218,14 +284,19 @@ private static void printHelp(OptionParser parser, PrintStream stream) {
stream.println();
parser.printHelpOn(stream);
stream.println();
stream.format("%-19s%-30s%s%n", "Method", "Params", "Description");
stream.format("%-19s%-30s%s%n", "------", "------", "------------");
stream.format("%-19s%-30s%s%n", "getversion", "", "Get server version");
stream.format("%-19s%-30s%s%n", "getbalance", "", "Get server wallet balance");
stream.format("%-19s%-30s%s%n", "lockwallet", "", "Remove wallet password from memory, locking the wallet");
stream.format("%-19s%-30s%s%n", "unlockwallet", "password timeout",
stream.format("%-22s%-50s%s%n", "Method", "Params", "Description");
stream.format("%-22s%-50s%s%n", "------", "------", "------------");
stream.format("%-22s%-50s%s%n", "getversion", "", "Get server version");
stream.format("%-22s%-50s%s%n", "getbalance", "", "Get server wallet balance");
stream.format("%-22s%-50s%s%n", "getaddressbalance", "address", "Get server wallet address balance");
stream.format("%-22s%-50s%s%n", "getfundingaddresses", "", "Get BTC funding addresses");
stream.format("%-22s%-50s%s%n", "getoffers", "buy | sell, fiat currency code", "Get current offers");
stream.format("%-22s%-50s%s%n", "createpaymentacct", "account name, account number, currency code", "Create PerfectMoney dummy account");
stream.format("%-22s%-50s%s%n", "getpaymentaccts", "", "Get user payment accounts");
stream.format("%-22s%-50s%s%n", "lockwallet", "", "Remove wallet password from memory, locking the wallet");
stream.format("%-22s%-50s%s%n", "unlockwallet", "password timeout",
"Store wallet password in memory for timeout seconds");
stream.format("%-19s%-30s%s%n", "setwalletpassword", "password [newpassword]",
stream.format("%-22s%-50s%s%n", "setwalletpassword", "password [newpassword]",
"Encrypt wallet with password, or set new password on encrypted wallet");
stream.println();
} catch (IOException ex) {
Expand Down
47 changes: 47 additions & 0 deletions cli/src/main/java/bisq/cli/CurrencyFormat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package bisq.cli;

import java.text.DecimalFormat;
import java.text.NumberFormat;

import java.math.BigDecimal;
import java.math.RoundingMode;

import java.util.Locale;

class CurrencyFormat {

private static final NumberFormat NUMBER_FORMAT = NumberFormat.getInstance(Locale.US);

static final BigDecimal SATOSHI_DIVISOR = new BigDecimal(100000000);
static final DecimalFormat BTC_FORMAT = new DecimalFormat("###,##0.00000000");

@SuppressWarnings("BigDecimalMethodWithoutRoundingCalled")
static final String formatSatoshis(long sats) {
return BTC_FORMAT.format(BigDecimal.valueOf(sats).divide(SATOSHI_DIVISOR));
}

static String formatAmountRange(long minAmount, long amount) {
return minAmount != amount
? formatSatoshis(minAmount) + " - " + formatSatoshis(amount)
: formatSatoshis(amount);
}

static String formatVolumeRange(long minVolume, long volume) {
return minVolume != volume
? formatOfferVolume(minVolume) + " - " + formatOfferVolume(volume)
: formatOfferVolume(volume);
}

static String formatOfferPrice(long price) {
NUMBER_FORMAT.setMaximumFractionDigits(4);
NUMBER_FORMAT.setMinimumFractionDigits(4);
NUMBER_FORMAT.setRoundingMode(RoundingMode.UNNECESSARY);
return NUMBER_FORMAT.format((double) price / 10000);
}

static String formatOfferVolume(long volume) {
NUMBER_FORMAT.setMaximumFractionDigits(0);
NUMBER_FORMAT.setRoundingMode(RoundingMode.UNNECESSARY);
return NUMBER_FORMAT.format((double) volume / 10000);
}
}
17 changes: 17 additions & 0 deletions cli/src/main/java/bisq/cli/PasswordCallCredentials.java
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

package bisq.cli;

import io.grpc.CallCredentials;
Expand Down
Loading