diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index e5463e11cfb..c7c16a4376f 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -17,10 +17,14 @@ 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.GetVersionGrpc; import bisq.proto.grpc.GetVersionRequest; import bisq.proto.grpc.LockWalletRequest; +import bisq.proto.grpc.PaymentAccountsGrpc; import bisq.proto.grpc.RemoveWalletPasswordRequest; import bisq.proto.grpc.SetWalletPasswordRequest; import bisq.proto.grpc.UnlockWalletRequest; @@ -56,8 +60,11 @@ public class CliMain { private enum Method { + createpaymentacct, getversion, getbalance, + getaddressbalance, + getfundingaddresses, lockwallet, unlockwallet, removewalletpassword, @@ -131,6 +138,7 @@ public static void run(String[] args) { })); var versionService = GetVersionGrpc.newBlockingStub(channel).withCallCredentials(credentials); + var paymentAccountsService = PaymentAccountsGrpc.newBlockingStub(channel).withCallCredentials(credentials); var walletService = WalletGrpc.newBlockingStub(channel).withCallCredentials(credentials); try { @@ -152,6 +160,46 @@ public static void run(String[] args) { 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 = walletService.getAddressBalance(request); + out.println(reply.getAddressBalanceInfo()); + return; + } + case getfundingaddresses: { + var request = GetFundingAddressesRequest.newBuilder().build(); + var reply = walletService.getFundingAddresses(request); + out.println(reply.getFundingAddressesInfo()); + return; + } + case createpaymentacct: { + if (nonOptionArgs.size() < 2) + throw new IllegalArgumentException("no account name specified"); + + var accountName = nonOptionArgs.get(1); + + if (nonOptionArgs.size() < 3) + throw new IllegalArgumentException("no account number specified"); + + var accountNumber = nonOptionArgs.get(2); + + if (nonOptionArgs.size() < 4) + throw new IllegalArgumentException("no fiat currency specified"); + + var fiatCurrencyCode = nonOptionArgs.get(3).toUpperCase(); + + var request = CreatePaymentAccountRequest.newBuilder() + .setAccountName(accountName) + .setAccountNumber(accountNumber) + .setFiatCurrencyCode(fiatCurrencyCode).build(); + paymentAccountsService.createPaymentAccount(request); + out.println(format("payment account %s saved", accountName)); + return; + } case lockwallet: { var request = LockWalletRequest.newBuilder().build(); walletService.lockWallet(request); @@ -201,7 +249,7 @@ public static void run(String[] args) { } default: { throw new RuntimeException(format("unhandled method '%s'", method)); - } + } } } catch (StatusRuntimeException ex) { // Remove the leading gRPC status code (e.g. "UNKNOWN: ") from the message @@ -218,14 +266,17 @@ 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", "createpaymentacct", "account name, account number, currency code", "Create PerfectMoney dummy account"); + 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) { diff --git a/cli/test.sh b/cli/test.sh index 94aae7d25b6..79754d188bb 100755 --- a/cli/test.sh +++ b/cli/test.sh @@ -48,28 +48,134 @@ run ./bisq-cli --password="xyz" getversion [ "$status" -eq 0 ] echo "actual output: $output" >&2 - [ "$output" = "1.3.2" ] + [ "$output" = "1.3.4" ] } @test "test getversion" { run ./bisq-cli --password=xyz getversion [ "$status" -eq 0 ] echo "actual output: $output" >&2 - [ "$output" = "1.3.2" ] + [ "$output" = "1.3.4" ] } -@test "test getbalance (available & unlocked wallet with 0 btc balance)" { +@test "test setwalletpassword \"a b c\"" { + run ./bisq-cli --password=xyz setwalletpassword "a b c" + [ "$status" -eq 0 ] + echo "actual output: $output" >&2 + [ "$output" = "wallet encrypted" ] + sleep 1 +} + +@test "test unlockwallet without password & timeout args" { + run ./bisq-cli --password=xyz unlockwallet + [ "$status" -eq 1 ] + echo "actual output: $output" >&2 + [ "$output" = "Error: no password specified" ] +} + +@test "test unlockwallet without timeout arg" { + run ./bisq-cli --password=xyz unlockwallet "a b c" + [ "$status" -eq 1 ] + echo "actual output: $output" >&2 + [ "$output" = "Error: no unlock timeout specified" ] +} + + +@test "test unlockwallet \"a b c\" 8" { + run ./bisq-cli --password=xyz unlockwallet "a b c" 8 + [ "$status" -eq 0 ] + echo "actual output: $output" >&2 + [ "$output" = "wallet unlocked" ] +} + +@test "test getbalance while wallet unlocked for 8s" { + run ./bisq-cli --password=xyz getbalance + [ "$status" -eq 0 ] + echo "actual output: $output" >&2 + [ "$output" = "0.00000000" ] + sleep 8 +} + +@test "test unlockwallet \"a b c\" 6" { + run ./bisq-cli --password=xyz unlockwallet "a b c" 6 + [ "$status" -eq 0 ] + echo "actual output: $output" >&2 + [ "$output" = "wallet unlocked" ] +} + +@test "test lockwallet before unlockwallet timeout=6s expires" { + run ./bisq-cli --password=xyz lockwallet + [ "$status" -eq 0 ] + echo "actual output: $output" >&2 + [ "$output" = "wallet locked" ] +} + +@test "test setwalletpassword incorrect old pwd error" { + run ./bisq-cli --password=xyz setwalletpassword "z z z" "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" + [ "$status" -eq 0 ] + echo "actual output: $output" >&2 + [ "$output" = "wallet encrypted with new password" ] + sleep 1 +} + +@test "test getbalance wallet locked error" { + run ./bisq-cli --password=xyz getbalance + [ "$status" -eq 1 ] + echo "actual output: $output" >&2 + [ "$output" = "Error: wallet is locked" ] +} + +@test "test removewalletpassword" { + run ./bisq-cli --password=xyz removewalletpassword "d e f" + [ "$status" -eq 0 ] + echo "actual output: $output" >&2 + [ "$output" = "wallet decrypted" ] + sleep 1 +} + +@test "test getbalance when wallet available & unlocked with 0 btc balance" { run ./bisq-cli --password=xyz getbalance [ "$status" -eq 0 ] echo "actual output: $output" >&2 [ "$output" = "0.00000000" ] } +@test "test getfundingaddresses" { + run ./bisq-cli --password=xyz getfundingaddresses + [ "$status" -eq 0 ] +} + +@test "test getaddressbalance missing address argument" { + run ./bisq-cli --password=xyz getaddressbalance + [ "$status" -eq 1 ] + echo "actual output: $output" >&2 + [ "$output" = "Error: no address specified" ] +} + +@test "test getaddressbalance bogus address argument" { + run ./bisq-cli --password=xyz getaddressbalance bogus + [ "$status" -eq 1 ] + echo "actual output: $output" >&2 + [ "$output" = "Error: address bogus not found in wallet" ] +} + +@test "test createpaymentacct PerfectMoneyDummy 0123456789 USD" { + run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy 0123456789 USD + [ "$status" -eq 0 ] +} + @test "test help displayed on stderr if no options or arguments" { run ./bisq-cli [ "$status" -eq 1 ] [ "${lines[0]}" = "Bisq RPC Client" ] - [ "${lines[1]}" = "Usage: bisq-cli [options] " ] + [ "${lines[1]}" = "Usage: bisq-cli [options] [params]" ] # TODO add asserts after help text is modified for new endpoints } @@ -77,6 +183,6 @@ run ./bisq-cli --help [ "$status" -eq 0 ] [ "${lines[0]}" = "Bisq RPC Client" ] - [ "${lines[1]}" = "Usage: bisq-cli [options] " ] + [ "${lines[1]}" = "Usage: bisq-cli [options] [params]" ] # TODO add asserts after help text is modified for new endpoints } diff --git a/core/src/main/java/bisq/core/grpc/CoreApi.java b/core/src/main/java/bisq/core/grpc/CoreApi.java index a0671f4d3b0..8d45f31d5d3 100644 --- a/core/src/main/java/bisq/core/grpc/CoreApi.java +++ b/core/src/main/java/bisq/core/grpc/CoreApi.java @@ -47,6 +47,7 @@ */ @Slf4j public class CoreApi { + private final CorePaymentAccountsService paymentAccountsService; private final OfferBookService offerBookService; private final TradeStatisticsManager tradeStatisticsManager; private final CreateOfferService createOfferService; @@ -54,11 +55,13 @@ public class CoreApi { private final User user; @Inject - public CoreApi(OfferBookService offerBookService, + public CoreApi(CorePaymentAccountsService paymentAccountsService, + OfferBookService offerBookService, TradeStatisticsManager tradeStatisticsManager, CreateOfferService createOfferService, OpenOfferManager openOfferManager, User user) { + this.paymentAccountsService = paymentAccountsService; this.offerBookService = offerBookService; this.tradeStatisticsManager = tradeStatisticsManager; this.createOfferService = createOfferService; @@ -78,8 +81,12 @@ public List getOffers() { return offerBookService.getOffers(); } + public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) { + paymentAccountsService.createPaymentAccount(accountName, accountNumber, fiatCurrencyCode); + } + public Set getPaymentAccounts() { - return user.getPaymentAccounts(); + return paymentAccountsService.getPaymentAccounts(); } public void placeOffer(String currencyCode, diff --git a/core/src/main/java/bisq/core/grpc/CorePaymentAccountsService.java b/core/src/main/java/bisq/core/grpc/CorePaymentAccountsService.java new file mode 100644 index 00000000000..db2d3be4a03 --- /dev/null +++ b/core/src/main/java/bisq/core/grpc/CorePaymentAccountsService.java @@ -0,0 +1,57 @@ +package bisq.core.grpc; + +import bisq.core.account.witness.AccountAgeWitnessService; +import bisq.core.locale.FiatCurrency; +import bisq.core.payment.PaymentAccount; +import bisq.core.payment.PaymentAccountFactory; +import bisq.core.payment.PerfectMoneyAccount; +import bisq.core.payment.payload.PaymentMethod; +import bisq.core.user.User; + +import bisq.common.config.Config; + +import javax.inject.Inject; + +import java.util.Set; + +import lombok.extern.slf4j.Slf4j; + +@Slf4j +public class CorePaymentAccountsService { + + private final Config config; + private final AccountAgeWitnessService accountAgeWitnessService; + private final User user; + + @Inject + public CorePaymentAccountsService(Config config, + AccountAgeWitnessService accountAgeWitnessService, + User user) { + this.config = config; + this.accountAgeWitnessService = accountAgeWitnessService; + this.user = user; + } + + public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) { + // Create and persist a PerfectMoney dummy payment account. There is no guard + // against creating accounts with duplicate names & numbers, only the uuid and + // creation date are unique. + PaymentMethod dummyPaymentMethod = PaymentMethod.getDummyPaymentMethod(PaymentMethod.PERFECT_MONEY_ID); + PaymentAccount paymentAccount = PaymentAccountFactory.getPaymentAccount(dummyPaymentMethod); + paymentAccount.init(); + paymentAccount.setAccountName(accountName); + ((PerfectMoneyAccount) paymentAccount).setAccountNr(accountNumber); + paymentAccount.setSingleTradeCurrency(new FiatCurrency(fiatCurrencyCode)); + user.addPaymentAccount(paymentAccount); + + // Don't do this on mainnet until thoroughly tested. + if (config.baseCurrencyNetwork.isRegtest()) + accountAgeWitnessService.publishMyAccountAgeWitness(paymentAccount.getPaymentAccountPayload()); + + log.info("Payment account {} saved", paymentAccount.getId()); + } + + public Set getPaymentAccounts() { + return user.getPaymentAccounts(); + } +} diff --git a/core/src/main/java/bisq/core/grpc/CoreWalletService.java b/core/src/main/java/bisq/core/grpc/CoreWalletsService.java similarity index 53% rename from core/src/main/java/bisq/core/grpc/CoreWalletService.java rename to core/src/main/java/bisq/core/grpc/CoreWalletsService.java index ff9383c55d4..f9e3b7d1c60 100644 --- a/core/src/main/java/bisq/core/grpc/CoreWalletService.java +++ b/core/src/main/java/bisq/core/grpc/CoreWalletsService.java @@ -1,28 +1,44 @@ package bisq.core.grpc; import bisq.core.btc.Balances; +import bisq.core.btc.model.AddressEntry; +import bisq.core.btc.wallet.BtcWalletService; import bisq.core.btc.wallet.WalletsManager; +import bisq.common.util.Tuple3; + +import org.bitcoinj.core.Address; +import org.bitcoinj.core.TransactionConfidence; import org.bitcoinj.crypto.KeyCrypterScrypt; import javax.inject.Inject; import org.spongycastle.crypto.params.KeyParameter; +import java.text.DecimalFormat; + +import java.math.BigDecimal; + +import java.util.List; +import java.util.Optional; import java.util.Timer; import java.util.TimerTask; +import java.util.function.Function; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; +import static java.lang.String.format; import static java.util.concurrent.TimeUnit.SECONDS; @Slf4j -class CoreWalletService { +class CoreWalletsService { private final Balances balances; private final WalletsManager walletsManager; + private final BtcWalletService btcWalletService; @Nullable private TimerTask lockTask; @@ -30,10 +46,19 @@ class CoreWalletService { @Nullable private KeyParameter tempAesKey; + private final BigDecimal satoshiDivisor = new BigDecimal(100000000); + private final DecimalFormat btcFormat = new DecimalFormat("###,##0.00000000"); + @SuppressWarnings("BigDecimalMethodWithoutRoundingCalled") + private final Function formatSatoshis = (sats) -> + btcFormat.format(BigDecimal.valueOf(sats).divide(satoshiDivisor)); + @Inject - public CoreWalletService(Balances balances, WalletsManager walletsManager) { + public CoreWalletsService(Balances balances, + WalletsManager walletsManager, + BtcWalletService btcWalletService) { this.balances = balances; this.walletsManager = walletsManager; + this.btcWalletService = btcWalletService; } public long getAvailableBalance() { @@ -50,6 +75,77 @@ public long getAvailableBalance() { return balance.getValue(); } + public long getAddressBalance(String addressString) { + Address address = getAddressEntry(addressString).getAddress(); + return btcWalletService.getBalanceForAddress(address).value; + } + + public String getAddressBalanceInfo(String addressString) { + var satoshiBalance = getAddressBalance(addressString); + var btcBalance = formatSatoshis.apply(satoshiBalance); + var numConfirmations = getNumConfirmationsForMostRecentTransaction(addressString); + return addressString + + " balance: " + format("%13s", btcBalance) + + ((numConfirmations > 0) ? (" confirmations: " + format("%6d", numConfirmations)) : ""); + } + + public String getFundingAddresses() { + if (!walletsManager.areWalletsAvailable()) + throw new IllegalStateException("wallet is not yet available"); + + if (walletsManager.areWalletsEncrypted() && tempAesKey == null) + throw new IllegalStateException("wallet is locked"); + + // Create a new funding address if none exists. + if (btcWalletService.getAvailableAddressEntries().size() == 0) + btcWalletService.getFreshAddressEntry(); + + // Populate a list of Tuple3 + List> addrBalanceConfirms = + btcWalletService.getAvailableAddressEntries().stream() + .map(a -> new Tuple3<>(a.getAddressString(), + getAddressBalance(a.getAddressString()), + getNumConfirmationsForMostRecentTransaction(a.getAddressString()))) + .collect(Collectors.toList()); + + // Check to see if at least one of the existing addresses has a zero balance. + boolean hasZeroBalance = false; + for (Tuple3 abc : addrBalanceConfirms) { + if (abc.second == 0) { + hasZeroBalance = true; + break; + } + } + if (!hasZeroBalance) { + // None of the existing addresses have a zero balance, create a new address. + addrBalanceConfirms.add( + new Tuple3<>(btcWalletService.getFreshAddressEntry().getAddressString(), + 0L, + 0)); + } + + // Iterate the list of Tuple3 objects + // and build the formatted info string. + StringBuilder addressInfoBuilder = new StringBuilder(); + addrBalanceConfirms.forEach(a -> { + var btcBalance = formatSatoshis.apply(a.second); + var numConfirmations = getNumConfirmationsForMostRecentTransaction(a.first); + String addressInfo = "" + a.first + + " balance: " + format("%13s", btcBalance) + + ((a.second > 0) ? (" confirmations: " + format("%6d", numConfirmations)) : "") + + "\n"; + addressInfoBuilder.append(addressInfo); + }); + + return addressInfoBuilder.toString().trim(); + } + + public int getNumConfirmationsForMostRecentTransaction(String addressString) { + Address address = getAddressEntry(addressString).getAddress(); + TransactionConfidence confidence = btcWalletService.getConfidenceForAddress(address); + return confidence == null ? 0 : confidence.getDepthInBlocks(); + } + public void setWalletPassword(String password, String newPassword) { if (!walletsManager.areWalletsAvailable()) throw new IllegalStateException("wallet is not yet available"); @@ -156,4 +252,16 @@ private KeyCrypterScrypt getKeyCrypterScrypt() { throw new IllegalStateException("wallet encrypter is not available"); return keyCrypterScrypt; } + + private AddressEntry getAddressEntry(String addressString) { + Optional addressEntry = + btcWalletService.getAddressEntryListAsImmutableList().stream() + .filter(e -> addressString.equals(e.getAddressString())) + .findFirst(); + + if (!addressEntry.isPresent()) + throw new IllegalStateException(format("address %s not found in wallet", addressString)); + + return addressEntry.get(); + } } diff --git a/core/src/main/java/bisq/core/grpc/GrpcPaymentAccountsService.java b/core/src/main/java/bisq/core/grpc/GrpcPaymentAccountsService.java new file mode 100644 index 00000000000..f2a9abf0bbb --- /dev/null +++ b/core/src/main/java/bisq/core/grpc/GrpcPaymentAccountsService.java @@ -0,0 +1,46 @@ +package bisq.core.grpc; + +import bisq.core.payment.PaymentAccount; + +import bisq.proto.grpc.CreatePaymentAccountReply; +import bisq.proto.grpc.CreatePaymentAccountRequest; +import bisq.proto.grpc.GetPaymentAccountsReply; +import bisq.proto.grpc.GetPaymentAccountsRequest; +import bisq.proto.grpc.PaymentAccountsGrpc; + +import io.grpc.stub.StreamObserver; + +import javax.inject.Inject; + +import java.util.stream.Collectors; + + +public class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImplBase { + + private final CoreApi coreApi; + + @Inject + public GrpcPaymentAccountsService(CoreApi coreApi) { + this.coreApi = coreApi; + } + + @Override + public void createPaymentAccount(CreatePaymentAccountRequest req, + StreamObserver responseObserver) { + coreApi.createPaymentAccount(req.getAccountName(), req.getAccountNumber(), req.getFiatCurrencyCode()); + var reply = CreatePaymentAccountReply.newBuilder().build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } + + @Override + public void getPaymentAccounts(GetPaymentAccountsRequest req, + StreamObserver responseObserver) { + var tradeStatistics = coreApi.getPaymentAccounts().stream() + .map(PaymentAccount::toProtoMessage) + .collect(Collectors.toList()); + var reply = GetPaymentAccountsReply.newBuilder().addAllPaymentAccounts(tradeStatistics).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } +} diff --git a/core/src/main/java/bisq/core/grpc/GrpcServer.java b/core/src/main/java/bisq/core/grpc/GrpcServer.java index 2b3543572b1..2c4f766b3c6 100644 --- a/core/src/main/java/bisq/core/grpc/GrpcServer.java +++ b/core/src/main/java/bisq/core/grpc/GrpcServer.java @@ -18,7 +18,6 @@ package bisq.core.grpc; import bisq.core.offer.Offer; -import bisq.core.payment.PaymentAccount; import bisq.core.trade.handlers.TransactionResultHandler; import bisq.core.trade.statistics.TradeStatistics2; @@ -27,9 +26,6 @@ import bisq.proto.grpc.GetOffersGrpc; import bisq.proto.grpc.GetOffersReply; import bisq.proto.grpc.GetOffersRequest; -import bisq.proto.grpc.GetPaymentAccountsGrpc; -import bisq.proto.grpc.GetPaymentAccountsReply; -import bisq.proto.grpc.GetPaymentAccountsRequest; import bisq.proto.grpc.GetTradeStatisticsGrpc; import bisq.proto.grpc.GetTradeStatisticsReply; import bisq.proto.grpc.GetTradeStatisticsRequest; @@ -60,14 +56,17 @@ public class GrpcServer { private final Server server; @Inject - public GrpcServer(Config config, CoreApi coreApi, GrpcWalletService walletService) { + public GrpcServer(Config config, + CoreApi coreApi, + GrpcPaymentAccountsService paymentAccountsService, + GrpcWalletService walletService) { this.coreApi = coreApi; this.server = ServerBuilder.forPort(config.apiPort) .addService(new GetVersionService()) .addService(new GetTradeStatisticsService()) .addService(new GetOffersService()) - .addService(new GetPaymentAccountsService()) .addService(new PlaceOfferService()) + .addService(paymentAccountsService) .addService(walletService) .intercept(new PasswordAuthInterceptor(config.apiPassword)) .build(); @@ -125,21 +124,6 @@ public void getOffers(GetOffersRequest req, StreamObserver respo } } - class GetPaymentAccountsService extends GetPaymentAccountsGrpc.GetPaymentAccountsImplBase { - @Override - public void getPaymentAccounts(GetPaymentAccountsRequest req, - StreamObserver responseObserver) { - - var tradeStatistics = coreApi.getPaymentAccounts().stream() - .map(PaymentAccount::toProtoMessage) - .collect(Collectors.toList()); - - var reply = GetPaymentAccountsReply.newBuilder().addAllPaymentAccounts(tradeStatistics).build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } - } - class PlaceOfferService extends PlaceOfferGrpc.PlaceOfferImplBase { @Override public void placeOffer(PlaceOfferRequest req, StreamObserver responseObserver) { diff --git a/core/src/main/java/bisq/core/grpc/GrpcWalletService.java b/core/src/main/java/bisq/core/grpc/GrpcWalletService.java index 92d4cc8b81f..0343960f394 100644 --- a/core/src/main/java/bisq/core/grpc/GrpcWalletService.java +++ b/core/src/main/java/bisq/core/grpc/GrpcWalletService.java @@ -1,7 +1,11 @@ package bisq.core.grpc; +import bisq.proto.grpc.GetAddressBalanceReply; +import bisq.proto.grpc.GetAddressBalanceRequest; import bisq.proto.grpc.GetBalanceReply; import bisq.proto.grpc.GetBalanceRequest; +import bisq.proto.grpc.GetFundingAddressesReply; +import bisq.proto.grpc.GetFundingAddressesRequest; import bisq.proto.grpc.LockWalletReply; import bisq.proto.grpc.LockWalletRequest; import bisq.proto.grpc.RemoveWalletPasswordReply; @@ -20,17 +24,17 @@ class GrpcWalletService extends WalletGrpc.WalletImplBase { - private final CoreWalletService walletService; + private final CoreWalletsService walletsService; @Inject - public GrpcWalletService(CoreWalletService walletService) { - this.walletService = walletService; + public GrpcWalletService(CoreWalletsService walletsService) { + this.walletsService = walletsService; } @Override public void getBalance(GetBalanceRequest req, StreamObserver responseObserver) { try { - long result = walletService.getAvailableBalance(); + long result = walletsService.getAvailableBalance(); var reply = GetBalanceReply.newBuilder().setBalance(result).build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -41,11 +45,41 @@ public void getBalance(GetBalanceRequest req, StreamObserver re } } + @Override + public void getAddressBalance(GetAddressBalanceRequest req, + StreamObserver responseObserver) { + try { + String result = walletsService.getAddressBalanceInfo(req.getAddress()); + var reply = GetAddressBalanceReply.newBuilder().setAddressBalanceInfo(result).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (IllegalStateException cause) { + var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage())); + responseObserver.onError(ex); + throw ex; + } + } + + @Override + public void getFundingAddresses(GetFundingAddressesRequest req, + StreamObserver responseObserver) { + try { + String result = walletsService.getFundingAddresses(); + var reply = GetFundingAddressesReply.newBuilder().setFundingAddressesInfo(result).build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (IllegalStateException cause) { + var ex = new StatusRuntimeException(Status.UNKNOWN.withDescription(cause.getMessage())); + responseObserver.onError(ex); + throw ex; + } + } + @Override public void setWalletPassword(SetWalletPasswordRequest req, StreamObserver responseObserver) { try { - walletService.setWalletPassword(req.getPassword(), req.getNewPassword()); + walletsService.setWalletPassword(req.getPassword(), req.getNewPassword()); var reply = SetWalletPasswordReply.newBuilder().build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -60,7 +94,7 @@ public void setWalletPassword(SetWalletPasswordRequest req, public void removeWalletPassword(RemoveWalletPasswordRequest req, StreamObserver responseObserver) { try { - walletService.removeWalletPassword(req.getPassword()); + walletsService.removeWalletPassword(req.getPassword()); var reply = RemoveWalletPasswordReply.newBuilder().build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -75,7 +109,7 @@ public void removeWalletPassword(RemoveWalletPasswordRequest req, public void lockWallet(LockWalletRequest req, StreamObserver responseObserver) { try { - walletService.lockWallet(); + walletsService.lockWallet(); var reply = LockWalletReply.newBuilder().build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -90,7 +124,7 @@ public void lockWallet(LockWalletRequest req, public void unlockWallet(UnlockWalletRequest req, StreamObserver responseObserver) { try { - walletService.unlockWallet(req.getPassword(), req.getTimeout()); + walletsService.unlockWallet(req.getPassword(), req.getTimeout()); var reply = UnlockWalletReply.newBuilder().build(); responseObserver.onNext(reply); responseObserver.onCompleted(); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index b8db4c6d24b..66c4eb2ada8 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -72,14 +72,25 @@ message GetOffersReply { } /////////////////////////////////////////////////////////////////////////////////////////// -// PaymentAccount +// PaymentAccounts /////////////////////////////////////////////////////////////////////////////////////////// -service GetPaymentAccounts { +service PaymentAccounts { + rpc CreatePaymentAccount (CreatePaymentAccountRequest) returns (CreatePaymentAccountReply) { + } rpc GetPaymentAccounts (GetPaymentAccountsRequest) returns (GetPaymentAccountsReply) { } } +message CreatePaymentAccountRequest { + string accountName = 1; + string accountNumber = 2; + string fiatCurrencyCode = 3; +} + +message CreatePaymentAccountReply { +} + message GetPaymentAccountsRequest { } @@ -119,6 +130,10 @@ message PlaceOfferReply { service Wallet { rpc GetBalance (GetBalanceRequest) returns (GetBalanceReply) { } + rpc GetAddressBalance (GetAddressBalanceRequest) returns (GetAddressBalanceReply) { + } + rpc GetFundingAddresses (GetFundingAddressesRequest) returns (GetFundingAddressesReply) { + } rpc SetWalletPassword (SetWalletPasswordRequest) returns (SetWalletPasswordReply) { } rpc RemoveWalletPassword (RemoveWalletPasswordRequest) returns (RemoveWalletPasswordReply) { @@ -136,6 +151,21 @@ message GetBalanceReply { uint64 balance = 1; } +message GetAddressBalanceRequest { + string address = 1; +} + +message GetAddressBalanceReply { + string addressBalanceInfo = 1; +} + +message GetFundingAddressesRequest { +} + +message GetFundingAddressesReply { + string fundingAddressesInfo = 1; +} + message SetWalletPasswordRequest { string password = 1; string newPassword = 2;