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);
- }
- }
-}