Skip to content

Commit

Permalink
Merge pull request #5284 from ghubstan/07-validate-paymentacct-n-new-…
Browse files Browse the repository at this point in the history
…offer

Validate offer <-> payment-acct in createoffer
  • Loading branch information
sqrrm authored Mar 9, 2021
2 parents 343d311 + 389abf4 commit 463f87b
Show file tree
Hide file tree
Showing 40 changed files with 674 additions and 226 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.junit.jupiter.api.TestMethodOrder;

import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static java.lang.String.format;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;

Expand All @@ -55,4 +56,40 @@ public void testAmtTooLargeShouldThrowException() {
assertEquals("UNKNOWN: An error occurred at task: ValidateOffer",
exception.getMessage());
}

@Test
@Order(2)
public void testNoMatchingEURPaymentAccountShouldThrowException() {
PaymentAccount chfAccount = createDummyF2FAccount(aliceClient, "ch");
@SuppressWarnings("ResultOfMethodCallIgnored")
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
aliceClient.createFixedPricedOffer("buy",
"eur",
10000000L,
10000000L,
"40000.0000",
getDefaultBuyerSecurityDepositAsPercent(),
chfAccount.getId(),
"btc"));
String expectedError = format("UNKNOWN: cannot create EUR offer with payment account %s", chfAccount.getId());
assertEquals(expectedError, exception.getMessage());
}

