From 2c85f258dc3e99f9c72c2191a6d18e4a8a01dfa3 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 1 Sep 2022 19:18:37 -0300 Subject: [PATCH] Remove dead api test harness code --- .../apitest/scenario/ScriptedBotTest.java | 121 ------ .../apitest/scenario/bot/AbstractBotTest.java | 110 ------ .../java/bisq/apitest/scenario/bot/Bot.java | 77 ---- .../bisq/apitest/scenario/bot/BotClient.java | 337 ----------------- .../bot/BotPaymentAccountGenerator.java | 68 ---- .../bot/InvalidRandomOfferException.java | 35 -- .../bot/PaymentAccountNotFoundException.java | 35 -- .../apitest/scenario/bot/RandomOffer.java | 178 --------- .../bisq/apitest/scenario/bot/RobotBob.java | 149 -------- .../scenario/bot/protocol/BotProtocol.java | 352 ------------------ .../bot/protocol/MakerBotProtocol.java | 116 ------ .../scenario/bot/protocol/ProtocolStep.java | 17 - .../bot/protocol/TakerBotProtocol.java | 137 ------- .../bot/script/BashScriptGenerator.java | 235 ------------ .../scenario/bot/script/BotScript.java | 78 ---- .../bot/script/BotScriptGenerator.java | 248 ------------ .../shutdown/ManualBotShutdownException.java | 35 -- .../scenario/bot/shutdown/ManualShutdown.java | 64 ---- 18 files changed, 2392 deletions(-) delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/ScriptedBotTest.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/AbstractBotTest.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/Bot.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/BotPaymentAccountGenerator.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/InvalidRandomOfferException.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/PaymentAccountNotFoundException.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/RobotBob.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/protocol/BotProtocol.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/protocol/MakerBotProtocol.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/protocol/ProtocolStep.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/script/BashScriptGenerator.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/script/BotScript.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/script/BotScriptGenerator.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/shutdown/ManualBotShutdownException.java delete mode 100644 apitest/src/test/java/bisq/apitest/scenario/bot/shutdown/ManualShutdown.java diff --git a/apitest/src/test/java/bisq/apitest/scenario/ScriptedBotTest.java b/apitest/src/test/java/bisq/apitest/scenario/ScriptedBotTest.java deleted file mode 100644 index fd187638803..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/ScriptedBotTest.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario; - -import lombok.extern.slf4j.Slf4j; - -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.MethodOrderer; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.condition.EnabledIf; - -import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind; -import static bisq.apitest.config.BisqAppConfig.alicedaemon; -import static bisq.apitest.config.BisqAppConfig.arbdaemon; -import static bisq.apitest.config.BisqAppConfig.bobdaemon; -import static bisq.apitest.config.BisqAppConfig.seednode; -import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.startShutdownTimer; -import static org.junit.jupiter.api.Assertions.fail; - - - -import bisq.apitest.config.ApiTestConfig; -import bisq.apitest.method.BitcoinCliHelper; -import bisq.apitest.scenario.bot.AbstractBotTest; -import bisq.apitest.scenario.bot.BotClient; -import bisq.apitest.scenario.bot.RobotBob; -import bisq.apitest.scenario.bot.script.BashScriptGenerator; -import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; - -// The test case is enabled if AbstractBotTest#botScriptExists() returns true. -@EnabledIf("botScriptExists") -@Slf4j -@TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class ScriptedBotTest extends AbstractBotTest { - - private RobotBob robotBob; - - @BeforeAll - public static void startTestHarness() { - botScript = deserializeBotScript(); - - if (botScript.isUseTestHarness()) { - startSupportingApps(true, - true, - bitcoind, - seednode, - arbdaemon, - alicedaemon, - bobdaemon); - } else { - // We need just enough configurations to make sure Bob and testers use - // the right apiPassword, to create a bitcoin-cli helper, and RobotBob's - // gRPC stubs. But the user will have to register dispute agents before - // an offer can be taken. - config = new ApiTestConfig("--apiPassword", "xyz"); - bitcoinCli = new BitcoinCliHelper(config); - log.warn("Don't forget to register dispute agents before trying to trade with me."); - } - - botClient = new BotClient(bobClient); - } - - @BeforeEach - public void initRobotBob() { - try { - BashScriptGenerator bashScriptGenerator = getBashScriptGenerator(); - robotBob = new RobotBob(botClient, botScript, bitcoinCli, bashScriptGenerator); - } catch (Exception ex) { - fail(ex); - } - } - - @Test - @Order(1) - public void runRobotBob() { - try { - - startShutdownTimer(); - robotBob.run(); - - } catch (ManualBotShutdownException ex) { - // This exception is thrown if a /tmp/bottest-shutdown file was found. - // You can also kill -15 - // of worker.org.gradle.process.internal.worker.GradleWorkerMain 'Gradle Test Executor #' - // - // This will cleanly shut everything down as well, but you will see a - // Process 'Gradle Test Executor #' finished with non-zero exit value 143 error, - // which you may think is a test failure. - log.warn("{} Shutting down test case before test completion;" - + " this is not a test failure.", - ex.getMessage()); - } catch (Throwable throwable) { - fail(throwable); - } - } - - @AfterAll - public static void tearDown() { - if (botScript.isUseTestHarness()) - tearDownScaffold(); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/AbstractBotTest.java b/apitest/src/test/java/bisq/apitest/scenario/bot/AbstractBotTest.java deleted file mode 100644 index 818a66d0c25..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/AbstractBotTest.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot; - -import bisq.core.locale.Country; - -import protobuf.PaymentAccount; - -import com.google.gson.GsonBuilder; - -import java.nio.file.Paths; - -import java.io.File; -import java.io.IOException; - -import lombok.extern.slf4j.Slf4j; - -import static bisq.core.locale.CountryUtil.findCountryByCode; -import static bisq.core.payment.payload.PaymentMethod.CLEAR_X_CHANGE_ID; -import static bisq.core.payment.payload.PaymentMethod.getPaymentMethod; -import static java.lang.String.format; -import static java.lang.System.getProperty; -import static java.nio.file.Files.readAllBytes; - - - -import bisq.apitest.method.MethodTest; -import bisq.apitest.scenario.bot.script.BashScriptGenerator; -import bisq.apitest.scenario.bot.script.BotScript; - -@Slf4j -public abstract class AbstractBotTest extends MethodTest { - - protected static final String BOT_SCRIPT_NAME = "bot-script.json"; - protected static BotScript botScript; - protected static BotClient botClient; - - protected BashScriptGenerator getBashScriptGenerator() { - if (botScript.isUseTestHarness()) { - PaymentAccount alicesAccount = createAlicesPaymentAccount(); - botScript.setPaymentAccountIdForCliScripts(alicesAccount.getId()); - } - return new BashScriptGenerator(config.apiPassword, - botScript.getApiPortForCliScripts(), - botScript.getPaymentAccountIdForCliScripts(), - botScript.isPrintCliScripts()); - } - - private PaymentAccount createAlicesPaymentAccount() { - BotPaymentAccountGenerator accountGenerator = - new BotPaymentAccountGenerator(new BotClient(aliceClient)); - String paymentMethodId = botScript.getBotPaymentMethodId(); - if (paymentMethodId != null) { - if (paymentMethodId.equals(CLEAR_X_CHANGE_ID)) { - // Only Zelle test accts are supported now. - return accountGenerator.createZellePaymentAccount( - "Alice's Zelle Account", - "Alice"); - } else { - throw new UnsupportedOperationException( - format("This test harness bot does not work with %s payment accounts yet.", - getPaymentMethod(paymentMethodId).getDisplayString())); - } - } else { - String countryCode = botScript.getCountryCode(); - Country country = findCountryByCode(countryCode).orElseThrow(() -> - new IllegalArgumentException(countryCode + " is not a valid iso country code.")); - return accountGenerator.createF2FPaymentAccount(country, - "Alice's " + country.name + " F2F Account"); - } - } - - protected static BotScript deserializeBotScript() { - try { - File botScriptFile = new File(getProperty("java.io.tmpdir"), BOT_SCRIPT_NAME); - String json = new String(readAllBytes(Paths.get(botScriptFile.getPath()))); - return new GsonBuilder().setPrettyPrinting().create().fromJson(json, BotScript.class); - } catch (IOException ex) { - throw new IllegalStateException("Error reading script bot file contents.", ex); - } - } - - @SuppressWarnings("unused") // This is used by the jupiter framework. - protected static boolean botScriptExists() { - File botScriptFile = new File(getProperty("java.io.tmpdir"), BOT_SCRIPT_NAME); - if (botScriptFile.exists()) { - botScriptFile.deleteOnExit(); - log.info("Enabled, found {}.", botScriptFile.getPath()); - return true; - } else { - log.info("Skipped, no bot script.\n\tTo generate a bot-script.json file, see BotScriptGenerator."); - return false; - } - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/Bot.java b/apitest/src/test/java/bisq/apitest/scenario/bot/Bot.java deleted file mode 100644 index 1fb46e717f4..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/Bot.java +++ /dev/null @@ -1,77 +0,0 @@ -package bisq.apitest.scenario.bot; - -import bisq.core.locale.Country; - -import protobuf.PaymentAccount; - -import lombok.extern.slf4j.Slf4j; - -import static bisq.core.locale.CountryUtil.findCountryByCode; -import static bisq.core.payment.payload.PaymentMethod.CLEAR_X_CHANGE_ID; -import static bisq.core.payment.payload.PaymentMethod.getPaymentMethod; -import static java.lang.String.format; -import static java.util.concurrent.TimeUnit.MINUTES; - - - -import bisq.apitest.method.BitcoinCliHelper; -import bisq.apitest.scenario.bot.script.BashScriptGenerator; -import bisq.apitest.scenario.bot.script.BotScript; - -@Slf4j -public -class Bot { - - static final String MAKE = "MAKE"; - static final String TAKE = "TAKE"; - - protected final BotClient botClient; - protected final BitcoinCliHelper bitcoinCli; - protected final BashScriptGenerator bashScriptGenerator; - protected final String[] actions; - protected final long protocolStepTimeLimitInMs; - protected final boolean stayAlive; - protected final boolean isUsingTestHarness; - protected final PaymentAccount paymentAccount; - - public Bot(BotClient botClient, - BotScript botScript, - BitcoinCliHelper bitcoinCli, - BashScriptGenerator bashScriptGenerator) { - this.botClient = botClient; - this.bitcoinCli = bitcoinCli; - this.bashScriptGenerator = bashScriptGenerator; - this.actions = botScript.getActions(); - this.protocolStepTimeLimitInMs = MINUTES.toMillis(botScript.getProtocolStepTimeLimitInMinutes()); - this.stayAlive = botScript.isStayAlive(); - this.isUsingTestHarness = botScript.isUseTestHarness(); - if (isUsingTestHarness) - this.paymentAccount = createBotPaymentAccount(botScript); - else - this.paymentAccount = botClient.getPaymentAccount(botScript.getPaymentAccountIdForBot()); - } - - private PaymentAccount createBotPaymentAccount(BotScript botScript) { - BotPaymentAccountGenerator accountGenerator = new BotPaymentAccountGenerator(botClient); - - String paymentMethodId = botScript.getBotPaymentMethodId(); - if (paymentMethodId != null) { - if (paymentMethodId.equals(CLEAR_X_CHANGE_ID)) { - return accountGenerator.createZellePaymentAccount("Bob's Zelle Account", - "Bob"); - } else { - throw new UnsupportedOperationException( - format("This bot test does not work with %s payment accounts yet.", - getPaymentMethod(paymentMethodId).getDisplayString())); - } - } else { - Country country = findCountry(botScript.getCountryCode()); - return accountGenerator.createF2FPaymentAccount(country, country.name + " F2F Account"); - } - } - - private Country findCountry(String countryCode) { - return findCountryByCode(countryCode).orElseThrow(() -> - new IllegalArgumentException(countryCode + " is not a valid iso country code.")); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java b/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java deleted file mode 100644 index ccdd236f3f3..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/BotClient.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot; - -import bisq.proto.grpc.BalancesInfo; -import bisq.proto.grpc.GetPaymentAccountsRequest; -import bisq.proto.grpc.OfferInfo; -import bisq.proto.grpc.TradeInfo; - -import protobuf.PaymentAccount; - -import java.text.DecimalFormat; - -import java.util.List; -import java.util.function.BiPredicate; - -import lombok.extern.slf4j.Slf4j; - -import static org.apache.commons.lang3.StringUtils.capitalize; - - - -import bisq.cli.GrpcClient; - -/** - * Convenience GrpcClient wrapper for bots using gRPC services. - */ -@SuppressWarnings({"JavaDoc", "unused"}) -@Slf4j -public class BotClient { - - private static final DecimalFormat FIXED_PRICE_FMT = new DecimalFormat("###########0"); - - private final GrpcClient grpcClient; - - public BotClient(GrpcClient grpcClient) { - this.grpcClient = grpcClient; - } - - /** - * Returns current BSQ and BTC balance information. - * @return BalancesInfo - */ - public BalancesInfo getBalance() { - return grpcClient.getBalances(); - } - - /** - * Return the most recent BTC market price for the given currencyCode. - * @param currencyCode - * @return double - */ - public double getCurrentBTCMarketPrice(String currencyCode) { - return grpcClient.getBtcPrice(currencyCode); - } - - /** - * Return the most recent BTC market price for the given currencyCode as an integer string. - * @param currencyCode - * @return String - */ - public String getCurrentBTCMarketPriceAsIntegerString(String currencyCode) { - return FIXED_PRICE_FMT.format(getCurrentBTCMarketPrice(currencyCode)); - } - - /** - * Return all BUY and SELL offers for the given currencyCode. - * @param currencyCode - * @return List - */ - public List getOffers(String currencyCode) { - var buyOffers = getBuyOffers(currencyCode); - if (buyOffers.size() > 0) { - return buyOffers; - } else { - return getSellOffers(currencyCode); - } - } - - /** - * Return BUY offers for the given currencyCode. - * @param currencyCode - * @return List - */ - public List getBuyOffers(String currencyCode) { - return grpcClient.getOffers("BUY", currencyCode); - } - - /** - * Return SELL offers for the given currencyCode. - * @param currencyCode - * @return List - */ - public List getSellOffers(String currencyCode) { - return grpcClient.getOffers("SELL", currencyCode); - } - - /** - * Create and return a new Offer using a market based price. - * @param paymentAccount - * @param direction - * @param currencyCode - * @param amountInSatoshis - * @param minAmountInSatoshis - * @param priceMarginAsPercent - * @param securityDepositAsPercent - * @param feeCurrency - * @param triggerPrice - * @return OfferInfo - */ - public OfferInfo createOfferAtMarketBasedPrice(PaymentAccount paymentAccount, - String direction, - String currencyCode, - long amountInSatoshis, - long minAmountInSatoshis, - double priceMarginAsPercent, - double securityDepositAsPercent, - String feeCurrency, - String triggerPrice) { - return grpcClient.createMarketBasedPricedOffer(direction, - currencyCode, - amountInSatoshis, - minAmountInSatoshis, - priceMarginAsPercent, - securityDepositAsPercent, - paymentAccount.getId(), - feeCurrency, - triggerPrice); - } - - /** - * Create and return a new Offer using a fixed price. - * @param paymentAccount - * @param direction - * @param currencyCode - * @param amountInSatoshis - * @param minAmountInSatoshis - * @param fixedOfferPriceAsString - * @param securityDepositAsPercent - * @param feeCurrency - * @return OfferInfo - */ - public OfferInfo createOfferAtFixedPrice(PaymentAccount paymentAccount, - String direction, - String currencyCode, - long amountInSatoshis, - long minAmountInSatoshis, - String fixedOfferPriceAsString, - double securityDepositAsPercent, - String feeCurrency) { - return grpcClient.createFixedPricedOffer(direction, - currencyCode, - amountInSatoshis, - minAmountInSatoshis, - fixedOfferPriceAsString, - securityDepositAsPercent, - paymentAccount.getId(), - feeCurrency); - } - - public TradeInfo takeOffer(String offerId, PaymentAccount paymentAccount, String feeCurrency) { - return grpcClient.takeOffer(offerId, paymentAccount.getId(), feeCurrency, 0L); - } - - /** - * Returns a persisted Trade with the given tradeId, or throws an exception. - * @param tradeId - * @return TradeInfo - */ - public TradeInfo getTrade(String tradeId) { - return grpcClient.getTrade(tradeId); - } - - /** - * Predicate returns true if the given exception indicates the trade with the given - * tradeId exists, but the trade's contract has not been fully prepared. - */ - public final BiPredicate tradeContractIsNotReady = (exception, tradeId) -> { - if (exception.getMessage().contains("no contract was found")) { - log.warn("Trade {} exists but is not fully prepared: {}.", - tradeId, - toCleanGrpcExceptionMessage(exception)); - return true; - } else { - return false; - } - }; - - /** - * Returns a trade's contract as a Json string, or null if the trade exists - * but the contract is not ready. - * @param tradeId - * @return String - */ - public String getTradeContract(String tradeId) { - try { - var trade = grpcClient.getTrade(tradeId); - return trade.getContractAsJson(); - } catch (Exception ex) { - if (tradeContractIsNotReady.test(ex, tradeId)) - return null; - else - throw ex; - } - } - - /** - * Returns true if the trade's taker deposit fee transaction has been published. - * @param tradeId a valid trade id - * @return boolean - */ - public boolean isTakerDepositFeeTxPublished(String tradeId) { - return grpcClient.getTrade(tradeId).getIsPayoutPublished(); - } - - /** - * Returns true if the trade's taker deposit fee transaction has been confirmed. - * @param tradeId a valid trade id - * @return boolean - */ - public boolean isTakerDepositFeeTxConfirmed(String tradeId) { - return grpcClient.getTrade(tradeId).getIsDepositConfirmed(); - } - - /** - * Returns true if the trade's 'start payment' message has been sent by the buyer. - * @param tradeId a valid trade id - * @return boolean - */ - public boolean isTradePaymentStartedSent(String tradeId) { - return grpcClient.getTrade(tradeId).getIsPaymentStartedMessageSent(); - } - - /** - * Returns true if the trade's 'payment received' message has been sent by the seller. - * @param tradeId a valid trade id - * @return boolean - */ - public boolean isTradePaymentReceivedConfirmationSent(String tradeId) { - return grpcClient.getTrade(tradeId).getIsPaymentReceivedMessageSent(); - } - - /** - * Returns true if the trade's payout transaction has been published. - * @param tradeId a valid trade id - * @return boolean - */ - public boolean isTradePayoutTxPublished(String tradeId) { - return grpcClient.getTrade(tradeId).getIsPayoutPublished(); - } - - /** - * Sends a 'confirm payment started message' for a trade with the given tradeId, - * or throws an exception. - * @param tradeId - */ - public void sendConfirmPaymentStartedMessage(String tradeId) { - grpcClient.confirmPaymentStarted(tradeId); - } - - /** - * Sends a 'confirm payment received message' for a trade with the given tradeId, - * or throws an exception. - * @param tradeId - */ - public void sendConfirmPaymentReceivedMessage(String tradeId) { - grpcClient.confirmPaymentReceived(tradeId); - } - - /** - * Sends a 'closetrade' for a trade with the given tradeId, - * or throws an exception. - * @param tradeId - */ - public void sendCloseTradeMessage(String tradeId) { - grpcClient.closeTrade(tradeId); - } - - /** - * Create and save a new PaymentAccount with details in the given json. - * @param json - * @return PaymentAccount - */ - public PaymentAccount createNewPaymentAccount(String json) { - return grpcClient.createPaymentAccount(json); - } - - /** - * Returns a persisted PaymentAccount with the given paymentAccountId, or throws - * an exception. - * @param paymentAccountId The id of the PaymentAccount being looked up. - * @return PaymentAccount - */ - public PaymentAccount getPaymentAccount(String paymentAccountId) { - return grpcClient.getPaymentAccounts().stream() - .filter(a -> (a.getId().equals(paymentAccountId))) - .findFirst() - .orElseThrow(() -> - new PaymentAccountNotFoundException("Could not find a payment account with id " - + paymentAccountId + ".")); - } - - /** - * Returns a persisted PaymentAccount with the given accountName, or throws - * an exception. - * @param accountName - * @return PaymentAccount - */ - public PaymentAccount getPaymentAccountWithName(String accountName) { - var req = GetPaymentAccountsRequest.newBuilder().build(); - return grpcClient.getPaymentAccounts().stream() - .filter(a -> (a.getAccountName().equals(accountName))) - .findFirst() - .orElseThrow(() -> - new PaymentAccountNotFoundException("Could not find a payment account with name " - + accountName + ".")); - } - - public String toCleanGrpcExceptionMessage(Exception ex) { - return capitalize(ex.getMessage().replaceFirst("^[A-Z_]+: ", "")); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/BotPaymentAccountGenerator.java b/apitest/src/test/java/bisq/apitest/scenario/bot/BotPaymentAccountGenerator.java deleted file mode 100644 index e586c3236af..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/BotPaymentAccountGenerator.java +++ /dev/null @@ -1,68 +0,0 @@ -package bisq.apitest.scenario.bot; - -import bisq.core.api.model.PaymentAccountForm; -import bisq.core.locale.Country; - -import protobuf.PaymentAccount; - -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; - -import java.io.File; - -import java.util.Map; - -import lombok.extern.slf4j.Slf4j; - -import static bisq.core.payment.payload.PaymentMethod.CLEAR_X_CHANGE_ID; -import static bisq.core.payment.payload.PaymentMethod.F2F_ID; - -@Slf4j -public class BotPaymentAccountGenerator { - - private final Gson gson = new GsonBuilder().setPrettyPrinting().serializeNulls().create(); - - private final BotClient botClient; - - public BotPaymentAccountGenerator(BotClient botClient) { - this.botClient = botClient; - } - - public PaymentAccount createF2FPaymentAccount(Country country, String accountName) { - try { - return botClient.getPaymentAccountWithName(accountName); - } catch (PaymentAccountNotFoundException ignored) { - // Ignore not found exception, create a new account. - } - Map p = getPaymentAccountFormMap(F2F_ID); - p.put("accountName", accountName); - p.put("city", country.name + " City"); - p.put("country", country.code); - p.put("contact", "By Semaphore"); - p.put("extraInfo", ""); - // Convert the map back to a json string and create the payment account over gRPC. - return botClient.createNewPaymentAccount(gson.toJson(p)); - } - - public PaymentAccount createZellePaymentAccount(String accountName, String holderName) { - try { - return botClient.getPaymentAccountWithName(accountName); - } catch (PaymentAccountNotFoundException ignored) { - // Ignore not found exception, create a new account. - } - Map p = getPaymentAccountFormMap(CLEAR_X_CHANGE_ID); - p.put("accountName", accountName); - p.put("emailOrMobileNr", holderName + "@zelle.com"); - p.put("holderName", holderName); - return botClient.createNewPaymentAccount(gson.toJson(p)); - } - - private Map getPaymentAccountFormMap(String paymentMethodId) { - PaymentAccountForm paymentAccountForm = new PaymentAccountForm(); - File jsonFormTemplate = paymentAccountForm.getPaymentAccountForm(paymentMethodId); - jsonFormTemplate.deleteOnExit(); - String jsonString = paymentAccountForm.toJsonString(jsonFormTemplate); - //noinspection unchecked - return (Map) gson.fromJson(jsonString, Object.class); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/InvalidRandomOfferException.java b/apitest/src/test/java/bisq/apitest/scenario/bot/InvalidRandomOfferException.java deleted file mode 100644 index ccd1a2ebf14..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/InvalidRandomOfferException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot; - -import bisq.common.BisqException; - -@SuppressWarnings("unused") -public class InvalidRandomOfferException extends BisqException { - public InvalidRandomOfferException(Throwable cause) { - super(cause); - } - - public InvalidRandomOfferException(String format, Object... args) { - super(format, args); - } - - public InvalidRandomOfferException(Throwable cause, String format, Object... args) { - super(cause, format, args); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/PaymentAccountNotFoundException.java b/apitest/src/test/java/bisq/apitest/scenario/bot/PaymentAccountNotFoundException.java deleted file mode 100644 index 8578a38af75..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/PaymentAccountNotFoundException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot; - -import bisq.common.BisqException; - -@SuppressWarnings("unused") -public class PaymentAccountNotFoundException extends BisqException { - public PaymentAccountNotFoundException(Throwable cause) { - super(cause); - } - - public PaymentAccountNotFoundException(String format, Object... args) { - super(format, args); - } - - public PaymentAccountNotFoundException(Throwable cause, String format, Object... args) { - super(cause, format, args); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java b/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java deleted file mode 100644 index 40afcadab95..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/RandomOffer.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot; - -import bisq.proto.grpc.OfferInfo; - -import protobuf.PaymentAccount; - -import java.security.SecureRandom; - -import java.text.DecimalFormat; - -import java.math.BigDecimal; - -import java.util.Objects; -import java.util.function.Supplier; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static bisq.apitest.method.offer.AbstractOfferTest.defaultBuyerSecurityDepositPct; -import static bisq.cli.CurrencyFormat.formatInternalFiatPrice; -import static bisq.cli.CurrencyFormat.formatSatoshis; -import static bisq.common.util.MathUtils.scaleDownByPowerOf10; -import static bisq.core.payment.payload.PaymentMethod.F2F_ID; -import static java.lang.String.format; -import static java.math.RoundingMode.HALF_UP; - -@Slf4j -public class RandomOffer { - private static final SecureRandom RANDOM = new SecureRandom(); - - private static final DecimalFormat FIXED_PRICE_FMT = new DecimalFormat("###########0"); - - @SuppressWarnings("FieldCanBeLocal") - // If not an F2F account, keep amount <= 0.01 BTC to avoid hitting unsigned - // acct trading limit. - private final Supplier nextAmount = () -> - this.getPaymentAccount().getPaymentMethod().getId().equals(F2F_ID) - ? (long) (10000000 + RANDOM.nextInt(2500000)) - : (long) (750000 + RANDOM.nextInt(250000)); - - @SuppressWarnings("FieldCanBeLocal") - private final Supplier nextMinAmount = () -> { - boolean useMinAmount = RANDOM.nextBoolean(); - if (useMinAmount) { - return this.getPaymentAccount().getPaymentMethod().getId().equals(F2F_ID) - ? this.getAmount() - 5000000L - : this.getAmount() - 50000L; - } else { - return this.getAmount(); - } - }; - - @SuppressWarnings("FieldCanBeLocal") - private final Supplier nextPriceMargin = () -> { - boolean useZeroMargin = RANDOM.nextBoolean(); - if (useZeroMargin) { - return 0.00; - } else { - BigDecimal min = BigDecimal.valueOf(-5.0).setScale(2, HALF_UP); - BigDecimal max = BigDecimal.valueOf(5.0).setScale(2, HALF_UP); - BigDecimal randomBigDecimal = min.add(BigDecimal.valueOf(RANDOM.nextDouble()).multiply(max.subtract(min))); - return randomBigDecimal.setScale(2, HALF_UP).doubleValue(); - } - }; - - private final BotClient botClient; - @Getter - private final PaymentAccount paymentAccount; - @Getter - private final String direction; - @Getter - private final String currencyCode; - @Getter - private final long amount; - @Getter - private final long minAmount; - @Getter - private final boolean useMarketBasedPrice; - @Getter - private final double priceMargin; - @Getter - private final String feeCurrency; - - @Getter - private String fixedOfferPrice = "0"; - @Getter - private OfferInfo offer; - @Getter - private String id; - - public RandomOffer(BotClient botClient, PaymentAccount paymentAccount) { - this.botClient = botClient; - this.paymentAccount = paymentAccount; - this.direction = RANDOM.nextBoolean() ? "BUY" : "SELL"; - this.currencyCode = Objects.requireNonNull(paymentAccount.getSelectedTradeCurrency()).getCode(); - this.amount = nextAmount.get(); - this.minAmount = nextMinAmount.get(); - this.useMarketBasedPrice = RANDOM.nextBoolean(); - this.priceMargin = nextPriceMargin.get(); - this.feeCurrency = RANDOM.nextBoolean() ? "BSQ" : "BTC"; - } - - public RandomOffer create() throws InvalidRandomOfferException { - try { - printDescription(); - if (useMarketBasedPrice) { - this.offer = botClient.createOfferAtMarketBasedPrice(paymentAccount, - direction, - currencyCode, - amount, - minAmount, - priceMargin, - defaultBuyerSecurityDepositPct.get(), - feeCurrency, - "0" /*no trigger price*/); - } else { - this.offer = botClient.createOfferAtFixedPrice(paymentAccount, - direction, - currencyCode, - amount, - minAmount, - fixedOfferPrice, - defaultBuyerSecurityDepositPct.get(), - feeCurrency); - } - this.id = offer.getId(); - return this; - } catch (Exception ex) { - String error = format("Could not create valid %s offer for %s BTC: %s", - currencyCode, - formatSatoshis(amount), - ex.getMessage()); - throw new InvalidRandomOfferException(error, ex); - } - } - - private void printDescription() { - double currentMarketPrice = botClient.getCurrentBTCMarketPrice(currencyCode); - // Calculate a fixed price based on the random mkt price margin, even if we don't use it. - double differenceFromMarketPrice = currentMarketPrice * scaleDownByPowerOf10(priceMargin, 2); - double fixedOfferPriceAsDouble = direction.equals("BUY") - ? currentMarketPrice - differenceFromMarketPrice - : currentMarketPrice + differenceFromMarketPrice; - this.fixedOfferPrice = FIXED_PRICE_FMT.format(fixedOfferPriceAsDouble); - String description = format("Creating new %s %s / %s offer for amount = %s BTC, min-amount = %s BTC.", - useMarketBasedPrice ? "mkt-based-price" : "fixed-priced", - direction, - currencyCode, - formatSatoshis(amount), - formatSatoshis(minAmount)); - log.info(description); - if (useMarketBasedPrice) { - log.info("Offer Price Margin = {}%", priceMargin); - log.info("Expected Offer Price = {} {}", formatInternalFiatPrice(Double.parseDouble(fixedOfferPrice)), currencyCode); - } else { - - log.info("Fixed Offer Price = {} {}", fixedOfferPrice, currencyCode); - } - log.info("Current Market Price = {} {}", formatInternalFiatPrice(currentMarketPrice), currencyCode); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/RobotBob.java b/apitest/src/test/java/bisq/apitest/scenario/bot/RobotBob.java deleted file mode 100644 index 618b64c66ad..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/RobotBob.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE; -import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.isShutdownCalled; -import static bisq.cli.table.builder.TableType.BSQ_BALANCE_TBL; -import static bisq.cli.table.builder.TableType.BTC_BALANCE_TBL; -import static java.util.concurrent.TimeUnit.SECONDS; - - - -import bisq.apitest.method.BitcoinCliHelper; -import bisq.apitest.scenario.bot.protocol.BotProtocol; -import bisq.apitest.scenario.bot.protocol.MakerBotProtocol; -import bisq.apitest.scenario.bot.protocol.TakerBotProtocol; -import bisq.apitest.scenario.bot.script.BashScriptGenerator; -import bisq.apitest.scenario.bot.script.BotScript; -import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; -import bisq.cli.table.builder.TableBuilder; - -@Slf4j -public -class RobotBob extends Bot { - - @Getter - private int numTrades; - - public RobotBob(BotClient botClient, - BotScript botScript, - BitcoinCliHelper bitcoinCli, - BashScriptGenerator bashScriptGenerator) { - super(botClient, botScript, bitcoinCli, bashScriptGenerator); - } - - public void run() { - for (String action : actions) { - checkActionIsValid(action); - - BotProtocol botProtocol; - if (action.equalsIgnoreCase(MAKE)) { - botProtocol = new MakerBotProtocol(botClient, - paymentAccount, - protocolStepTimeLimitInMs, - bitcoinCli, - bashScriptGenerator); - } else { - botProtocol = new TakerBotProtocol(botClient, - paymentAccount, - protocolStepTimeLimitInMs, - bitcoinCli, - bashScriptGenerator); - } - - botProtocol.run(); - - if (!botProtocol.getCurrentProtocolStep().equals(DONE)) { - throw new IllegalStateException(botProtocol.getClass().getSimpleName() + " failed to complete."); - } - - StringBuilder balancesBuilder = new StringBuilder(); - balancesBuilder.append("BTC").append("\n"); - balancesBuilder.append(new TableBuilder(BTC_BALANCE_TBL, botClient.getBalance().getBtc()).build().toString()).append("\n"); - balancesBuilder.append("BSQ").append("\n"); - balancesBuilder.append(new TableBuilder(BSQ_BALANCE_TBL, botClient.getBalance().getBsq()).build().toString()); - - log.info("Completed {} successful trade{}. Current Balance:\n{}", - ++numTrades, - numTrades == 1 ? "" : "s", - balancesBuilder); - - if (numTrades < actions.length) { - try { - SECONDS.sleep(20); - } catch (InterruptedException ignored) { - // empty - } - } - - } // end of actions loop - - if (stayAlive) - waitForManualShutdown(); - else - warnCLIUserBeforeShutdown(); - } - - private void checkActionIsValid(String action) { - if (!action.equalsIgnoreCase(MAKE) && !action.equalsIgnoreCase(TAKE)) - throw new IllegalStateException(action + " is not a valid bot action; must be 'make' or 'take'"); - } - - private void waitForManualShutdown() { - String harnessOrCase = isUsingTestHarness ? "harness" : "case"; - log.info("All script actions have been completed, but the test {} will stay alive" - + " until a /tmp/bottest-shutdown file is detected.", - harnessOrCase); - log.info("When ready to shutdown the test {}, run '$ touch /tmp/bottest-shutdown'.", - harnessOrCase); - if (!isUsingTestHarness) { - log.warn("You will have to manually shutdown the bitcoind and Bisq nodes" - + " running outside of the test harness."); - } - try { - while (!isShutdownCalled()) { - SECONDS.sleep(10); - } - log.warn("Manual shutdown signal received."); - } catch (ManualBotShutdownException ex) { - log.warn(ex.getMessage()); - } catch (InterruptedException ignored) { - // empty - } - } - - private void warnCLIUserBeforeShutdown() { - if (isUsingTestHarness) { - long delayInSeconds = 30; - log.warn("All script actions have been completed. You have {} seconds to complete any" - + " remaining tasks before the test harness shuts down.", - delayInSeconds); - try { - SECONDS.sleep(delayInSeconds); - } catch (InterruptedException ignored) { - // empty - } - } else { - log.info("Shutting down test case"); - } - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/BotProtocol.java b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/BotProtocol.java deleted file mode 100644 index 4b84e607ebe..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/BotProtocol.java +++ /dev/null @@ -1,352 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot.protocol; - - -import bisq.proto.grpc.TradeInfo; - -import protobuf.PaymentAccount; - -import java.security.SecureRandom; - -import java.io.File; - -import java.util.Objects; -import java.util.concurrent.atomic.AtomicLong; -import java.util.function.Consumer; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.function.Supplier; -import java.util.stream.Collectors; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static bisq.apitest.scenario.bot.protocol.ProtocolStep.*; -import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled; -import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; -import static java.lang.String.format; -import static java.lang.System.currentTimeMillis; -import static java.util.Arrays.stream; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - - - -import bisq.apitest.method.BitcoinCliHelper; -import bisq.apitest.scenario.bot.BotClient; -import bisq.apitest.scenario.bot.script.BashScriptGenerator; -import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; -import bisq.cli.table.builder.TableBuilder; - -@Slf4j -public abstract class BotProtocol { - - static final SecureRandom RANDOM = new SecureRandom(); - static final String BUY = "BUY"; - static final String SELL = "SELL"; - - protected final Supplier randomDelay = () -> (long) (2000 + RANDOM.nextInt(5000)); - - protected final AtomicLong protocolStepStartTime = new AtomicLong(0); - protected final Consumer initProtocolStep = (step) -> { - currentProtocolStep = step; - printBotProtocolStep(); - protocolStepStartTime.set(currentTimeMillis()); - }; - - @Getter - protected ProtocolStep currentProtocolStep; - - @Getter // Functions within 'this' need the @Getter. - protected final BotClient botClient; - protected final PaymentAccount paymentAccount; - protected final String currencyCode; - protected final long protocolStepTimeLimitInMs; - protected final BitcoinCliHelper bitcoinCli; - @Getter - protected final BashScriptGenerator bashScriptGenerator; - - public BotProtocol(BotClient botClient, - PaymentAccount paymentAccount, - long protocolStepTimeLimitInMs, - BitcoinCliHelper bitcoinCli, - BashScriptGenerator bashScriptGenerator) { - this.botClient = botClient; - this.paymentAccount = paymentAccount; - this.currencyCode = Objects.requireNonNull(paymentAccount.getSelectedTradeCurrency()).getCode(); - this.protocolStepTimeLimitInMs = protocolStepTimeLimitInMs; - this.bitcoinCli = bitcoinCli; - this.bashScriptGenerator = bashScriptGenerator; - this.currentProtocolStep = START; - } - - public abstract void run(); - - protected boolean isWithinProtocolStepTimeLimit() { - return (currentTimeMillis() - protocolStepStartTime.get()) < protocolStepTimeLimitInMs; - } - - protected void checkIsStartStep() { - if (currentProtocolStep != START) { - throw new IllegalStateException("First bot protocol step must be " + START.name()); - } - } - - protected void printBotProtocolStep() { - log.info("Starting protocol step {}. Bot will shutdown if step not completed within {} minutes.", - currentProtocolStep.name(), MILLISECONDS.toMinutes(protocolStepTimeLimitInMs)); - - if (currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED)) { - log.info("Generate a btc block to trigger taker's deposit fee tx confirmation."); - createGenerateBtcBlockScript(); - } - } - - protected final Function waitForTakerFeeTxConfirm = (trade) -> { - sleep(5000); - waitForTakerFeeTxPublished(trade.getTradeId()); - waitForTakerFeeTxConfirmed(trade.getTradeId()); - return trade; - }; - - protected final Function waitForPaymentStartedMessage = (trade) -> { - initProtocolStep.accept(WAIT_FOR_PAYMENT_STARTED_MESSAGE); - try { - createPaymentStartedScript(trade); - log.info(" Waiting for a 'payment started' message from buyer for trade with id {}.", trade.getTradeId()); - while (isWithinProtocolStepTimeLimit()) { - checkIfShutdownCalled("Interrupted before checking if 'payment started' message has been sent."); - try { - var t = this.getBotClient().getTrade(trade.getTradeId()); - if (t.getIsPaymentStartedMessageSent()) { - log.info("Buyer has started payment for trade:\n{}", - new TableBuilder(TRADE_DETAIL_TBL, t).build().toString()); - return t; - } - } catch (Exception ex) { - throw new IllegalStateException(this.getBotClient().toCleanGrpcExceptionMessage(ex)); - } - sleep(randomDelay.get()); - } // end while - - throw new IllegalStateException("Payment was never sent; we won't wait any longer."); - } catch (ManualBotShutdownException ex) { - throw ex; // not an error, tells bot to shutdown - } catch (Exception ex) { - throw new IllegalStateException("Error while waiting payment sent message.", ex); - } - }; - - protected final Function sendPaymentStartedMessage = (trade) -> { - initProtocolStep.accept(SEND_PAYMENT_STARTED_MESSAGE); - checkIfShutdownCalled("Interrupted before sending 'payment started' message."); - this.getBotClient().sendConfirmPaymentStartedMessage(trade.getTradeId()); - return trade; - }; - - protected final Function waitForPaymentReceivedConfirmation = (trade) -> { - initProtocolStep.accept(WAIT_FOR_PAYMENT_RECEIVED_CONFIRMATION_MESSAGE); - createPaymentReceivedScript(trade); - try { - log.info("Waiting for a 'payment received confirmation' message from seller for trade with id {}.", trade.getTradeId()); - while (isWithinProtocolStepTimeLimit()) { - checkIfShutdownCalled("Interrupted before checking if 'payment received confirmation' message has been sent."); - try { - var t = this.getBotClient().getTrade(trade.getTradeId()); - if (t.getIsPaymentReceivedMessageSent()) { - log.info("Seller has received payment for trade:\n{}", - new TableBuilder(TRADE_DETAIL_TBL, t).build().toString()); - return t; - } - } catch (Exception ex) { - throw new IllegalStateException(this.getBotClient().toCleanGrpcExceptionMessage(ex)); - } - sleep(randomDelay.get()); - } // end while - - throw new IllegalStateException("Payment was never received; we won't wait any longer."); - } catch (ManualBotShutdownException ex) { - throw ex; // not an error, tells bot to shutdown - } catch (Exception ex) { - throw new IllegalStateException("Error while waiting payment received confirmation message.", ex); - } - }; - - protected final Function sendPaymentReceivedMessage = (trade) -> { - initProtocolStep.accept(SEND_PAYMENT_RECEIVED_CONFIRMATION_MESSAGE); - checkIfShutdownCalled("Interrupted before sending 'payment received confirmation' message."); - this.getBotClient().sendConfirmPaymentReceivedMessage(trade.getTradeId()); - return trade; - }; - - protected final Function waitForPayoutTx = (trade) -> { - initProtocolStep.accept(WAIT_FOR_PAYOUT_TX); - try { - log.info("Waiting on the 'payout tx published confirmation' for trade with id {}.", trade.getTradeId()); - while (isWithinProtocolStepTimeLimit()) { - checkIfShutdownCalled("Interrupted before checking if payout tx has been published."); - try { - var t = this.getBotClient().getTrade(trade.getTradeId()); - if (t.getIsPayoutPublished()) { - log.info("Payout tx {} has been published for trade:\n{}", - t.getPayoutTxId(), - new TableBuilder(TRADE_DETAIL_TBL, t).build().toString()); - return t; - } - } catch (Exception ex) { - throw new IllegalStateException(this.getBotClient().toCleanGrpcExceptionMessage(ex)); - } - sleep(randomDelay.get()); - } // end while - - throw new IllegalStateException("Payout tx was never published; we won't wait any longer."); - } catch (ManualBotShutdownException ex) { - throw ex; // not an error, tells bot to shutdown - } catch (Exception ex) { - throw new IllegalStateException("Error while waiting for published payout tx.", ex); - } - }; - - protected final Function closeTrade = (trade) -> { - initProtocolStep.accept(CLOSE_TRADE); - var isBuy = trade.getOffer().getDirection().equalsIgnoreCase(BUY); - var isSell = trade.getOffer().getDirection().equalsIgnoreCase(SELL); - var cliUserIsSeller = (this instanceof MakerBotProtocol && isBuy) || (this instanceof TakerBotProtocol && isSell); - if (cliUserIsSeller) { - createKeepFundsScript(trade); - } else { - createGetBalanceScript(); - } - checkIfShutdownCalled("Interrupted before closing trade with 'closetrade' command."); - this.getBotClient().sendCloseTradeMessage(trade.getTradeId()); - return trade; - }; - - protected void createPaymentStartedScript(TradeInfo trade) { - File script = bashScriptGenerator.createPaymentStartedScript(trade); - printCliHintAndOrScript(script, "The manual CLI side can send a 'payment started' message"); - } - - protected void createPaymentReceivedScript(TradeInfo trade) { - File script = bashScriptGenerator.createPaymentReceivedScript(trade); - printCliHintAndOrScript(script, "The manual CLI side can sent a 'payment received confirmation' message"); - } - - protected void createKeepFundsScript(TradeInfo trade) { - File script = bashScriptGenerator.createKeepFundsScript(trade); - printCliHintAndOrScript(script, "The manual CLI side can close the trade"); - } - - protected void createGetBalanceScript() { - File script = bashScriptGenerator.createGetBalanceScript(); - printCliHintAndOrScript(script, "The manual CLI side can view current balances"); - } - - protected void createGenerateBtcBlockScript() { - String newBitcoinCoreAddress = bitcoinCli.getNewBtcAddress(); - File script = bashScriptGenerator.createGenerateBtcBlockScript(newBitcoinCoreAddress); - printCliHintAndOrScript(script, "The manual CLI side can generate 1 btc block"); - } - - protected void printCliHintAndOrScript(File script, String hint) { - log.info("{} by running bash script '{}'.", hint, script.getAbsolutePath()); - if (this.getBashScriptGenerator().isPrintCliScripts()) - this.getBashScriptGenerator().printCliScript(script, log); - - sleep(5000); // Allow 5s for CLI user to read the hint. - } - - protected void sleep(long ms) { - try { - MILLISECONDS.sleep(ms); - } catch (InterruptedException ignored) { - // empty - } - } - - private void waitForTakerFeeTxPublished(String tradeId) { - waitForTakerDepositFee(tradeId, WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED); - } - - private void waitForTakerFeeTxConfirmed(String tradeId) { - waitForTakerDepositFee(tradeId, WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED); - } - - private void waitForTakerDepositFee(String tradeId, ProtocolStep depositTxProtocolStep) { - initProtocolStep.accept(depositTxProtocolStep); - validateCurrentProtocolStep(WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED, WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED); - try { - log.info(waitingForDepositFeeTxMsg(tradeId)); - while (isWithinProtocolStepTimeLimit()) { - checkIfShutdownCalled("Interrupted before checking taker deposit fee tx is published and confirmed."); - try { - var trade = this.getBotClient().getTrade(tradeId); - if (isDepositFeeTxStepComplete.test(trade)) - return; - else - sleep(randomDelay.get()); - } catch (Exception ex) { - if (this.getBotClient().tradeContractIsNotReady.test(ex, tradeId)) - sleep(randomDelay.get()); - else - throw new IllegalStateException(this.getBotClient().toCleanGrpcExceptionMessage(ex)); - } - } // end while - throw new IllegalStateException(stoppedWaitingForDepositFeeTxMsg(this.getBotClient().getTrade(tradeId).getDepositTxId())); - } catch (ManualBotShutdownException ex) { - throw ex; // not an error, tells bot to shutdown - } catch (Exception ex) { - throw new IllegalStateException("Error while waiting for taker deposit tx to be published or confirmed.", ex); - } - } - - private final Predicate isDepositFeeTxStepComplete = (trade) -> { - if (currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED) && trade.getIsDepositPublished()) { - log.info("Taker deposit fee tx {} has been published.", trade.getDepositTxId()); - return true; - } else if (currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED) && trade.getIsDepositConfirmed()) { - log.info("Taker deposit fee tx {} has been confirmed.", trade.getDepositTxId()); - return true; - } else { - return false; - } - }; - - private void validateCurrentProtocolStep(Enum... validBotSteps) { - for (Enum validBotStep : validBotSteps) { - if (currentProtocolStep.equals(validBotStep)) - return; - } - throw new IllegalStateException("Unexpected bot step: " + currentProtocolStep.name() + ".\n" - + "Must be one of " - + stream(validBotSteps).map((Enum::name)).collect(Collectors.joining(",")) - + "."); - } - - private String waitingForDepositFeeTxMsg(String tradeId) { - return format("Waiting for taker deposit fee tx for trade %s to be %s.", - tradeId, - currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED) ? "published" : "confirmed"); - } - - private String stoppedWaitingForDepositFeeTxMsg(String txId) { - return format("Taker deposit fee tx %s is took too long to be %s; we won't wait any longer.", - txId, - currentProtocolStep.equals(WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED) ? "published" : "confirmed"); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/MakerBotProtocol.java b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/MakerBotProtocol.java deleted file mode 100644 index a5ce8f5bcaf..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/MakerBotProtocol.java +++ /dev/null @@ -1,116 +0,0 @@ -package bisq.apitest.scenario.bot.protocol; - -import bisq.proto.grpc.OfferInfo; -import bisq.proto.grpc.TradeInfo; - -import protobuf.PaymentAccount; - -import java.io.File; - -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Supplier; - -import lombok.extern.slf4j.Slf4j; - -import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE; -import static bisq.apitest.scenario.bot.protocol.ProtocolStep.WAIT_FOR_OFFER_TAKER; -import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled; -import static bisq.cli.table.builder.TableType.OFFER_TBL; -import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; - - - -import bisq.apitest.method.BitcoinCliHelper; -import bisq.apitest.scenario.bot.BotClient; -import bisq.apitest.scenario.bot.RandomOffer; -import bisq.apitest.scenario.bot.script.BashScriptGenerator; -import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; -import bisq.cli.table.builder.TableBuilder; - -@Slf4j -public class MakerBotProtocol extends BotProtocol { - - public MakerBotProtocol(BotClient botClient, - PaymentAccount paymentAccount, - long protocolStepTimeLimitInMs, - BitcoinCliHelper bitcoinCli, - BashScriptGenerator bashScriptGenerator) { - super(botClient, - paymentAccount, - protocolStepTimeLimitInMs, - bitcoinCli, - bashScriptGenerator); - } - - @Override - public void run() { - checkIsStartStep(); - - Function, TradeInfo> makeTrade = waitForNewTrade.andThen(waitForTakerFeeTxConfirm); - var trade = makeTrade.apply(randomOffer); - - var makerIsBuyer = trade.getOffer().getDirection().equalsIgnoreCase(BUY); - Function completeFiatTransaction = makerIsBuyer - ? sendPaymentStartedMessage.andThen(waitForPaymentReceivedConfirmation) - : waitForPaymentStartedMessage.andThen(sendPaymentReceivedMessage); - completeFiatTransaction.apply(trade); - - Function closeTrade = waitForPayoutTx.andThen(this.closeTrade); - closeTrade.apply(trade); - - currentProtocolStep = DONE; - } - - private final Supplier randomOffer = () -> { - checkIfShutdownCalled("Interrupted before creating random offer."); - OfferInfo offer = new RandomOffer(botClient, paymentAccount).create().getOffer(); - log.info("Created random {} offer\n{}", currencyCode, new TableBuilder(OFFER_TBL, offer).build()); - return offer; - }; - - private final Function, TradeInfo> waitForNewTrade = (randomOffer) -> { - initProtocolStep.accept(WAIT_FOR_OFFER_TAKER); - OfferInfo offer = randomOffer.get(); - createTakeOfferCliScript(offer); - try { - log.info("Impatiently waiting for offer {} to be taken, repeatedly calling gettrade.", offer.getId()); - while (isWithinProtocolStepTimeLimit()) { - checkIfShutdownCalled("Interrupted while waiting for offer to be taken."); - try { - var trade = getNewTrade(offer.getId()); - if (trade.isPresent()) - return trade.get(); - else - sleep(randomDelay.get()); - } catch (Exception ex) { - throw new IllegalStateException(this.getBotClient().toCleanGrpcExceptionMessage(ex), ex); - } - } // end while - throw new IllegalStateException("Offer was never taken; we won't wait any longer."); - } catch (ManualBotShutdownException ex) { - throw ex; // not an error, tells bot to shutdown - } catch (Exception ex) { - throw new IllegalStateException("Error while waiting for offer to be taken.", ex); - } - }; - - private Optional getNewTrade(String offerId) { - try { - var trade = botClient.getTrade(offerId); - log.info("Offer {} was taken, new trade:\n{}", - offerId, - new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString()); - return Optional.of(trade); - } catch (Exception ex) { - // Get trade will throw a non-fatal gRPC exception if not found. - log.info(this.getBotClient().toCleanGrpcExceptionMessage(ex)); - return Optional.empty(); - } - } - - private void createTakeOfferCliScript(OfferInfo offer) { - File script = bashScriptGenerator.createTakeOfferScript(offer); - printCliHintAndOrScript(script, "The manual CLI side can take the offer"); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/ProtocolStep.java b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/ProtocolStep.java deleted file mode 100644 index 2c8c8cd07f7..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/ProtocolStep.java +++ /dev/null @@ -1,17 +0,0 @@ -package bisq.apitest.scenario.bot.protocol; - -public enum ProtocolStep { - START, - FIND_OFFER, - TAKE_OFFER, - WAIT_FOR_OFFER_TAKER, - WAIT_FOR_TAKER_DEPOSIT_TX_PUBLISHED, - WAIT_FOR_TAKER_DEPOSIT_TX_CONFIRMED, - SEND_PAYMENT_STARTED_MESSAGE, - WAIT_FOR_PAYMENT_STARTED_MESSAGE, - SEND_PAYMENT_RECEIVED_CONFIRMATION_MESSAGE, - WAIT_FOR_PAYMENT_RECEIVED_CONFIRMATION_MESSAGE, - WAIT_FOR_PAYOUT_TX, - CLOSE_TRADE, - DONE -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java b/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java deleted file mode 100644 index a0aba127d66..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/protocol/TakerBotProtocol.java +++ /dev/null @@ -1,137 +0,0 @@ -package bisq.apitest.scenario.bot.protocol; - -import bisq.proto.grpc.OfferInfo; -import bisq.proto.grpc.TradeInfo; - -import protobuf.PaymentAccount; - -import java.io.File; - -import java.util.Optional; -import java.util.function.Function; -import java.util.function.Supplier; - -import lombok.extern.slf4j.Slf4j; - -import static bisq.apitest.scenario.bot.protocol.ProtocolStep.DONE; -import static bisq.apitest.scenario.bot.protocol.ProtocolStep.FIND_OFFER; -import static bisq.apitest.scenario.bot.protocol.ProtocolStep.TAKE_OFFER; -import static bisq.apitest.scenario.bot.shutdown.ManualShutdown.checkIfShutdownCalled; -import static bisq.cli.table.builder.TableType.OFFER_TBL; -import static bisq.core.payment.payload.PaymentMethod.F2F_ID; - - - -import bisq.apitest.method.BitcoinCliHelper; -import bisq.apitest.scenario.bot.BotClient; -import bisq.apitest.scenario.bot.script.BashScriptGenerator; -import bisq.apitest.scenario.bot.shutdown.ManualBotShutdownException; -import bisq.cli.table.builder.TableBuilder; - -@Slf4j -public class TakerBotProtocol extends BotProtocol { - - public TakerBotProtocol(BotClient botClient, - PaymentAccount paymentAccount, - long protocolStepTimeLimitInMs, - BitcoinCliHelper bitcoinCli, - BashScriptGenerator bashScriptGenerator) { - super(botClient, - paymentAccount, - protocolStepTimeLimitInMs, - bitcoinCli, - bashScriptGenerator); - } - - @Override - public void run() { - checkIsStartStep(); - - Function takeTrade = takeOffer.andThen(waitForTakerFeeTxConfirm); - var trade = takeTrade.apply(findOffer.get()); - - var takerIsSeller = trade.getOffer().getDirection().equalsIgnoreCase(BUY); - Function completeFiatTransaction = takerIsSeller - ? waitForPaymentStartedMessage.andThen(sendPaymentReceivedMessage) - : sendPaymentStartedMessage.andThen(waitForPaymentReceivedConfirmation); - completeFiatTransaction.apply(trade); - - Function closeTrade = waitForPayoutTx.andThen(this.closeTrade); - closeTrade.apply(trade); - - currentProtocolStep = DONE; - } - - private final Supplier> firstOffer = () -> { - var offers = botClient.getOffers(currencyCode); - if (offers.size() > 0) { - log.info("Offers found:\n{}", new TableBuilder(OFFER_TBL, offers).build()); - OfferInfo offer = offers.get(0); - log.info("Will take first offer {}", offer.getId()); - return Optional.of(offer); - } else { - log.info("No buy or sell {} offers found.", currencyCode); - return Optional.empty(); - } - }; - - private final Supplier findOffer = () -> { - initProtocolStep.accept(FIND_OFFER); - createMakeOfferScript(); - try { - log.info("Impatiently waiting for at least one {} offer to be created, repeatedly calling getoffers.", currencyCode); - while (isWithinProtocolStepTimeLimit()) { - checkIfShutdownCalled("Interrupted while checking offers."); - try { - Optional offer = firstOffer.get(); - if (offer.isPresent()) - return offer.get(); - else - sleep(randomDelay.get()); - } catch (Exception ex) { - throw new IllegalStateException(this.getBotClient().toCleanGrpcExceptionMessage(ex), ex); - } - } // end while - throw new IllegalStateException("Offer was never created; we won't wait any longer."); - } catch (ManualBotShutdownException ex) { - throw ex; // not an error, tells bot to shutdown - } catch (Exception ex) { - throw new IllegalStateException("Error while waiting for a new offer.", ex); - } - }; - - private final Function takeOffer = (offer) -> { - initProtocolStep.accept(TAKE_OFFER); - checkIfShutdownCalled("Interrupted before taking offer."); - String feeCurrency = RANDOM.nextBoolean() ? "BSQ" : "BTC"; - return botClient.takeOffer(offer.getId(), paymentAccount, feeCurrency); - }; - - private void createMakeOfferScript() { - String direction = RANDOM.nextBoolean() ? "BUY" : "SELL"; - String feeCurrency = RANDOM.nextBoolean() ? "BSQ" : "BTC"; - boolean createMarginPricedOffer = RANDOM.nextBoolean(); - // If not using an F2F account, don't go over possible 0.01 BTC - // limit if account is not signed. - String amount = paymentAccount.getPaymentMethod().getId().equals(F2F_ID) - ? "0.25" - : "0.01"; - File script; - if (createMarginPricedOffer) { - script = bashScriptGenerator.createMakeMarginPricedOfferScript(direction, - currencyCode, - amount, - "0.0", - "15.0", - feeCurrency); - } else { - script = bashScriptGenerator.createMakeFixedPricedOfferScript(direction, - currencyCode, - amount, - botClient.getCurrentBTCMarketPriceAsIntegerString(currencyCode), - "15.0", - feeCurrency); - } - printCliHintAndOrScript(script, "The manual CLI side can create an offer"); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/script/BashScriptGenerator.java b/apitest/src/test/java/bisq/apitest/scenario/bot/script/BashScriptGenerator.java deleted file mode 100644 index 4c6b73f8084..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/script/BashScriptGenerator.java +++ /dev/null @@ -1,235 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot.script; - -import bisq.common.file.FileUtil; - -import bisq.proto.grpc.OfferInfo; -import bisq.proto.grpc.TradeInfo; - -import com.google.common.io.Files; - -import java.nio.file.Paths; - -import java.io.File; -import java.io.IOException; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -import lombok.Getter; -import lombok.extern.slf4j.Slf4j; - -import static com.google.common.io.FileWriteMode.APPEND; -import static java.lang.String.format; -import static java.lang.System.getProperty; -import static java.nio.charset.StandardCharsets.UTF_8; -import static java.nio.file.Files.readAllBytes; - -@Slf4j -@Getter -public class BashScriptGenerator { - - private final int apiPort; - private final String apiPassword; - private final String paymentAccountId; - private final String cliBase; - private final boolean printCliScripts; - - public BashScriptGenerator(String apiPassword, - int apiPort, - String paymentAccountId, - boolean printCliScripts) { - this.apiPassword = apiPassword; - this.apiPort = apiPort; - this.paymentAccountId = paymentAccountId; - this.printCliScripts = printCliScripts; - this.cliBase = format("./bisq-cli --password=%s --port=%d", apiPassword, apiPort); - } - - public File createMakeMarginPricedOfferScript(String direction, - String currencyCode, - String amount, - String marketPriceMargin, - String securityDeposit, - String feeCurrency) { - String makeOfferCmd = format("%s createoffer --payment-account=%s " - + " --direction=%s" - + " --currency-code=%s" - + " --amount=%s" - + " --market-price-margin=%s" - + " --security-deposit=%s" - + " --fee-currency=%s", - cliBase, - this.getPaymentAccountId(), - direction, - currencyCode, - amount, - marketPriceMargin, - securityDeposit, - feeCurrency); - String getOffersCmd = format("%s getmyoffers --direction=%s --currency-code=%s", - cliBase, - direction, - currencyCode); - return createCliScript("createoffer.sh", - makeOfferCmd, - "sleep 2", - getOffersCmd); - } - - public File createMakeFixedPricedOfferScript(String direction, - String currencyCode, - String amount, - String fixedPrice, - String securityDeposit, - String feeCurrency) { - String makeOfferCmd = format("%s createoffer --payment-account=%s " - + " --direction=%s" - + " --currency-code=%s" - + " --amount=%s" - + " --fixed-price=%s" - + " --security-deposit=%s" - + " --fee-currency=%s", - cliBase, - this.getPaymentAccountId(), - direction, - currencyCode, - amount, - fixedPrice, - securityDeposit, - feeCurrency); - String getOffersCmd = format("%s getmyoffers --direction=%s --currency-code=%s", - cliBase, - direction, - currencyCode); - return createCliScript("createoffer.sh", - makeOfferCmd, - "sleep 2", - getOffersCmd); - } - - public File createTakeOfferScript(OfferInfo offer) { - String getOffersCmd = format("%s getoffers --direction=%s --currency-code=%s", - cliBase, - offer.getDirection(), - offer.getCounterCurrencyCode()); - String takeOfferCmd = format("%s takeoffer --offer-id=%s --payment-account=%s --fee-currency=BSQ", - cliBase, - offer.getId(), - this.getPaymentAccountId()); - String getTradeCmd = format("%s gettrade --trade-id=%s", - cliBase, - offer.getId()); - return createCliScript("takeoffer.sh", - getOffersCmd, - takeOfferCmd, - "sleep 5", - getTradeCmd); - } - - public File createPaymentStartedScript(TradeInfo trade) { - String paymentStartedCmd = format("%s confirmpaymentstarted --trade-id=%s", - cliBase, - trade.getTradeId()); - String getTradeCmd = format("%s gettrade --trade-id=%s", cliBase, trade.getTradeId()); - return createCliScript("confirmpaymentstarted.sh", - paymentStartedCmd, - "sleep 2", - getTradeCmd); - } - - public File createPaymentReceivedScript(TradeInfo trade) { - String paymentStartedCmd = format("%s confirmpaymentreceived --trade-id=%s", - cliBase, - trade.getTradeId()); - String getTradeCmd = format("%s gettrade --trade-id=%s", cliBase, trade.getTradeId()); - return createCliScript("confirmpaymentreceived.sh", - paymentStartedCmd, - "sleep 2", - getTradeCmd); - } - - public File createKeepFundsScript(TradeInfo trade) { - String paymentStartedCmd = format("%s closetrade --trade-id=%s", cliBase, trade.getTradeId()); - String getTradeCmd = format("%s gettrade --trade-id=%s", cliBase, trade.getTradeId()); - String getBalanceCmd = format("%s getbalance", cliBase); - return createCliScript("closetrade.sh", - paymentStartedCmd, - "sleep 2", - getTradeCmd, - getBalanceCmd); - } - - public File createGetBalanceScript() { - String getBalanceCmd = format("%s getbalance", cliBase); - return createCliScript("getbalance.sh", getBalanceCmd); - } - - public File createGenerateBtcBlockScript(String address) { - String bitcoinCliCmd = format("bitcoin-cli -regtest -rpcport=19443 -rpcuser=apitest" - + " -rpcpassword=apitest generatetoaddress 1 \"%s\"", - address); - return createCliScript("genbtcblk.sh", - bitcoinCliCmd); - } - - public File createCliScript(String scriptName, String... commands) { - String filename = getProperty("java.io.tmpdir") + File.separator + scriptName; - File oldScript = new File(filename); - if (oldScript.exists()) { - try { - FileUtil.deleteFileIfExists(oldScript); - } catch (IOException ex) { - throw new IllegalStateException("Unable to delete old script.", ex); - } - } - File script = new File(filename); - try { - List lines = new ArrayList<>(); - lines.add("#!/bin/bash"); - lines.add("############################################################"); - lines.add("# This example CLI script may be overwritten during the test"); - lines.add("# run, and will be deleted when the test harness shuts down."); - lines.add("# Make a copy if you want to save it."); - lines.add("############################################################"); - lines.add("set -x"); - Collections.addAll(lines, commands); - Files.asCharSink(script, UTF_8, APPEND).writeLines(lines); - if (!script.setExecutable(true)) - throw new IllegalStateException("Unable to set script owner's execute permission."); - } catch (IOException ex) { - log.error("", ex); - throw new IllegalStateException(ex); - } finally { - script.deleteOnExit(); - } - return script; - } - - public void printCliScript(File cliScript, - org.slf4j.Logger logger) { - try { - String contents = new String(readAllBytes(Paths.get(cliScript.getPath()))); - logger.info("CLI script {}:\n{}", cliScript.getAbsolutePath(), contents); - } catch (IOException ex) { - throw new IllegalStateException("Error reading CLI script contents.", ex); - } - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/script/BotScript.java b/apitest/src/test/java/bisq/apitest/scenario/bot/script/BotScript.java deleted file mode 100644 index 2caaed68add..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/script/BotScript.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot.script; - -import lombok.Getter; -import lombok.Setter; -import lombok.ToString; - -import javax.annotation.Nullable; - -@Getter -@ToString -public -class BotScript { - - // Common, default is true. - private final boolean useTestHarness; - - // Used only with test harness. Mutually exclusive, but if both are not null, - // the botPaymentMethodId takes precedence over countryCode. - @Nullable - private final String botPaymentMethodId; - @Nullable - private final String countryCode; - - // Used only without test harness. - @Nullable - @Setter - private String paymentAccountIdForBot; - @Nullable - @Setter - private String paymentAccountIdForCliScripts; - - // Common, used with or without test harness. - private final int apiPortForCliScripts; - private final String[] actions; - private final long protocolStepTimeLimitInMinutes; - private final boolean printCliScripts; - private final boolean stayAlive; - - @SuppressWarnings("NullableProblems") - BotScript(boolean useTestHarness, - String botPaymentMethodId, - String countryCode, - String paymentAccountIdForBot, - String paymentAccountIdForCliScripts, - String[] actions, - int apiPortForCliScripts, - long protocolStepTimeLimitInMinutes, - boolean printCliScripts, - boolean stayAlive) { - this.useTestHarness = useTestHarness; - this.botPaymentMethodId = botPaymentMethodId; - this.countryCode = countryCode != null ? countryCode.toUpperCase() : null; - this.paymentAccountIdForBot = paymentAccountIdForBot; - this.paymentAccountIdForCliScripts = paymentAccountIdForCliScripts; - this.apiPortForCliScripts = apiPortForCliScripts; - this.actions = actions; - this.protocolStepTimeLimitInMinutes = protocolStepTimeLimitInMinutes; - this.printCliScripts = printCliScripts; - this.stayAlive = stayAlive; - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/script/BotScriptGenerator.java b/apitest/src/test/java/bisq/apitest/scenario/bot/script/BotScriptGenerator.java deleted file mode 100644 index 9d62799e9e3..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/script/BotScriptGenerator.java +++ /dev/null @@ -1,248 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot.script; - -import bisq.core.util.JsonUtil; - -import bisq.common.file.JsonFileManager; - -import joptsimple.BuiltinHelpFormatter; -import joptsimple.OptionParser; -import joptsimple.OptionSet; -import joptsimple.OptionSpec; - -import java.io.File; -import java.io.IOException; -import java.io.PrintStream; - -import lombok.extern.slf4j.Slf4j; - -import javax.annotation.Nullable; - -import static java.lang.System.err; -import static java.lang.System.exit; -import static java.lang.System.getProperty; -import static java.lang.System.out; - -@Slf4j -public class BotScriptGenerator { - - private final boolean useTestHarness; - @Nullable - private final String countryCode; - @Nullable - private final String botPaymentMethodId; - @Nullable - private final String paymentAccountIdForBot; - @Nullable - private final String paymentAccountIdForCliScripts; - private final int apiPortForCliScripts; - private final String actions; - private final int protocolStepTimeLimitInMinutes; - private final boolean printCliScripts; - private final boolean stayAlive; - - public BotScriptGenerator(String[] args) { - OptionParser parser = new OptionParser(); - var helpOpt = parser.accepts("help", "Print this help text.") - .forHelp(); - OptionSpec useTestHarnessOpt = parser - .accepts("use-testharness", "Use the test harness, or manually start your own nodes.") - .withRequiredArg() - .ofType(Boolean.class) - .defaultsTo(true); - OptionSpec actionsOpt = parser - .accepts("actions", "A comma delimited list with no spaces, e.g., make,take,take,make,...") - .withRequiredArg(); - OptionSpec botPaymentMethodIdOpt = parser - .accepts("bot-payment-method", - "The bot's (Bob) payment method id. If using the test harness," - + " the id will be used to automatically create a payment account.") - .withRequiredArg(); - OptionSpec countryCodeOpt = parser - .accepts("country-code", - "The two letter country-code for an F2F payment account if using the test harness," - + " but the bot-payment-method option takes precedence.") - .withRequiredArg(); - OptionSpec apiPortForCliScriptsOpt = parser - .accepts("api-port-for-cli-scripts", - "The api port used in bot generated bash/cli scripts.") - .withRequiredArg() - .ofType(Integer.class) - .defaultsTo(9998); - OptionSpec paymentAccountIdForBotOpt = parser - .accepts("payment-account-for-bot", - "The bot side's payment account id, when the test harness is not used," - + " and Bob & Alice accounts are not automatically created.") - .withRequiredArg(); - OptionSpec paymentAccountIdForCliScriptsOpt = parser - .accepts("payment-account-for-cli-scripts", - "The other side's payment account id, used in generated bash/cli scripts when" - + " the test harness is not used, and Bob & Alice accounts are not automatically created.") - .withRequiredArg(); - OptionSpec protocolStepTimeLimitInMinutesOpt = parser - .accepts("step-time-limit", "Each protocol step's time limit in minutes") - .withRequiredArg() - .ofType(Integer.class) - .defaultsTo(60); - OptionSpec printCliScriptsOpt = parser - .accepts("print-cli-scripts", "Print the generated CLI scripts from bot") - .withRequiredArg() - .ofType(Boolean.class) - .defaultsTo(false); - OptionSpec stayAliveOpt = parser - .accepts("stay-alive", "Leave test harness nodes running after the last action.") - .withRequiredArg() - .ofType(Boolean.class) - .defaultsTo(true); - OptionSet options = parser.parse(args); - - if (options.has(helpOpt)) { - printHelp(parser, out); - exit(0); - } - - if (!options.has(actionsOpt)) { - printHelp(parser, err); - exit(1); - } - - this.useTestHarness = options.has(useTestHarnessOpt) ? options.valueOf(useTestHarnessOpt) : true; - this.actions = options.valueOf(actionsOpt); - this.apiPortForCliScripts = options.has(apiPortForCliScriptsOpt) ? options.valueOf(apiPortForCliScriptsOpt) : 9998; - this.botPaymentMethodId = options.has(botPaymentMethodIdOpt) ? options.valueOf(botPaymentMethodIdOpt) : null; - this.countryCode = options.has(countryCodeOpt) ? options.valueOf(countryCodeOpt) : null; - this.paymentAccountIdForBot = options.has(paymentAccountIdForBotOpt) ? options.valueOf(paymentAccountIdForBotOpt) : null; - this.paymentAccountIdForCliScripts = options.has(paymentAccountIdForCliScriptsOpt) ? options.valueOf(paymentAccountIdForCliScriptsOpt) : null; - this.protocolStepTimeLimitInMinutes = options.valueOf(protocolStepTimeLimitInMinutesOpt); - this.printCliScripts = options.valueOf(printCliScriptsOpt); - this.stayAlive = options.valueOf(stayAliveOpt); - - var noPaymentAccountCountryOrMethodForTestHarness = useTestHarness && - (!options.has(countryCodeOpt) && !options.has(botPaymentMethodIdOpt)); - if (noPaymentAccountCountryOrMethodForTestHarness) { - log.error("When running the test harness, payment accounts are automatically generated,"); - log.error("and you must provide one of the following options:"); - log.error(" \t\t(1) --bot-payment-method= OR"); - log.error(" \t\t(2) --country-code="); - log.error("If the bot-payment-method option is not present, the bot will create" - + " a country based F2F account using the country-code."); - log.error("If both are present, the bot-payment-method will take precedence. " - + "Currently, only the CLEAR_X_CHANGE_ID bot-payment-method is supported."); - printHelp(parser, err); - exit(1); - } - - var noPaymentAccountIdOrApiPortForCliScripts = !useTestHarness && - (!options.has(paymentAccountIdForCliScriptsOpt) || !options.has(paymentAccountIdForBotOpt)); - if (noPaymentAccountIdOrApiPortForCliScripts) { - log.error("If not running the test harness, payment accounts are not automatically generated,"); - log.error("and you must provide three options:"); - log.error(" \t\t(1) --api-port-for-cli-scripts="); - log.error(" \t\t(2) --payment-account-for-bot="); - log.error(" \t\t(3) --payment-account-for-cli-scripts="); - log.error("These will be used by the bot and in CLI scripts the bot will generate when creating an offer."); - printHelp(parser, err); - exit(1); - } - } - - private void printHelp(OptionParser parser, PrintStream stream) { - try { - String usage = "Examples\n--------\n" - + examplesUsingTestHarness() - + examplesNotUsingTestHarness(); - stream.println(); - parser.formatHelpWith(new HelpFormatter()); - parser.printHelpOn(stream); - stream.println(); - stream.println(usage); - stream.println(); - } catch (IOException ex) { - log.error("", ex); - } - } - - private String examplesUsingTestHarness() { - @SuppressWarnings("StringBufferReplaceableByString") StringBuilder builder = new StringBuilder(); - builder.append("To generate a bot-script.json file that will start the test harness,"); - builder.append(" create F2F accounts for Bob and Alice,"); - builder.append(" and take an offer created by Alice's CLI:").append("\n"); - builder.append("\tUsage: BotScriptGenerator").append("\n"); - builder.append("\t\t").append("--use-testharness=true").append("\n"); - builder.append("\t\t").append("--country-code=").append("\n"); - builder.append("\t\t").append("--actions=take").append("\n"); - builder.append("\n"); - builder.append("To generate a bot-script.json file that will start the test harness,"); - builder.append(" create Zelle accounts for Bob and Alice,"); - builder.append(" and create an offer to be taken by Alice's CLI:").append("\n"); - builder.append("\tUsage: BotScriptGenerator").append("\n"); - builder.append("\t\t").append("--use-testharness=true").append("\n"); - builder.append("\t\t").append("--bot-payment-method=CLEAR_X_CHANGE").append("\n"); - builder.append("\t\t").append("--actions=make").append("\n"); - builder.append("\n"); - return builder.toString(); - } - - private String examplesNotUsingTestHarness() { - @SuppressWarnings("StringBufferReplaceableByString") StringBuilder builder = new StringBuilder(); - builder.append("To generate a bot-script.json file that will not start the test harness,"); - builder.append(" but will create useful bash scripts for the CLI user,"); - builder.append(" and make two offers, then take two offers:").append("\n"); - builder.append("\tUsage: BotScriptGenerator").append("\n"); - builder.append("\t\t").append("--use-testharness=false").append("\n"); - builder.append("\t\t").append("--api-port-for-cli-scripts=").append("\n"); - builder.append("\t\t").append("--payment-account-for-bot=").append("\n"); - builder.append("\t\t").append("--payment-account-for-cli-scripts=").append("\n"); - builder.append("\t\t").append("--actions=make,make,take,take").append("\n"); - builder.append("\n"); - return builder.toString(); - } - - private String generateBotScriptTemplate() { - return JsonUtil.objectToJson(new BotScript( - useTestHarness, - botPaymentMethodId, - countryCode, - paymentAccountIdForBot, - paymentAccountIdForCliScripts, - actions.split("\\s*,\\s*").clone(), - apiPortForCliScripts, - protocolStepTimeLimitInMinutes, - printCliScripts, - stayAlive)); - } - - public static void main(String[] args) { - BotScriptGenerator generator = new BotScriptGenerator(args); - String json = generator.generateBotScriptTemplate(); - String destDir = getProperty("java.io.tmpdir"); - JsonFileManager jsonFileManager = new JsonFileManager(new File(destDir)); - jsonFileManager.writeToDisc(json, "bot-script"); - JsonFileManager.shutDownAllInstances(); - log.info("Saved {}/bot-script.json", destDir); - log.info("bot-script.json contents\n{}", json); - } - - // Makes a formatter with a given overall row width of 120 and column separator width of 2. - private static class HelpFormatter extends BuiltinHelpFormatter { - public HelpFormatter() { - super(120, 2); - } - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/shutdown/ManualBotShutdownException.java b/apitest/src/test/java/bisq/apitest/scenario/bot/shutdown/ManualBotShutdownException.java deleted file mode 100644 index 8a0e68bad18..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/shutdown/ManualBotShutdownException.java +++ /dev/null @@ -1,35 +0,0 @@ -/* - * This file is part of Bisq. - * - * Bisq is free software: you can redistribute it and/or modify it - * under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or (at - * your option) any later version. - * - * Bisq is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public - * License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with Bisq. If not, see . - */ - -package bisq.apitest.scenario.bot.shutdown; - -import bisq.common.BisqException; - -@SuppressWarnings("unused") -public class ManualBotShutdownException extends BisqException { - public ManualBotShutdownException(Throwable cause) { - super(cause); - } - - public ManualBotShutdownException(String format, Object... args) { - super(format, args); - } - - public ManualBotShutdownException(Throwable cause, String format, Object... args) { - super(cause, format, args); - } -} diff --git a/apitest/src/test/java/bisq/apitest/scenario/bot/shutdown/ManualShutdown.java b/apitest/src/test/java/bisq/apitest/scenario/bot/shutdown/ManualShutdown.java deleted file mode 100644 index fc680f1c818..00000000000 --- a/apitest/src/test/java/bisq/apitest/scenario/bot/shutdown/ManualShutdown.java +++ /dev/null @@ -1,64 +0,0 @@ -package bisq.apitest.scenario.bot.shutdown; - -import bisq.common.UserThread; - -import java.io.File; -import java.io.IOException; - -import java.util.concurrent.atomic.AtomicBoolean; - -import lombok.extern.slf4j.Slf4j; - -import static bisq.common.file.FileUtil.deleteFileIfExists; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -@Slf4j -public class ManualShutdown { - - public static final String SHUTDOWN_FILENAME = "/tmp/bottest-shutdown"; - - private static final AtomicBoolean SHUTDOWN_CALLED = new AtomicBoolean(false); - - /** - * Looks for a /tmp/bottest-shutdown file and throws a BotShutdownException if found. - * - * Running '$ touch /tmp/bottest-shutdown' could be used to trigger a scaffold teardown. - * - * This is much easier than manually shutdown down bisq apps & bitcoind. - */ - public static void startShutdownTimer() { - deleteStaleShutdownFile(); - - UserThread.runPeriodically(() -> { - File shutdownFile = new File(SHUTDOWN_FILENAME); - if (shutdownFile.exists()) { - log.warn("Caught manual shutdown signal: /tmp/bottest-shutdown file exists."); - try { - deleteFileIfExists(shutdownFile); - } catch (IOException ex) { - log.error("", ex); - throw new IllegalStateException(ex); - } - SHUTDOWN_CALLED.set(true); - } - }, 2000, MILLISECONDS); - } - - public static boolean isShutdownCalled() { - return SHUTDOWN_CALLED.get(); - } - - public static void checkIfShutdownCalled(String warning) throws ManualBotShutdownException { - if (isShutdownCalled()) - throw new ManualBotShutdownException(warning); - } - - private static void deleteStaleShutdownFile() { - try { - deleteFileIfExists(new File(SHUTDOWN_FILENAME)); - } catch (IOException ex) { - log.error("", ex); - throw new IllegalStateException(ex); - } - } -}