From a7542e98bf62d86bc3486e1d4d925a9a0a64d8c6 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Mon, 15 Jun 2020 14:11:51 -0300 Subject: [PATCH] Add rpc method 'createpaymentacct' This addresses task 4 in issue 4257. https://github.com/bisq-network/bisq/issues/4257 This PR should be reviewed/merged after PR 4304. https://github.com/bisq-network/bisq/pull/4304 This new gRPC PaymentAccounts service method creates a dummy PerfectMoney payment account for the given name, number and fiat currency code, as part of the required "simplest possible trading API" (for demo). An implementation supporting all payment methods is not in the scope. Changes specific to the new rpc method implementation follow: * New createpaymentacct method + help text was added to CliMain. Help text formatting was also changed to make room for larger method names and argument lists. * The PaymentAccount proto service def was renamed PaymentAccounts to avoid a name collision, and the new rpc CreatePaymentAccount was made part of the newly named PaymentAccounts service def. * New GrpcPaymentAccountsService (gRPC boilerplate) and CorePaymentAccountsService (method implementations) classes were added. * The gRPC GetPaymentAccountsService stub was moved from GrpcServer to the new GrpcPaymentAccountsService class, and GrpcPaymentAccountsService is injected into GrpcServer. * A new createpaymentacct unit test was added to the bats test suite (checks for successful return status code). Maybe bit out of scope, some small changes were made towards making sure the entire API is defined in CoreApi, which is used as a pass-through object to the new CorePaymentAccountsService. In the next PR, similar refactoring will be done to make CoreApi the pass-through object for all of the existing CoreWalletsService methods. (CoreWalletsService will be injected into CoreApi.) In the future, all Grpc*Service implementations will call core services through CoreApi, for the sake of consistency. --- cli/src/main/java/bisq/cli/CliMain.java | 47 ++++++++++++--- cli/test.sh | 5 ++ .../src/main/java/bisq/core/grpc/CoreApi.java | 11 +++- .../core/grpc/CorePaymentAccountsService.java | 57 +++++++++++++++++++ .../core/grpc/GrpcPaymentAccountsService.java | 46 +++++++++++++++ .../main/java/bisq/core/grpc/GrpcServer.java | 26 ++------- proto/src/main/proto/grpc.proto | 15 ++++- 7 files changed, 173 insertions(+), 34 deletions(-) create mode 100644 core/src/main/java/bisq/core/grpc/CorePaymentAccountsService.java create mode 100644 core/src/main/java/bisq/core/grpc/GrpcPaymentAccountsService.java diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index ab6f39fa41c..c7c16a4376f 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -17,12 +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; @@ -58,6 +60,7 @@ public class CliMain { private enum Method { + createpaymentacct, getversion, getbalance, getaddressbalance, @@ -135,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 { @@ -172,6 +176,30 @@ public static void run(String[] args) { 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); @@ -238,16 +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", "getaddressbalance", "", "Get server wallet address balance"); - stream.format("%-19s%-30s%s%n", "getfundingaddresses", "", "Get BTC funding addresses"); - 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 875cb0a8a27..79754d188bb 100755 --- a/cli/test.sh +++ b/cli/test.sh @@ -166,6 +166,11 @@ [ "$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 ] 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/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/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index 41b490b9b53..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 { }