@Test
@Order(2)
public void testNoMatchingCADPaymentAccountShouldThrowException() {
PaymentAccount audAccount = createDummyF2FAccount(aliceClient, "au");
@SuppressWarnings("ResultOfMethodCallIgnored")
Throwable exception = assertThrows(StatusRuntimeException.class, () ->
aliceClient.createFixedPricedOffer("buy",
"cad",
10000000L,
10000000L,
"63000.0000",
getDefaultBuyerSecurityDepositAsPercent(),
audAccount.getId(),
"btc"));
String expectedError = format("UNKNOWN: cannot create CAD offer with payment account %s", audAccount.getId());
assertEquals(expectedError, exception.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ public class AbstractPaymentAccountTest extends MethodTest {
static final String PROPERTY_NAME_SALT = "salt";
static final String PROPERTY_NAME_SORT_CODE = "sortCode";
static final String PROPERTY_NAME_STATE = "state";
static final String PROPERTY_NAME_TRADE_CURRENCIES = "tradeCurrencies";
static final String PROPERTY_NAME_USERNAME = "userName";

static final Gson GSON = new GsonBuilder()
Expand Down

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions apitest/src/test/java/bisq/apitest/scenario/OfferTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ public class OfferTest extends AbstractOfferTest {
public void testAmtTooLargeShouldThrowException() {
ValidateCreateOfferTest test = new ValidateCreateOfferTest();
test.testAmtTooLargeShouldThrowException();
test.testNoMatchingEURPaymentAccountShouldThrowException();
test.testNoMatchingCADPaymentAccountShouldThrowException();
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@
import static bisq.apitest.Scaffold.BitcoinCoreApp.bitcoind;
import static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.seednode;
import static java.util.Objects.requireNonNull;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;


Expand All @@ -27,10 +25,6 @@
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class PaymentAccountTest extends AbstractPaymentAccountTest {

// Two dummy (usd +eth) accounts are set up as defaults in regtest / dao mode,
// then we add 28 more payment accounts in testCreatePaymentAccount().
private static final int EXPECTED_NUM_PAYMENT_ACCOUNTS = 2 + 28;

@BeforeAll
public static void setUp() {
try {
Expand Down Expand Up @@ -75,14 +69,18 @@ public void testCreatePaymentAccount(TestInfo testInfo) {
test.testCreateSepaAccount(testInfo);
test.testCreateSpecificBanksAccount(testInfo);
test.testCreateSwishAccount(testInfo);
test.testCreateTransferwiseAccount(testInfo);

// TransferwiseAccount is only PaymentAccount with a
// tradeCurrencies field in the json form.
test.testCreateTransferwiseAccountWith1TradeCurrency(testInfo);
test.testCreateTransferwiseAccountWith10TradeCurrencies(testInfo);
test.testCreateTransferwiseAccountWithInvalidBrlTradeCurrencyShouldThrowException(testInfo);
test.testCreateTransferwiseAccountWithoutTradeCurrenciesShouldThrowException(testInfo);

test.testCreateUpholdAccount(testInfo);
test.testCreateUSPostalMoneyOrderAccount(testInfo);
test.testCreateWeChatPayAccount(testInfo);
test.testCreateWesternUnionAccount(testInfo);

var paymentAccounts = requireNonNull(aliceClient).getPaymentAccounts();
assertEquals(EXPECTED_NUM_PAYMENT_ACCOUNTS, paymentAccounts.size());
}

@AfterAll
Expand Down
11 changes: 11 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,17 @@ configure(project(':cli')) {
implementation "ch.qos.logback:logback-classic:$logbackVersion"
compileOnly "org.projectlombok:lombok:$lombokVersion"
annotationProcessor "org.projectlombok:lombok:$lombokVersion"

testImplementation "org.junit.jupiter:junit-jupiter-api:$jupiterVersion"
testImplementation "org.junit.jupiter:junit-jupiter-params:$jupiterVersion"
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
testRuntime "javax.annotation:javax.annotation-api:$javaxAnnotationVersion"
}

test {
useJUnitPlatform()
}
}

Expand Down
2 changes: 1 addition & 1 deletion cli/src/main/java/bisq/cli/TableFormat.java
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public static String formatPaymentAcctTbl(List<PaymentAccount> paymentAccounts)
+ padEnd(COL_HEADER_PAYMENT_METHOD, paymentMethodColWidth, ' ') + COL_HEADER_DELIMITER
+ COL_HEADER_UUID + COL_HEADER_DELIMITER + "\n";
String colDataFormat = "%-" + nameColWidth + "s" // left justify
+ " %" + COL_HEADER_CURRENCY.length() + "s" // right justify
+ " %-" + COL_HEADER_CURRENCY.length() + "s" // left justify
+ " %-" + paymentMethodColWidth + "s" // left justify
+ " %-" + COL_HEADER_UUID.length() + "s"; // left justify
return headerLine
Expand Down
25 changes: 22 additions & 3 deletions cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,13 @@

package bisq.cli.opts;

import joptsimple.OptionException;
import joptsimple.OptionParser;
import joptsimple.OptionSet;
import joptsimple.OptionSpec;

import java.util.List;
import java.util.function.Function;

import lombok.Getter;

Expand All @@ -48,12 +50,29 @@ protected AbstractMethodOptionParser(String[] args) {
}

public AbstractMethodOptionParser parse() {
options = parser.parse(new ArgumentList(args).getMethodArguments());
nonOptionArguments = (List<String>) options.nonOptionArguments();
return this;
try {
options = parser.parse(new ArgumentList(args).getMethodArguments());
//noinspection unchecked
nonOptionArguments = (List<String>) options.nonOptionArguments();
return this;
} catch (OptionException ex) {
throw new IllegalArgumentException(cliExceptionMessageStyle.apply(ex), ex);
}
}

public boolean isForHelp() {
return options.has(helpOpt);
}

private final Function<OptionException, String> cliExceptionMessageStyle = (ex) -> {
if (ex.getMessage() == null)
return null;

var optionToken = "option ";
var cliMessage = ex.getMessage().toLowerCase();
if (cliMessage.startsWith(optionToken) && cliMessage.length() > optionToken.length()) {
cliMessage = cliMessage.substring(cliMessage.indexOf(" ") + 1);
}
return cliMessage;
};
}
6 changes: 2 additions & 4 deletions cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
import joptsimple.OptionSpec;

import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
import static joptsimple.internal.Strings.EMPTY;

public class CancelOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {

final OptionSpec<String> offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to cancel")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

public CancelOfferOptionParser(String[] args) {
super(args);
Expand All @@ -40,7 +38,7 @@ public CancelOfferOptionParser parse() {
if (options.has(helpOpt))
return this;

if (!options.has(offerIdOpt))
if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty())
throw new IllegalArgumentException("no offer id specified");

return this;
Expand Down
34 changes: 19 additions & 15 deletions cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,27 @@ public class CreateOfferOptionParser extends AbstractMethodOptionParser implemen
.defaultsTo(EMPTY);

final OptionSpec<String> directionOpt = parser.accepts(OPT_DIRECTION, "offer direction (buy|sell)")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency code (eur|usd|...)")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

final OptionSpec<String> amountOpt = parser.accepts(OPT_AMOUNT, "amount of btc to buy or sell")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

final OptionSpec<String> minAmountOpt = parser.accepts(OPT_MIN_AMOUNT, "minimum amount of btc to buy or sell")
.withOptionalArg()
.defaultsTo(EMPTY);
.withOptionalArg();

final OptionSpec<String> mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, "market btc price margin (%)")
.withOptionalArg()
.defaultsTo("0.00");

final OptionSpec<String> fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price")
.withOptionalArg()
.defaultsTo(EMPTY);
.defaultsTo("0");

final OptionSpec<String> securityDepositOpt = parser.accepts(OPT_SECURITY_DEPOSIT, "maker security deposit (%)")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

final OptionSpec<String> makerFeeCurrencyCodeOpt = parser.accepts(OPT_FEE_CURRENCY, "maker fee currency code (bsq|btc)")
.withOptionalArg()
Expand All @@ -75,19 +70,28 @@ public CreateOfferOptionParser parse() {
if (options.has(helpOpt))
return this;

if (!options.has(paymentAccountIdOpt))
if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty())
throw new IllegalArgumentException("no payment account id specified");

if (!options.has(directionOpt))
if (!options.has(directionOpt) || options.valueOf(directionOpt).isEmpty())
throw new IllegalArgumentException("no direction (buy|sell) specified");

if (!options.has(amountOpt))
if (!options.has(currencyCodeOpt) || options.valueOf(currencyCodeOpt).isEmpty())
throw new IllegalArgumentException("no currency code specified");

if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty())
throw new IllegalArgumentException("no btc amount specified");

if (!options.has(mktPriceMarginOpt) && !options.has(fixedPriceOpt))
throw new IllegalArgumentException("no market price margin or fixed price specified");

if (!options.has(securityDepositOpt))
if (options.has(mktPriceMarginOpt) && options.valueOf(mktPriceMarginOpt).isEmpty())
throw new IllegalArgumentException("no market price margin specified");

if (options.has(fixedPriceOpt) && options.valueOf(fixedPriceOpt).isEmpty())
throw new IllegalArgumentException("no fixed price specified");

if (!options.has(securityDepositOpt) || options.valueOf(securityDepositOpt).isEmpty())
throw new IllegalArgumentException("no security deposit specified");

return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,12 @@

import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT_FORM;
import static java.lang.String.format;
import static joptsimple.internal.Strings.EMPTY;

public class CreatePaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts {

final OptionSpec<String> paymentAcctFormPathOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_FORM,
"path to json payment account form")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

public CreatePaymentAcctOptionParser(String[] args) {
super(args);
Expand All @@ -45,7 +43,7 @@ public CreatePaymentAcctOptionParser parse() {
if (options.has(helpOpt))
return this;

if (!options.has(paymentAcctFormPathOpt))
if (!options.has(paymentAcctFormPathOpt) || options.valueOf(paymentAcctFormPathOpt).isEmpty())
throw new IllegalArgumentException("no path to json payment account form specified");

Path path = Paths.get(options.valueOf(paymentAcctFormPathOpt));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
import joptsimple.OptionSpec;

import static bisq.cli.opts.OptLabel.OPT_ADDRESS;
import static joptsimple.internal.Strings.EMPTY;

public class GetAddressBalanceOptionParser extends AbstractMethodOptionParser implements MethodOpts {

final OptionSpec<String> addressOpt = parser.accepts(OPT_ADDRESS, "wallet btc address")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

public GetAddressBalanceOptionParser(String[] args) {
super(args);
Expand All @@ -40,7 +38,7 @@ public GetAddressBalanceOptionParser parse() {
if (options.has(helpOpt))
return this;

if (!options.has(addressOpt))
if (!options.has(addressOpt) || options.valueOf(addressOpt).isEmpty())
throw new IllegalArgumentException("no address specified");

return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
import joptsimple.OptionSpec;

import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE;
import static joptsimple.internal.Strings.EMPTY;

public class GetBTCMarketPriceOptionParser extends AbstractMethodOptionParser implements MethodOpts {

final OptionSpec<String> currencyCodeOpt = parser.accepts(OPT_CURRENCY_CODE, "currency-code")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

public GetBTCMarketPriceOptionParser(String[] args) {
super(args);
Expand All @@ -40,7 +38,7 @@ public GetBTCMarketPriceOptionParser parse() {
if (options.has(helpOpt))
return this;

if (!options.has(currencyCodeOpt))
if (!options.has(currencyCodeOpt) || options.valueOf(currencyCodeOpt).isEmpty())
throw new IllegalArgumentException("no currency code specified");

return this;
Expand Down
6 changes: 2 additions & 4 deletions cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
import joptsimple.OptionSpec;

import static bisq.cli.opts.OptLabel.OPT_OFFER_ID;
import static joptsimple.internal.Strings.EMPTY;

public class GetOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts {

final OptionSpec<String> offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to get")
.withRequiredArg()
.defaultsTo(EMPTY);
.withRequiredArg();

public GetOfferOptionParser(String[] args) {
super(args);
Expand All @@ -40,7 +38,7 @@ public GetOfferOptionParser parse() {
if (options.has(helpOpt))
return this;

if (!options.has(offerIdOpt))
if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty())
throw new IllegalArgumentException("no offer id specified");

return this;
Expand Down
Loading

0 comments on commit 463f87b

Please sign in to comment.