mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-24 07:07:43 +01:00
Merge pull request #4539 from ghubstan/refactor-bisqdaemonmain
Refactor API & add registerdisputeagent method to CLI
This commit is contained in:
commit
dee92670c5
18 changed files with 317 additions and 143 deletions
|
@ -48,14 +48,14 @@
|
|||
run ./bisq-cli --password="xyz" getversion
|
||||
[ "$status" -eq 0 ]
|
||||
echo "actual output: $output" >&2
|
||||
[ "$output" = "1.3.8" ]
|
||||
[ "$output" = "1.3.9" ]
|
||||
}
|
||||
|
||||
@test "test getversion" {
|
||||
run ./bisq-cli --password=xyz getversion
|
||||
[ "$status" -eq 0 ]
|
||||
echo "actual output: $output" >&2
|
||||
[ "$output" = "1.3.8" ]
|
||||
[ "$output" = "1.3.9" ]
|
||||
}
|
||||
|
||||
@test "test setwalletpassword \"a b c\"" {
|
||||
|
@ -166,15 +166,15 @@
|
|||
[ "$output" = "Error: address bogus not found in wallet" ]
|
||||
}
|
||||
|
||||
@test "test createpaymentacct PerfectMoneyDummy (missing nbr, ccy params)" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy
|
||||
@test "test createpaymentacct PerfectMoneyDummy (missing name, nbr, ccy params)" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PERFECT_MONEY
|
||||
[ "$status" -eq 1 ]
|
||||
echo "actual output: $output" >&2
|
||||
[ "$output" = "Error: incorrect parameter count, expecting account name, account number, currency code" ]
|
||||
[ "$output" = "Error: incorrect parameter count, expecting payment method id, account name, account number, currency code" ]
|
||||
}
|
||||
|
||||
@test "test createpaymentacct PerfectMoneyDummy 0123456789 USD" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PerfectMoneyDummy 0123456789 USD
|
||||
@test "test createpaymentacct PERFECT_MONEY PerfectMoneyDummy 0123456789 USD" {
|
||||
run ./bisq-cli --password=xyz createpaymentacct PERFECT_MONEY PerfectMoneyDummy 0123456789 USD
|
||||
[ "$status" -eq 0 ]
|
||||
}
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ import bisq.proto.grpc.GetOffersRequest;
|
|||
import bisq.proto.grpc.GetPaymentAccountsRequest;
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
import bisq.proto.grpc.LockWalletRequest;
|
||||
import bisq.proto.grpc.RegisterDisputeAgentRequest;
|
||||
import bisq.proto.grpc.RemoveWalletPasswordRequest;
|
||||
import bisq.proto.grpc.SetWalletPasswordRequest;
|
||||
import bisq.proto.grpc.UnlockWalletRequest;
|
||||
|
@ -69,7 +70,8 @@ public class CliMain {
|
|||
lockwallet,
|
||||
unlockwallet,
|
||||
removewalletpassword,
|
||||
setwalletpassword
|
||||
setwalletpassword,
|
||||
registerdisputeagent
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
@ -114,9 +116,9 @@ public class CliMain {
|
|||
}
|
||||
|
||||
var methodName = nonOptionArgs.get(0);
|
||||
final Method method;
|
||||
Method method;
|
||||
try {
|
||||
method = Method.valueOf(methodName);
|
||||
method = getMethodFromCmd(methodName);
|
||||
} catch (IllegalArgumentException ex) {
|
||||
throw new IllegalArgumentException(format("'%s' is not a supported method", methodName));
|
||||
}
|
||||
|
@ -128,6 +130,7 @@ public class CliMain {
|
|||
throw new IllegalArgumentException("missing required 'password' option");
|
||||
|
||||
GrpcStubs grpcStubs = new GrpcStubs(host, port, password);
|
||||
var disputeAgentsService = grpcStubs.disputeAgentsService;
|
||||
var versionService = grpcStubs.versionService;
|
||||
var offersService = grpcStubs.offersService;
|
||||
var paymentAccountsService = grpcStubs.paymentAccountsService;
|
||||
|
@ -166,34 +169,38 @@ public class CliMain {
|
|||
}
|
||||
case getoffers: {
|
||||
if (nonOptionArgs.size() < 3)
|
||||
throw new IllegalArgumentException("incorrect parameter count, expecting direction (buy|sell), currency code");
|
||||
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)
|
||||
.setCurrencyCode(fiatCurrency)
|
||||
.build();
|
||||
var reply = offersService.getOffers(request);
|
||||
out.println(formatOfferTable(reply.getOffersList(), fiatCurrency));
|
||||
return;
|
||||
}
|
||||
case createpaymentacct: {
|
||||
if (nonOptionArgs.size() < 4)
|
||||
if (nonOptionArgs.size() < 5)
|
||||
throw new IllegalArgumentException(
|
||||
"incorrect parameter count, expecting account name, account number, currency code");
|
||||
"incorrect parameter count, expecting payment method id,"
|
||||
+ " account name, account number, currency code");
|
||||
|
||||
var accountName = nonOptionArgs.get(1);
|
||||
var accountNumber = nonOptionArgs.get(2);
|
||||
var fiatCurrencyCode = nonOptionArgs.get(3);
|
||||
var paymentMethodId = nonOptionArgs.get(1);
|
||||
var accountName = nonOptionArgs.get(2);
|
||||
var accountNumber = nonOptionArgs.get(3);
|
||||
var currencyCode = nonOptionArgs.get(4);
|
||||
|
||||
var request = CreatePaymentAccountRequest.newBuilder()
|
||||
.setPaymentMethodId(paymentMethodId)
|
||||
.setAccountName(accountName)
|
||||
.setAccountNumber(accountNumber)
|
||||
.setFiatCurrencyCode(fiatCurrencyCode).build();
|
||||
.setCurrencyCode(currencyCode).build();
|
||||
paymentAccountsService.createPaymentAccount(request);
|
||||
out.println(format("payment account %s saved", accountName));
|
||||
out.printf("payment account %s saved", accountName);
|
||||
return;
|
||||
}
|
||||
case getpaymentaccts: {
|
||||
|
@ -232,7 +239,8 @@ public class CliMain {
|
|||
if (nonOptionArgs.size() < 2)
|
||||
throw new IllegalArgumentException("no password specified");
|
||||
|
||||
var request = RemoveWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1)).build();
|
||||
var request = RemoveWalletPasswordRequest.newBuilder()
|
||||
.setPassword(nonOptionArgs.get(1)).build();
|
||||
walletsService.removeWalletPassword(request);
|
||||
out.println("wallet decrypted");
|
||||
return;
|
||||
|
@ -241,7 +249,8 @@ public class CliMain {
|
|||
if (nonOptionArgs.size() < 2)
|
||||
throw new IllegalArgumentException("no password specified");
|
||||
|
||||
var requestBuilder = SetWalletPasswordRequest.newBuilder().setPassword(nonOptionArgs.get(1));
|
||||
var requestBuilder = SetWalletPasswordRequest.newBuilder()
|
||||
.setPassword(nonOptionArgs.get(1));
|
||||
var hasNewPassword = nonOptionArgs.size() == 3;
|
||||
if (hasNewPassword)
|
||||
requestBuilder.setNewPassword(nonOptionArgs.get(2));
|
||||
|
@ -249,6 +258,19 @@ public class CliMain {
|
|||
out.println("wallet encrypted" + (hasNewPassword ? " 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 requestBuilder = RegisterDisputeAgentRequest.newBuilder()
|
||||
.setDisputeAgentType(disputeAgentType).setRegistrationKey(registrationKey);
|
||||
disputeAgentsService.registerDisputeAgent(requestBuilder.build());
|
||||
out.println(disputeAgentType + " registered");
|
||||
return;
|
||||
}
|
||||
default: {
|
||||
throw new RuntimeException(format("unhandled method '%s'", method));
|
||||
}
|
||||
|
@ -260,6 +282,13 @@ public class CliMain {
|
|||
}
|
||||
}
|
||||
|
||||
private static Method getMethodFromCmd(String methodName) {
|
||||
// TODO if we use const type for enum we need add some mapping. Even if we don't
|
||||
// change now it is handy to have flexibility in case we change internal code
|
||||
// and don't want to break user commands.
|
||||
return Method.valueOf(methodName.toLowerCase());
|
||||
}
|
||||
|
||||
private static void printHelp(OptionParser parser, PrintStream stream) {
|
||||
try {
|
||||
stream.println("Bisq RPC Client");
|
||||
|
|
|
@ -139,8 +139,14 @@ public class CoreApi {
|
|||
// PaymentAccounts
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void createPaymentAccount(String accountName, String accountNumber, String fiatCurrencyCode) {
|
||||
paymentAccountsService.createPaymentAccount(accountName, accountNumber, fiatCurrencyCode);
|
||||
public void createPaymentAccount(String paymentMethodId,
|
||||
String accountName,
|
||||
String accountNumber,
|
||||
String currencyCode) {
|
||||
paymentAccountsService.createPaymentAccount(paymentMethodId,
|
||||
accountName,
|
||||
accountNumber,
|
||||
currencyCode);
|
||||
}
|
||||
|
||||
public Set<PaymentAccount> getPaymentAccounts() {
|
||||
|
|
|
@ -68,7 +68,7 @@ class CoreDisputeAgentsService {
|
|||
this.languageCodes = Arrays.asList("de", "en", "es", "fr");
|
||||
}
|
||||
|
||||
public void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||
void registerDisputeAgent(String disputeAgentType, String registrationKey) {
|
||||
if (!p2PService.isBootstrapped())
|
||||
throw new IllegalStateException("p2p service is not bootstrapped yet");
|
||||
|
||||
|
|
|
@ -58,7 +58,7 @@ class CoreOffersService {
|
|||
this.user = user;
|
||||
}
|
||||
|
||||
public List<Offer> getOffers(String direction, String fiatCurrencyCode) {
|
||||
List<Offer> getOffers(String direction, String fiatCurrencyCode) {
|
||||
List<Offer> offers = offerBookService.getOffers().stream()
|
||||
.filter(o -> {
|
||||
var offerOfWantedDirection = o.getDirection().name().equalsIgnoreCase(direction);
|
||||
|
@ -77,7 +77,7 @@ class CoreOffersService {
|
|||
return offers;
|
||||
}
|
||||
|
||||
public void createOffer(String currencyCode,
|
||||
void createOffer(String currencyCode,
|
||||
String directionAsString,
|
||||
long priceAsLong,
|
||||
boolean useMarketBasedPrice,
|
||||
|
@ -111,7 +111,7 @@ class CoreOffersService {
|
|||
resultHandler);
|
||||
}
|
||||
|
||||
public void createOffer(String offerId,
|
||||
void createOffer(String offerId,
|
||||
String currencyCode,
|
||||
OfferPayload.Direction direction,
|
||||
Price price,
|
||||
|
|
|
@ -33,6 +33,9 @@ import java.util.Set;
|
|||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static bisq.core.payment.payload.PaymentMethod.*;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Slf4j
|
||||
class CorePaymentAccountsService {
|
||||
|
||||
|
@ -49,17 +52,19 @@ class CorePaymentAccountsService {
|
|||
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.toUpperCase()));
|
||||
user.addPaymentAccount(paymentAccount);
|
||||
void createPaymentAccount(String paymentMethodId,
|
||||
String accountName,
|
||||
String accountNumber,
|
||||
String currencyCode) {
|
||||
|
||||
PaymentAccount paymentAccount = getNewPaymentAccount(paymentMethodId,
|
||||
accountName,
|
||||
accountNumber,
|
||||
currencyCode);
|
||||
|
||||
// TODO not sure if there is more to do at account creation.
|
||||
// Need to check all the flow when its created from UI.
|
||||
user.addPaymentAccountIfNotExists(paymentAccount);
|
||||
|
||||
// Don't do this on mainnet until thoroughly tested.
|
||||
if (config.baseCurrencyNetwork.isRegtest())
|
||||
|
@ -68,7 +73,64 @@ class CorePaymentAccountsService {
|
|||
log.info("Payment account {} saved", paymentAccount.getId());
|
||||
}
|
||||
|
||||
public Set<PaymentAccount> getPaymentAccounts() {
|
||||
Set<PaymentAccount> getPaymentAccounts() {
|
||||
return user.getPaymentAccounts();
|
||||
}
|
||||
|
||||
private PaymentAccount getNewPaymentAccount(String paymentMethodId,
|
||||
String accountName,
|
||||
String accountNumber,
|
||||
String currencyCode) {
|
||||
PaymentAccount paymentAccount = null;
|
||||
PaymentMethod paymentMethod = getPaymentMethodById(paymentMethodId);
|
||||
|
||||
switch (paymentMethod.getId()) {
|
||||
case UPHOLD_ID:
|
||||
case MONEY_BEAM_ID:
|
||||
case POPMONEY_ID:
|
||||
case REVOLUT_ID:
|
||||
//noinspection DuplicateBranchesInSwitch
|
||||
log.error("PaymentMethod {} not supported yet.", paymentMethod);
|
||||
break;
|
||||
case PERFECT_MONEY_ID:
|
||||
// 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.
|
||||
paymentAccount = PaymentAccountFactory.getPaymentAccount(paymentMethod);
|
||||
paymentAccount.init();
|
||||
paymentAccount.setAccountName(accountName);
|
||||
((PerfectMoneyAccount) paymentAccount).setAccountNr(accountNumber);
|
||||
paymentAccount.setSingleTradeCurrency(new FiatCurrency(currencyCode));
|
||||
break;
|
||||
case SEPA_ID:
|
||||
case SEPA_INSTANT_ID:
|
||||
case FASTER_PAYMENTS_ID:
|
||||
case NATIONAL_BANK_ID:
|
||||
case SAME_BANK_ID:
|
||||
case SPECIFIC_BANKS_ID:
|
||||
case JAPAN_BANK_ID:
|
||||
case ALI_PAY_ID:
|
||||
case WECHAT_PAY_ID:
|
||||
case SWISH_ID:
|
||||
case CLEAR_X_CHANGE_ID:
|
||||
case CHASE_QUICK_PAY_ID:
|
||||
case INTERAC_E_TRANSFER_ID:
|
||||
case US_POSTAL_MONEY_ORDER_ID:
|
||||
case MONEY_GRAM_ID:
|
||||
case WESTERN_UNION_ID:
|
||||
case CASH_DEPOSIT_ID:
|
||||
case HAL_CASH_ID:
|
||||
case F2F_ID:
|
||||
case PROMPT_PAY_ID:
|
||||
case ADVANCED_CASH_ID:
|
||||
default:
|
||||
log.error("PaymentMethod {} not supported yet.", paymentMethod);
|
||||
break;
|
||||
}
|
||||
|
||||
checkNotNull(paymentAccount,
|
||||
"Could not create payment account with paymentMethodId "
|
||||
+ paymentMethodId + ".");
|
||||
return paymentAccount;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -71,7 +71,7 @@ class CoreWalletsService {
|
|||
this.btcWalletService = btcWalletService;
|
||||
}
|
||||
|
||||
public long getAvailableBalance() {
|
||||
long getAvailableBalance() {
|
||||
verifyWalletsAreAvailable();
|
||||
verifyEncryptedWalletIsUnlocked();
|
||||
|
||||
|
@ -82,27 +82,26 @@ class CoreWalletsService {
|
|||
return balance.getValue();
|
||||
}
|
||||
|
||||
public long getAddressBalance(String addressString) {
|
||||
long getAddressBalance(String addressString) {
|
||||
Address address = getAddressEntry(addressString).getAddress();
|
||||
return btcWalletService.getBalanceForAddress(address).value;
|
||||
}
|
||||
|
||||
public AddressBalanceInfo getAddressBalanceInfo(String addressString) {
|
||||
AddressBalanceInfo getAddressBalanceInfo(String addressString) {
|
||||
var satoshiBalance = getAddressBalance(addressString);
|
||||
var numConfirmations = getNumConfirmationsForMostRecentTransaction(addressString);
|
||||
return new AddressBalanceInfo(addressString, satoshiBalance, numConfirmations);
|
||||
}
|
||||
|
||||
public List<AddressBalanceInfo> getFundingAddresses() {
|
||||
List<AddressBalanceInfo> getFundingAddresses() {
|
||||
verifyWalletsAreAvailable();
|
||||
verifyEncryptedWalletIsUnlocked();
|
||||
|
||||
// Create a new funding address if none exists.
|
||||
if (btcWalletService.getAvailableAddressEntries().size() == 0)
|
||||
if (btcWalletService.getAvailableAddressEntries().isEmpty())
|
||||
btcWalletService.getFreshAddressEntry();
|
||||
|
||||
List<String> addressStrings =
|
||||
btcWalletService
|
||||
List<String> addressStrings = btcWalletService
|
||||
.getAvailableAddressEntries()
|
||||
.stream()
|
||||
.map(AddressEntry::getAddressString)
|
||||
|
@ -113,8 +112,7 @@ class CoreWalletsService {
|
|||
// this::getAddressBalance cannot return null.
|
||||
var balances = memoize(this::getAddressBalance);
|
||||
|
||||
boolean noAddressHasZeroBalance =
|
||||
addressStrings.stream()
|
||||
boolean noAddressHasZeroBalance = addressStrings.stream()
|
||||
.allMatch(addressString -> balances.getUnchecked(addressString) != 0);
|
||||
|
||||
if (noAddressHasZeroBalance) {
|
||||
|
@ -129,13 +127,13 @@ class CoreWalletsService {
|
|||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public int getNumConfirmationsForMostRecentTransaction(String addressString) {
|
||||
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) {
|
||||
void setWalletPassword(String password, String newPassword) {
|
||||
verifyWalletsAreAvailable();
|
||||
|
||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||
|
@ -165,7 +163,7 @@ class CoreWalletsService {
|
|||
walletsManager.backupWallets();
|
||||
}
|
||||
|
||||
public void lockWallet() {
|
||||
void lockWallet() {
|
||||
if (!walletsManager.areWalletsEncrypted())
|
||||
throw new IllegalStateException("wallet is not encrypted with a password");
|
||||
|
||||
|
@ -175,7 +173,7 @@ class CoreWalletsService {
|
|||
tempAesKey = null;
|
||||
}
|
||||
|
||||
public void unlockWallet(String password, long timeout) {
|
||||
void unlockWallet(String password, long timeout) {
|
||||
verifyWalletIsAvailableAndEncrypted();
|
||||
|
||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||
|
@ -213,7 +211,7 @@ class CoreWalletsService {
|
|||
|
||||
// Provided for automated wallet protection method testing, despite the
|
||||
// security risks exposed by providing users the ability to decrypt their wallets.
|
||||
public void removeWalletPassword(String password) {
|
||||
void removeWalletPassword(String password) {
|
||||
verifyWalletIsAvailableAndEncrypted();
|
||||
KeyCrypterScrypt keyCrypterScrypt = getKeyCrypterScrypt();
|
||||
|
||||
|
|
|
@ -793,6 +793,9 @@ public abstract class WalletService {
|
|||
return maybeAddTxToWallet(transaction.bitcoinSerialize(), wallet, source);
|
||||
}
|
||||
|
||||
public Address getAddress(String addressString) {
|
||||
return Address.fromBase58(params, addressString);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// bisqWalletEventListener
|
||||
|
|
|
@ -189,6 +189,12 @@ public class User implements PersistedDataHost {
|
|||
// Collection operations
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void addPaymentAccountIfNotExists(PaymentAccount paymentAccount) {
|
||||
if (!paymentAccountExists(paymentAccount)) {
|
||||
addPaymentAccount(paymentAccount);
|
||||
}
|
||||
}
|
||||
|
||||
public void addPaymentAccount(PaymentAccount paymentAccount) {
|
||||
paymentAccount.onAddToUser();
|
||||
|
||||
|
@ -493,4 +499,8 @@ public class User implements PersistedDataHost {
|
|||
public boolean isPaymentAccountImport() {
|
||||
return isPaymentAccountImport;
|
||||
}
|
||||
|
||||
private boolean paymentAccountExists(PaymentAccount paymentAccount) {
|
||||
return getPaymentAccountsAsObservable().stream().anyMatch(e -> e.equals(paymentAccount));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import bisq.core.app.CoreModule;
|
|||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.AppModule;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
import bisq.common.setup.CommonSetup;
|
||||
|
||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||
|
@ -39,13 +40,15 @@ import bisq.daemon.grpc.GrpcServer;
|
|||
@Slf4j
|
||||
public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.BisqSetupListener {
|
||||
|
||||
private GrpcServer grpcServer;
|
||||
|
||||
public static void main(String[] args) {
|
||||
new BisqDaemonMain().execute(args);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// First synchronous execution tasks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected void configUserThread() {
|
||||
|
@ -70,9 +73,9 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
|
|||
headlessApp.setGracefulShutDownHandler(this);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
// We continue with a series of synchronous execution tasks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
protected AppModule getModule() {
|
||||
|
@ -91,7 +94,8 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
|
|||
// We need to be in user thread! We mapped at launchApplication already...
|
||||
headlessApp.startApplication();
|
||||
|
||||
// In headless mode we don't have an async behaviour so we trigger the setup by calling onApplicationStarted
|
||||
// In headless mode we don't have an async behaviour so we trigger the setup by
|
||||
// calling onApplicationStarted.
|
||||
onApplicationStarted();
|
||||
}
|
||||
|
||||
|
@ -99,7 +103,14 @@ public class BisqDaemonMain extends BisqHeadlessAppMain implements BisqSetup.Bis
|
|||
protected void onApplicationStarted() {
|
||||
super.onApplicationStarted();
|
||||
|
||||
GrpcServer grpcServer = injector.getInstance(GrpcServer.class);
|
||||
grpcServer = injector.getInstance(GrpcServer.class);
|
||||
grpcServer.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gracefulShutDown(ResultHandler resultHandler) {
|
||||
super.gracefulShutDown(resultHandler);
|
||||
|
||||
grpcServer.shutdown();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.core.api.CoreApi;
|
||||
import bisq.core.trade.statistics.TradeStatistics2;
|
||||
|
||||
import bisq.proto.grpc.GetTradeStatisticsGrpc;
|
||||
import bisq.proto.grpc.GetTradeStatisticsReply;
|
||||
import bisq.proto.grpc.GetTradeStatisticsRequest;
|
||||
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
class GrpcGetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase {
|
||||
|
||||
private final CoreApi coreApi;
|
||||
|
||||
@Inject
|
||||
public GrpcGetTradeStatisticsService(CoreApi coreApi) {
|
||||
this.coreApi = coreApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getTradeStatistics(GetTradeStatisticsRequest req,
|
||||
StreamObserver<GetTradeStatisticsReply> responseObserver) {
|
||||
|
||||
var tradeStatistics = coreApi.getTradeStatistics().stream()
|
||||
.map(TradeStatistics2::toProtoTradeStatistics2)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
}
|
|
@ -52,7 +52,7 @@ class GrpcOffersService extends OffersGrpc.OffersImplBase {
|
|||
// The client cannot see bisq.core.Offer or its fromProto method.
|
||||
// We use the lighter weight OfferInfo proto wrapper instead, containing just
|
||||
// enough fields to view and create offers.
|
||||
List<OfferInfo> result = coreApi.getOffers(req.getDirection(), req.getFiatCurrencyCode())
|
||||
List<OfferInfo> result = coreApi.getOffers(req.getDirection(), req.getCurrencyCode())
|
||||
.stream().map(offer -> new OfferInfo.OfferInfoBuilder()
|
||||
.withId(offer.getId())
|
||||
.withDirection(offer.getDirection().name())
|
||||
|
|
|
@ -45,7 +45,10 @@ class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImpl
|
|||
@Override
|
||||
public void createPaymentAccount(CreatePaymentAccountRequest req,
|
||||
StreamObserver<CreatePaymentAccountReply> responseObserver) {
|
||||
coreApi.createPaymentAccount(req.getAccountName(), req.getAccountNumber(), req.getFiatCurrencyCode());
|
||||
coreApi.createPaymentAccount(req.getPaymentMethodId(),
|
||||
req.getAccountName(),
|
||||
req.getAccountNumber(),
|
||||
req.getCurrencyCode());
|
||||
var reply = CreatePaymentAccountReply.newBuilder().build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
|
@ -54,10 +57,11 @@ class GrpcPaymentAccountsService extends PaymentAccountsGrpc.PaymentAccountsImpl
|
|||
@Override
|
||||
public void getPaymentAccounts(GetPaymentAccountsRequest req,
|
||||
StreamObserver<GetPaymentAccountsReply> responseObserver) {
|
||||
var tradeStatistics = coreApi.getPaymentAccounts().stream()
|
||||
var paymentAccounts = coreApi.getPaymentAccounts().stream()
|
||||
.map(PaymentAccount::toProtoMessage)
|
||||
.collect(Collectors.toList());
|
||||
var reply = GetPaymentAccountsReply.newBuilder().addAllPaymentAccounts(tradeStatistics).build();
|
||||
var reply = GetPaymentAccountsReply.newBuilder()
|
||||
.addAllPaymentAccounts(paymentAccounts).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
|
|
|
@ -18,30 +18,21 @@
|
|||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.core.api.CoreApi;
|
||||
import bisq.core.trade.statistics.TradeStatistics2;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
import bisq.proto.grpc.GetTradeStatisticsGrpc;
|
||||
import bisq.proto.grpc.GetTradeStatisticsReply;
|
||||
import bisq.proto.grpc.GetTradeStatisticsRequest;
|
||||
import bisq.proto.grpc.GetVersionGrpc;
|
||||
import bisq.proto.grpc.GetVersionReply;
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
|
||||
import io.grpc.Server;
|
||||
import io.grpc.ServerBuilder;
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Singleton
|
||||
@Slf4j
|
||||
public class GrpcServer {
|
||||
|
||||
|
@ -51,19 +42,22 @@ public class GrpcServer {
|
|||
@Inject
|
||||
public GrpcServer(Config config,
|
||||
CoreApi coreApi,
|
||||
PasswordAuthInterceptor passwordAuthInterceptor,
|
||||
GrpcDisputeAgentsService disputeAgentsService,
|
||||
GrpcOffersService offersService,
|
||||
GrpcPaymentAccountsService paymentAccountsService,
|
||||
GrpcVersionService versionService,
|
||||
GrpcGetTradeStatisticsService tradeStatisticsService,
|
||||
GrpcWalletsService walletsService) {
|
||||
this.coreApi = coreApi;
|
||||
this.server = ServerBuilder.forPort(config.apiPort)
|
||||
.addService(disputeAgentsService)
|
||||
.addService(new GetVersionService())
|
||||
.addService(new GetTradeStatisticsService())
|
||||
.addService(offersService)
|
||||
.addService(paymentAccountsService)
|
||||
.addService(tradeStatisticsService)
|
||||
.addService(versionService)
|
||||
.addService(walletsService)
|
||||
.intercept(new PasswordAuthInterceptor(config.apiPassword))
|
||||
.intercept(passwordAuthInterceptor)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -71,36 +65,14 @@ public class GrpcServer {
|
|||
try {
|
||||
server.start();
|
||||
log.info("listening on port {}", server.getPort());
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
server.shutdown();
|
||||
log.info("shutdown complete");
|
||||
}));
|
||||
} catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
class GetVersionService extends GetVersionGrpc.GetVersionImplBase {
|
||||
@Override
|
||||
public void getVersion(GetVersionRequest req, StreamObserver<GetVersionReply> responseObserver) {
|
||||
var reply = GetVersionReply.newBuilder().setVersion(coreApi.getVersion()).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
}
|
||||
|
||||
class GetTradeStatisticsService extends GetTradeStatisticsGrpc.GetTradeStatisticsImplBase {
|
||||
@Override
|
||||
public void getTradeStatistics(GetTradeStatisticsRequest req,
|
||||
StreamObserver<GetTradeStatisticsReply> responseObserver) {
|
||||
|
||||
var tradeStatistics = coreApi.getTradeStatistics().stream()
|
||||
.map(TradeStatistics2::toProtoTradeStatistics2)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
var reply = GetTradeStatisticsReply.newBuilder().addAllTradeStatistics(tradeStatistics).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
public void shutdown() {
|
||||
log.info("Server shutdown started");
|
||||
server.shutdown();
|
||||
log.info("Server shutdown complete");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.core.api.CoreApi;
|
||||
|
||||
import bisq.proto.grpc.GetVersionGrpc;
|
||||
import bisq.proto.grpc.GetVersionReply;
|
||||
import bisq.proto.grpc.GetVersionRequest;
|
||||
|
||||
import io.grpc.stub.StreamObserver;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
class GrpcVersionService extends GetVersionGrpc.GetVersionImplBase {
|
||||
|
||||
private final CoreApi coreApi;
|
||||
|
||||
@Inject
|
||||
public GrpcVersionService(CoreApi coreApi) {
|
||||
this.coreApi = coreApi;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getVersion(GetVersionRequest req, StreamObserver<GetVersionReply> responseObserver) {
|
||||
var reply = GetVersionReply.newBuilder().setVersion(coreApi.getVersion()).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
}
|
||||
}
|
|
@ -54,11 +54,17 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
|||
this.coreApi = coreApi;
|
||||
}
|
||||
|
||||
// TODO we need to support 3 or 4 balance types: available, reserved, lockedInTrade
|
||||
// and maybe total wallet balance (available+reserved). To not duplicate the methods,
|
||||
// we should pass an enum type. Enums in proto are a bit cumbersome as they are
|
||||
// global so you quickly run into namespace conflicts if not always prefixes which
|
||||
// makes it more verbose. In the core code base we move to the strategy to store the
|
||||
// enum name and map it. This gives also more flexibility with updates.
|
||||
@Override
|
||||
public void getBalance(GetBalanceRequest req, StreamObserver<GetBalanceReply> responseObserver) {
|
||||
try {
|
||||
long result = coreApi.getAvailableBalance();
|
||||
var reply = GetBalanceReply.newBuilder().setBalance(result).build();
|
||||
long availableBalance = coreApi.getAvailableBalance();
|
||||
var reply = GetBalanceReply.newBuilder().setBalance(availableBalance).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
} catch (IllegalStateException cause) {
|
||||
|
@ -72,8 +78,9 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
|||
public void getAddressBalance(GetAddressBalanceRequest req,
|
||||
StreamObserver<GetAddressBalanceReply> responseObserver) {
|
||||
try {
|
||||
AddressBalanceInfo result = coreApi.getAddressBalanceInfo(req.getAddress());
|
||||
var reply = GetAddressBalanceReply.newBuilder().setAddressBalanceInfo(result.toProtoMessage()).build();
|
||||
AddressBalanceInfo balanceInfo = coreApi.getAddressBalanceInfo(req.getAddress());
|
||||
var reply = GetAddressBalanceReply.newBuilder()
|
||||
.setAddressBalanceInfo(balanceInfo.toProtoMessage()).build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
} catch (IllegalStateException cause) {
|
||||
|
@ -87,10 +94,10 @@ class GrpcWalletsService extends WalletsGrpc.WalletsImplBase {
|
|||
public void getFundingAddresses(GetFundingAddressesRequest req,
|
||||
StreamObserver<GetFundingAddressesReply> responseObserver) {
|
||||
try {
|
||||
List<AddressBalanceInfo> result = coreApi.getFundingAddresses();
|
||||
List<AddressBalanceInfo> balanceInfo = coreApi.getFundingAddresses();
|
||||
var reply = GetFundingAddressesReply.newBuilder()
|
||||
.addAllAddressBalanceInfo(
|
||||
result.stream()
|
||||
balanceInfo.stream()
|
||||
.map(AddressBalanceInfo::toProtoMessage)
|
||||
.collect(Collectors.toList()))
|
||||
.build();
|
||||
|
|
|
@ -17,12 +17,16 @@
|
|||
|
||||
package bisq.daemon.grpc;
|
||||
|
||||
import bisq.common.config.Config;
|
||||
|
||||
import io.grpc.Metadata;
|
||||
import io.grpc.ServerCall;
|
||||
import io.grpc.ServerCallHandler;
|
||||
import io.grpc.ServerInterceptor;
|
||||
import io.grpc.StatusRuntimeException;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static io.grpc.Metadata.ASCII_STRING_MARSHALLER;
|
||||
import static io.grpc.Metadata.Key;
|
||||
import static io.grpc.Status.UNAUTHENTICATED;
|
||||
|
@ -36,12 +40,13 @@ import static java.lang.String.format;
|
|||
*/
|
||||
class PasswordAuthInterceptor implements ServerInterceptor {
|
||||
|
||||
public static final String PASSWORD_KEY = "password";
|
||||
private static final String PASSWORD_KEY = "password";
|
||||
|
||||
private final String expectedPasswordValue;
|
||||
|
||||
public PasswordAuthInterceptor(String expectedPasswordValue) {
|
||||
this.expectedPasswordValue = expectedPasswordValue;
|
||||
@Inject
|
||||
public PasswordAuthInterceptor(Config config) {
|
||||
this.expectedPasswordValue = config.apiPassword;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -53,7 +53,7 @@ service Offers {
|
|||
|
||||
message GetOffersRequest {
|
||||
string direction = 1;
|
||||
string fiatCurrencyCode = 2;
|
||||
string currencyCode = 2;
|
||||
}
|
||||
|
||||
message GetOffersReply {
|
||||
|
@ -61,7 +61,7 @@ message GetOffersReply {
|
|||
}
|
||||
|
||||
message CreateOfferRequest {
|
||||
string currencyCode = 1; // TODO switch order w/ direction field in next PR
|
||||
string currencyCode = 1;
|
||||
string direction = 2;
|
||||
uint64 price = 3;
|
||||
bool useMarketBasedPrice = 4;
|
||||
|
@ -107,9 +107,11 @@ service PaymentAccounts {
|
|||
}
|
||||
|
||||
message CreatePaymentAccountRequest {
|
||||
string accountName = 1;
|
||||
string accountNumber = 2;
|
||||
string fiatCurrencyCode = 3;
|
||||
string paymentMethodId = 1;
|
||||
string accountName = 2;
|
||||
string accountNumber = 3;
|
||||
// TODO Support all currencies. Maybe add a repeated and if only one is used its a singletonList.
|
||||
string currencyCode = 4;
|
||||
}
|
||||
|
||||
message CreatePaymentAccountReply {
|
||||
|
@ -123,7 +125,7 @@ message GetPaymentAccountsReply {
|
|||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// TradeStatistics
|
||||
// GetTradeStatistics
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
service GetTradeStatistics {
|
||||
|
|
Loading…
Add table
Reference in a new issue