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

Add 'confirmpaymentreceived' api method #4675

Merged
merged 4 commits into from
Oct 22, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion apitest/src/test/java/bisq/apitest/ApiTestCase.java
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,12 @@ protected static GrpcStubs grpcStubs(BisqAppConfig bisqAppConfig) {
}
}

protected void sleep(long ms) {
protected static void genBtcBlocksThenWait(int numBlocks, long wait) {
bitcoinCli.generateBlocks(numBlocks);
sleep(wait);
}

protected static void sleep(long ms) {
try {
MILLISECONDS.sleep(ms);
} catch (InterruptedException ignored) {
Expand Down
36 changes: 36 additions & 0 deletions apitest/src/test/java/bisq/apitest/method/MethodTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,22 @@

package bisq.apitest.method;

import bisq.proto.grpc.ConfirmPaymentReceivedRequest;
import bisq.proto.grpc.ConfirmPaymentStartedRequest;
import bisq.proto.grpc.CreatePaymentAccountRequest;
import bisq.proto.grpc.GetBalanceRequest;
import bisq.proto.grpc.GetFundingAddressesRequest;
import bisq.proto.grpc.GetOfferRequest;
import bisq.proto.grpc.GetPaymentAccountsRequest;
import bisq.proto.grpc.GetTradeRequest;
import bisq.proto.grpc.LockWalletRequest;
import bisq.proto.grpc.MarketPriceRequest;
import bisq.proto.grpc.OfferInfo;
import bisq.proto.grpc.RegisterDisputeAgentRequest;
import bisq.proto.grpc.RemoveWalletPasswordRequest;
import bisq.proto.grpc.SetWalletPasswordRequest;
import bisq.proto.grpc.TakeOfferRequest;
import bisq.proto.grpc.TradeInfo;
import bisq.proto.grpc.UnlockWalletRequest;

import protobuf.PaymentAccount;
Expand Down Expand Up @@ -88,6 +93,22 @@ protected final GetOfferRequest createGetOfferRequest(String offerId) {
return GetOfferRequest.newBuilder().setId(offerId).build();
}

protected final TakeOfferRequest createTakeOfferRequest(String offerId, String paymentAccountId) {
return TakeOfferRequest.newBuilder().setOfferId(offerId).setPaymentAccountId(paymentAccountId).build();
}

protected final GetTradeRequest createGetTradeRequest(String tradeId) {
return GetTradeRequest.newBuilder().setTradeId(tradeId).build();
}

protected final ConfirmPaymentStartedRequest createConfirmPaymentStartedRequest(String tradeId) {
return ConfirmPaymentStartedRequest.newBuilder().setTradeId(tradeId).build();
}

protected final ConfirmPaymentReceivedRequest createConfirmPaymentReceivedRequest(String tradeId) {
return ConfirmPaymentReceivedRequest.newBuilder().setTradeId(tradeId).build();
}

// Convenience methods for calling frequently used & thoroughly tested gRPC services.

protected final long getBalance(BisqAppConfig bisqAppConfig) {
Expand Down Expand Up @@ -149,6 +170,21 @@ protected final OfferInfo getOffer(BisqAppConfig bisqAppConfig, String offerId)
return grpcStubs(bisqAppConfig).offersService.getOffer(req).getOffer();
}

protected final TradeInfo getTrade(BisqAppConfig bisqAppConfig, String tradeId) {
var req = createGetTradeRequest(tradeId);
return grpcStubs(bisqAppConfig).tradesService.getTrade(req).getTrade();
}

protected final void confirmPaymentStarted(BisqAppConfig bisqAppConfig, String tradeId) {
var req = createConfirmPaymentStartedRequest(tradeId);
grpcStubs(bisqAppConfig).tradesService.confirmPaymentStarted(req);
}

protected final void confirmPaymentReceived(BisqAppConfig bisqAppConfig, String tradeId) {
var req = createConfirmPaymentReceivedRequest(tradeId);
grpcStubs(bisqAppConfig).tradesService.confirmPaymentReceived(req);
}

// Static conveniences for test methods and test case fixture setups.

protected static RegisterDisputeAgentRequest createRegisterDisputeAgentRequest(String disputeAgentType) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@

import bisq.core.monetary.Altcoin;

import bisq.proto.grpc.CreateOfferRequest;
import bisq.proto.grpc.GetOffersRequest;
import bisq.proto.grpc.OfferInfo;

import protobuf.PaymentAccount;

import org.bitcoinj.utils.Fiat;

import java.math.BigDecimal;
Expand All @@ -36,26 +39,28 @@

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.common.util.MathUtils.roundDouble;
import static bisq.common.util.MathUtils.scaleDownByPowerOf10;
import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent;
import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static java.lang.String.format;
import static java.math.RoundingMode.HALF_UP;
import static java.util.Comparator.comparing;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.junit.jupiter.api.Assertions.fail;



import bisq.apitest.method.MethodTest;
import bisq.cli.GrpcStubs;


@Slf4j
abstract class AbstractCreateOfferTest extends MethodTest {
public abstract class AbstractOfferTest extends MethodTest {

protected static GrpcStubs aliceStubs;
protected static GrpcStubs bobStubs;

@BeforeAll
public static void setUp() {
Expand All @@ -64,36 +69,74 @@ public static void setUp() {

static void startSupportingApps() {
try {
// setUpScaffold(new String[]{"--supportingApps", "bitcoind,seednode,alicedaemon", "--enableBisqDebugging", "true"});
setUpScaffold(bitcoind, seednode, alicedaemon);
// setUpScaffold(new String[]{"--supportingApps", "bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon", "--enableBisqDebugging", "true"});
setUpScaffold(bitcoind, seednode, arbdaemon, alicedaemon, bobdaemon);
registerDisputeAgents(arbdaemon);
aliceStubs = grpcStubs(alicedaemon);
bobStubs = grpcStubs(bobdaemon);

// Generate 1 regtest block for alice's wallet to show 10 BTC balance,
// and give alicedaemon time to parse the new block.
bitcoinCli.generateBlocks(1);
MILLISECONDS.sleep(1500);
genBtcBlocksThenWait(1, 1500);
} catch (Exception ex) {
fail(ex);
}
}

protected final OfferInfo createAliceOffer(PaymentAccount paymentAccount,
String direction,
String currencyCode,
long amount) {
return createMarketBasedPricedOffer(aliceStubs, paymentAccount, direction, currencyCode, amount);
}

protected final OfferInfo createBobOffer(PaymentAccount paymentAccount,
String direction,
String currencyCode,
long amount) {
return createMarketBasedPricedOffer(bobStubs, paymentAccount, direction, currencyCode, amount);
}

protected final OfferInfo createMarketBasedPricedOffer(GrpcStubs grpcStubs,
PaymentAccount paymentAccount,
String direction,
String currencyCode,
long amount) {
var req = CreateOfferRequest.newBuilder()
.setPaymentAccountId(paymentAccount.getId())
.setDirection(direction)
.setCurrencyCode(currencyCode)
.setAmount(amount)
.setMinAmount(amount)
.setUseMarketBasedPrice(true)
.setMarketPriceMargin(0.00)
.setPrice("0")
.setBuyerSecurityDeposit(getDefaultBuyerSecurityDepositAsPercent())
.build();
return grpcStubs.offersService.createOffer(req).getOffer();
}

protected final OfferInfo getOffer(String offerId) {
return aliceStubs.offersService.getOffer(createGetOfferRequest(offerId)).getOffer();
}

protected final OfferInfo getMostRecentOffer(String direction, String currencyCode) {
List<OfferInfo> offerInfoList = getOffersSortedByDate(direction, currencyCode);
protected final OfferInfo getMostRecentOffer(GrpcStubs grpcStubs, String direction, String currencyCode) {
List<OfferInfo> offerInfoList = getOffersSortedByDate(grpcStubs, direction, currencyCode);
if (offerInfoList.isEmpty())
fail(format("No %s offers found for currency %s", direction, currencyCode));

return offerInfoList.get(offerInfoList.size() - 1);
}

protected final List<OfferInfo> getOffersSortedByDate(String direction, String currencyCode) {
protected final int getOpenOffersCount(GrpcStubs grpcStubs, String direction, String currencyCode) {
return getOffersSortedByDate(grpcStubs, direction, currencyCode).size();
}

protected final List<OfferInfo> getOffersSortedByDate(GrpcStubs grpcStubs, String direction, String currencyCode) {
var req = GetOffersRequest.newBuilder()
.setDirection(direction)
.setCurrencyCode(currencyCode).build();
var reply = aliceStubs.offersService.getOffers(req);
var reply = grpcStubs.offersService.getOffers(req);
return sortOffersByDate(reply.getOffersList());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CreateOfferUsingFixedPriceTest extends AbstractCreateOfferTest {
public class CreateOfferUsingFixedPriceTest extends AbstractOfferTest {

@Test
@Order(1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class CreateOfferUsingMarketPriceMarginTest extends AbstractCreateOfferTest {
public class CreateOfferUsingMarketPriceMarginTest extends AbstractOfferTest {

private static final DecimalFormat PCT_FORMAT = new DecimalFormat("##0.00");
private static final double MKT_PRICE_MARGIN_ERROR_TOLERANCE = 0.0050; // 0.50%
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class ValidateCreateOfferTest extends AbstractCreateOfferTest {
public class ValidateCreateOfferTest extends AbstractOfferTest {

@Test
@Order(1)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package bisq.apitest.method.trade;

import bisq.core.trade.Trade;

import bisq.proto.grpc.TradeInfo;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;



import bisq.apitest.method.offer.AbstractOfferTest;

public class AbstractTradeTest extends AbstractOfferTest {

protected final TradeInfo takeAlicesOffer(String offerId, String paymentAccountId) {
return bobStubs.tradesService.takeOffer(createTakeOfferRequest(offerId, paymentAccountId)).getTrade();
}

protected final TradeInfo takeBobsOffer(String offerId, String paymentAccountId) {
return aliceStubs.tradesService.takeOffer(createTakeOfferRequest(offerId, paymentAccountId)).getTrade();
}

protected final void verifyExpectedTradeStateAndPhase(TradeInfo trade,
Trade.State expectedState,
Trade.Phase expectedPhase) {
assertNotNull(trade);
assertEquals(expectedState.name(), trade.getState());
assertEquals(expectedPhase.name(), trade.getPhase());
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
/*
* 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.method.trade;

import protobuf.PaymentAccount;

import io.grpc.StatusRuntimeException;

import lombok.extern.slf4j.Slf4j;

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 static bisq.apitest.config.BisqAppConfig.alicedaemon;
import static bisq.apitest.config.BisqAppConfig.bobdaemon;
import static bisq.core.trade.Trade.Phase.DEPOSIT_CONFIRMED;
import static bisq.core.trade.Trade.Phase.DEPOSIT_PUBLISHED;
import static bisq.core.trade.Trade.Phase.FIAT_SENT;
import static bisq.core.trade.Trade.Phase.PAYOUT_PUBLISHED;
import static bisq.core.trade.Trade.State.BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG;
import static bisq.core.trade.Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN;
import static bisq.core.trade.Trade.State.SELLER_PUBLISHED_DEPOSIT_TX;
import static bisq.core.trade.Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.fail;
import static protobuf.Offer.State.OFFER_FEE_PAID;
import static protobuf.OpenOffer.State.AVAILABLE;

@Slf4j
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class TakeBuyBTCOfferTest extends AbstractTradeTest {

// Alice is buyer, Bob is seller.

private static String tradeId;

private PaymentAccount alicesAccount;
private PaymentAccount bobsAccount;

@BeforeEach
public void init() {
alicesAccount = getDefaultPerfectDummyPaymentAccount(alicedaemon);
bobsAccount = getDefaultPerfectDummyPaymentAccount(bobdaemon);
}

@Test
@Order(1)
public void testTakeAlicesBuyOffer() {
try {
var alicesOffer = createAliceOffer(alicesAccount, "buy", "usd", 12500000);
var offerId = alicesOffer.getId();

// Wait for Alice's AddToOfferBook task.
// Wait times vary; my logs show >= 2 second delay.
sleep(3000);
assertEquals(1, getOpenOffersCount(aliceStubs, "buy", "usd"));

var trade = takeAlicesOffer(offerId, bobsAccount.getId());
assertNotNull(trade);
assertEquals(offerId, trade.getTradeId());
// Cache the trade id for the other tests.
tradeId = trade.getTradeId();

genBtcBlocksThenWait(1, 2250);
assertEquals(0, getOpenOffersCount(aliceStubs, "buy", "usd"));

trade = getTrade(bobdaemon, trade.getTradeId());
verifyExpectedTradeStateAndPhase(trade, SELLER_PUBLISHED_DEPOSIT_TX, DEPOSIT_PUBLISHED);

genBtcBlocksThenWait(1, 2250);
trade = getTrade(bobdaemon, trade.getTradeId());
verifyExpectedTradeStateAndPhase(trade, DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN, DEPOSIT_CONFIRMED);
} catch (StatusRuntimeException e) {
fail(e);
}
}

@Test
@Order(2)
public void testAlicesConfirmPaymentStarted() {
try {
var trade = getTrade(alicedaemon, tradeId);
assertNotNull(trade);

confirmPaymentStarted(alicedaemon, trade.getTradeId());
sleep(3000);

trade = getTrade(alicedaemon, tradeId);
assertEquals(OFFER_FEE_PAID.name(), trade.getOffer().getState());
verifyExpectedTradeStateAndPhase(trade, BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG, FIAT_SENT);
} catch (StatusRuntimeException e) {
fail(e);
}
}

@Test
@Order(3)
public void testBobsConfirmPaymentReceived() {
var trade = getTrade(bobdaemon, tradeId);
assertNotNull(trade);

confirmPaymentReceived(bobdaemon, trade.getTradeId());
sleep(3000);

trade = getTrade(bobdaemon, tradeId);
// TODO is this a bug? Why is offer.state == available?
assertEquals(AVAILABLE.name(), trade.getOffer().getState());
verifyExpectedTradeStateAndPhase(trade, SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG, PAYOUT_PUBLISHED);
}
}
Loading