Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adjust api to new minimum fee per vbyte #5238

Merged
merged 10 commits into from
Feb 26, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import bisq.core.api.model.TxFeeRateInfo;

import io.grpc.StatusRuntimeException;

import lombok.extern.slf4j.Slf4j;

import org.junit.jupiter.api.AfterAll;
Expand All @@ -15,8 +17,11 @@
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.seednode;
import static bisq.common.config.BaseCurrencyNetwork.BTC_DAO_REGTEST;
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.MethodOrderer.OrderAnnotation;

Expand Down Expand Up @@ -50,17 +55,28 @@ public void testGetTxFeeRate(final TestInfo testInfo) {

@Test
@Order(2)
public void testSetTxFeeRate(final TestInfo testInfo) {
TxFeeRateInfo txFeeRateInfo = setTxFeeRate(alicedaemon, 10);
public void testSetInvalidTxFeeRateShouldThrowException(final TestInfo testInfo) {
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
setTxFeeRate(alicedaemon, 10));
String expectedExceptionMessage =
format("UNKNOWN: tx fee rate preference must be >= %d sats/byte",
BTC_DAO_REGTEST.getDefaultMinFeePerVbyte());
assertEquals(expectedExceptionMessage, exception.getMessage());
}

@Test
@Order(3)
public void testSetValidTxFeeRate(final TestInfo testInfo) {
TxFeeRateInfo txFeeRateInfo = setTxFeeRate(alicedaemon, 15);
log.debug("{} -> Fee rates with custom preference: {}", testName(testInfo), txFeeRateInfo);

assertTrue(txFeeRateInfo.isUseCustomTxFeeRate());
assertEquals(10, txFeeRateInfo.getCustomTxFeeRate());
assertEquals(15, txFeeRateInfo.getCustomTxFeeRate());
assertTrue(txFeeRateInfo.getFeeServiceRate() > 0);
}

@Test
@Order(3)
@Order(4)
public void testUnsetTxFeeRate(final TestInfo testInfo) {
TxFeeRateInfo txFeeRateInfo = unsetTxFeeRate(alicedaemon);
log.debug("{} -> Fee rate with no preference: {}", testName(testInfo), txFeeRateInfo);
Expand Down
126 changes: 126 additions & 0 deletions apitest/src/test/java/bisq/apitest/scenario/ScriptedBotTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

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 java.net.InetAddress.getLoopbackAddress;
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;
import bisq.cli.GrpcStubs;

// 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);
bobStubs = new GrpcStubs(getLoopbackAddress().getHostAddress(),
bobdaemon.apiPort,
config.apiPassword);
log.warn("Don't forget to register dispute agents before trying to trade with me.");
}

botClient = new BotClient(bobStubs);
}

@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 <pid>
// 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();
}
}
3 changes: 2 additions & 1 deletion apitest/src/test/java/bisq/apitest/scenario/WalletTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ public void testTxFeeRateMethods(final TestInfo testInfo) {
BtcTxFeeRateTest test = new BtcTxFeeRateTest();

test.testGetTxFeeRate(testInfo);
test.testSetTxFeeRate(testInfo);
test.testSetInvalidTxFeeRateShouldThrowException(testInfo);
test.testSetValidTxFeeRate(testInfo);
test.testUnsetTxFeeRate(testInfo);
}

Expand Down
110 changes: 110 additions & 0 deletions apitest/src/test/java/bisq/apitest/scenario/bot/AbstractBotTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* 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 <http://www.gnu.org/licenses/>.
*/

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.getPaymentMethodById;
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(aliceStubs));
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.",
getPaymentMethodById(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;
}
}
}
77 changes: 77 additions & 0 deletions apitest/src/test/java/bisq/apitest/scenario/bot/Bot.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
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.getPaymentMethodById;
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.",
getPaymentMethodById(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."));
}
}
Loading