From 55a54f39b4958ab9c0fdf88b67468ef0adf5810d Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:36:27 -0300 Subject: [PATCH 01/35] Add GetTrades proto message, + needed TradeInfo fields --- proto/src/main/proto/grpc.proto | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index bb4504c74c9..5316597655c 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -388,6 +388,8 @@ message StopReply { service Trades { rpc GetTrade (GetTradeRequest) returns (GetTradeReply) { } + rpc GetTrades (GetTradesRequest) returns (GetTradesReply) { + } rpc TakeOffer (TakeOfferRequest) returns (TakeOfferReply) { } rpc ConfirmPaymentStarted (ConfirmPaymentStartedRequest) returns (ConfirmPaymentStartedReply) { @@ -437,6 +439,19 @@ message GetTradeReply { TradeInfo trade = 1; } +message GetTradesRequest { + enum Category { + OPEN = 0; + CLOSED = 1; + FAILED = 2; + } + Category category = 1; +} + +message GetTradesReply { + repeated TradeInfo trades = 1; +} + message CloseTradeRequest { string tradeId = 1; } @@ -495,8 +510,16 @@ message TradeInfo { string contractAsJson = 24; ContractInfo contract = 25; uint64 tradeVolume = 26; + // Optional Bisq v2+ trade protocol fields. BsqSwapTradeInfo bsqSwapTradeInfo = 28; + + // Needed by open/closed/failed trade list items. + uint64 buyerDeposit = 29; + uint64 sellerDeposit = 30; + string statusDescription = 31; + + // TODO? Field for displaying correct precision per coin type, e.g., int32 coinPrecision = 32; } message ContractInfo { From e6e944bcb00e5b2e6f54c4c99882a698792c8bce Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:38:09 -0300 Subject: [PATCH 02/35] Add GrpcTradesService.getTrades service method --- .../bisq/daemon/grpc/GrpcTradesService.java | 68 ++++++++++++++++++- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index dbcb4c6c51d..36f3a4c8eb8 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -18,7 +18,9 @@ package bisq.daemon.grpc; import bisq.core.api.CoreApi; +import bisq.core.api.model.CanceledTradeInfo; import bisq.core.api.model.TradeInfo; +import bisq.core.offer.OpenOffer; import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Trade; import bisq.core.trade.model.bsq_swap.BsqSwapTrade; @@ -33,6 +35,8 @@ import bisq.proto.grpc.FailTradeRequest; import bisq.proto.grpc.GetTradeReply; import bisq.proto.grpc.GetTradeRequest; +import bisq.proto.grpc.GetTradesReply; +import bisq.proto.grpc.GetTradesRequest; import bisq.proto.grpc.TakeOfferReply; import bisq.proto.grpc.TakeOfferRequest; import bisq.proto.grpc.UnFailTradeReply; @@ -45,14 +49,19 @@ import javax.inject.Inject; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import static bisq.core.api.model.TradeInfo.toNewTradeInfo; import static bisq.core.api.model.TradeInfo.toTradeInfo; import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; +import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; +import static bisq.proto.grpc.GetTradesRequest.Category.OPEN; import static bisq.proto.grpc.TradesGrpc.*; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -129,6 +138,22 @@ public void getTrade(GetTradeRequest req, } } + @Override + public void getTrades(GetTradesRequest req, + StreamObserver responseObserver) { + try { + var category = req.getCategory(); + var trades = category.equals(OPEN) + ? coreApi.getOpenTrades() + : coreApi.getTradeHistory(category); + var reply = buildGetTradesReply(trades, category); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (Throwable cause) { + exceptionHandler.handleException(log, cause, responseObserver); + } + } + @Override public void confirmPaymentStarted(ConfirmPaymentStartedRequest req, StreamObserver responseObserver) { @@ -262,13 +287,50 @@ private GetTradeReply buildGetTradeReply(Trade trade) { .build(); } + + private GetTradesReply buildGetTradesReply(List trades, GetTradesRequest.Category category) { + List result = trades.stream() + .map(tradeModel -> { + + var role = coreApi.getTradeRole(tradeModel); + var isMyOffer = coreApi.isMyOffer(tradeModel.getOffer()); + + var isBsqSwapTrade = tradeModel instanceof BsqSwapTrade; + var numConfirmations = isBsqSwapTrade + ? coreApi.getTransactionConfirmations(((BsqSwapTrade) tradeModel).getTxId()) + : 0; + return isBsqSwapTrade + ? toTradeInfo((BsqSwapTrade) tradeModel, role, isMyOffer, numConfirmations) + : toTradeInfo(tradeModel, role, isMyOffer); + }) + .collect(Collectors.toList()); + + // Add canceled OpenOffers to returned closed trades list. + Optional> canceledOpenOffers = category.equals(CLOSED) + ? Optional.of(coreApi.getCanceledOpenOffers()) + : Optional.empty(); + List canceledTrades = new ArrayList<>(); + canceledOpenOffers.ifPresent(openOffers -> canceledTrades.addAll( + openOffers.stream() + .map(CanceledTradeInfo::toCanceledTradeInfo) + .collect(Collectors.toList()) + )); + + return GetTradesReply.newBuilder() + .addAllTrades(result.stream() + .map(TradeInfo::toProtoMessage) + .collect(Collectors.toList())) + .addAllTrades(canceledTrades.stream() + .map(TradeInfo::toProtoMessage) + .collect(Collectors.toList())) + .build(); + } + private boolean wasMyOffer(TradeModel tradeModel) { return coreApi.isMyOffer(tradeModel.getOffer()); } private String getMyRole(TradeModel tradeModel) { - return tradeModel.getOffer().isBsqSwapOffer() - ? coreApi.getBsqSwapTradeRole((BsqSwapTrade) tradeModel) - : coreApi.getTradeRole(tradeModel.getId()); + return coreApi.getTradeRole(tradeModel); } } From b4fa52f4e4584b5cb674c1e9c89222b3d6ac20e3 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:39:07 -0300 Subject: [PATCH 03/35] Add List getTrades() to FailedTradesManager (w/out transitive jfx dep) --- .../java/bisq/core/trade/bisq_v1/FailedTradesManager.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/bisq/core/trade/bisq_v1/FailedTradesManager.java b/core/src/main/java/bisq/core/trade/bisq_v1/FailedTradesManager.java index 2ab852f9728..2a5a41563e9 100644 --- a/core/src/main/java/bisq/core/trade/bisq_v1/FailedTradesManager.java +++ b/core/src/main/java/bisq/core/trade/bisq_v1/FailedTradesManager.java @@ -40,6 +40,7 @@ import java.util.Objects; import java.util.Optional; import java.util.function.Predicate; +import java.util.stream.Collectors; import java.util.stream.Stream; import org.slf4j.Logger; @@ -122,6 +123,10 @@ public ObservableList getObservableList() { return failedTrades.getObservableList(); } + public List getTrades() { + return getObservableList().stream().collect(Collectors.toList()); + } + public Optional getTradeById(String id) { return failedTrades.stream().filter(e -> e.getId().equals(id)).findFirst(); } From 50bbbfe6c453ae513fb64f37e0e37fabcfac1414 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:41:09 -0300 Subject: [PATCH 04/35] Add List getTrades() to TradeManager (w/out transitive jfx dep) --- core/src/main/java/bisq/core/trade/TradeManager.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index e68410c9c30..22586f991d1 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -108,6 +108,7 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -870,6 +871,12 @@ public Optional getTradeById(String tradeId) { .map(tradeModel -> (Trade) tradeModel); } + public List getTrades() { + return getObservableList().stream() + .filter(t -> !t.hasFailed()) + .collect(Collectors.toList()); + } + private void removeTrade(Trade trade) { if (tradableList.remove(trade)) { requestPersistence(); From a53fee345f7bdf6b937a75a9e86252babc912019 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:41:53 -0300 Subject: [PATCH 05/35] Add List getCanceledOpenOffers() ClosedTradableManager --- .../main/java/bisq/core/trade/ClosedTradableManager.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/main/java/bisq/core/trade/ClosedTradableManager.java b/core/src/main/java/bisq/core/trade/ClosedTradableManager.java index 0eb00bf5f8f..bc586807ce1 100644 --- a/core/src/main/java/bisq/core/trade/ClosedTradableManager.java +++ b/core/src/main/java/bisq/core/trade/ClosedTradableManager.java @@ -21,6 +21,7 @@ import bisq.core.monetary.Price; import bisq.core.monetary.Volume; import bisq.core.offer.Offer; +import bisq.core.offer.OpenOffer; import bisq.core.provider.price.PriceFeedService; import bisq.core.trade.bisq_v1.CleanupMailboxMessagesService; import bisq.core.trade.bisq_v1.DumpDelayedPayoutTx; @@ -57,6 +58,7 @@ import lombok.extern.slf4j.Slf4j; +import static bisq.core.offer.OpenOffer.State.CANCELED; import static bisq.core.trade.ClosedTradableUtil.castToTrade; import static bisq.core.trade.ClosedTradableUtil.castToTradeModel; import static bisq.core.trade.ClosedTradableUtil.isBsqSwapTrade; @@ -149,6 +151,13 @@ public List getClosedTrades() { .collect(Collectors.toList())); } + public List getCanceledOpenOffers() { + return ImmutableList.copyOf(getObservableList().stream() + .filter(e -> (e instanceof OpenOffer) && ((OpenOffer) e).getState().equals(CANCELED)) + .map(e -> (OpenOffer) e) + .collect(Collectors.toList())); + } + public Optional getTradableById(String id) { return closedTradables.stream().filter(e -> e.getId().equals(id)).findFirst(); } From 5c217793c883c03cb91481738bdca5391ba14893 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:42:34 -0300 Subject: [PATCH 06/35] Add buyer/seller dep & status-desc fields to TradeInfoV1Builder --- .../api/model/builder/TradeInfoV1Builder.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java index 2bff2254a85..3b37e5de4c3 100644 --- a/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java +++ b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java @@ -46,6 +46,8 @@ public final class TradeInfoV1Builder { private long tradeAmountAsLong; private long tradePrice; private long tradeVolume; + private long buyerDeposit; + private long sellerDeposit; private String tradingPeerNodeAddress; private String state; private String phase; @@ -58,6 +60,7 @@ public final class TradeInfoV1Builder { private boolean isWithdrawn; private String contractAsJson; private ContractInfo contract; + private String statusDescription; public TradeInfoV1Builder withOffer(OfferInfo offer) { this.offer = offer; @@ -129,6 +132,16 @@ public TradeInfoV1Builder withTradeVolume(long tradeVolume) { return this; } + public TradeInfoV1Builder withBuyerDeposit(long buyerDeposit) { + this.buyerDeposit = buyerDeposit; + return this; + } + + public TradeInfoV1Builder withSellerDeposit(long sellerDeposit) { + this.sellerDeposit = sellerDeposit; + return this; + } + public TradeInfoV1Builder withTradePeriodState(String tradePeriodState) { this.tradePeriodState = tradePeriodState; return this; @@ -189,6 +202,12 @@ public TradeInfoV1Builder withContract(ContractInfo contract) { return this; } + + public TradeInfoV1Builder withStatusDescription(String statusDescription) { + this.statusDescription = statusDescription; + return this; + } + public TradeInfo build() { return new TradeInfo(this); } From a9e7484aee3d629568dd1ad297d3d41689bbe61a Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:43:34 -0300 Subject: [PATCH 07/35] Add buyer/seller dep & status-desc fields to TradeInfo --- .../java/bisq/core/api/model/TradeInfo.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java index b8eb474e6ed..283efc2e5f3 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -57,6 +57,8 @@ public class TradeInfo implements Payload { private final long tradeAmountAsLong; private final long tradePrice; private final long tradeVolume; + private final long buyerDeposit; + private final long sellerDeposit; private final String tradingPeerNodeAddress; private final String state; private final String phase; @@ -71,6 +73,7 @@ public class TradeInfo implements Payload { private final ContractInfo contract; // Optional BSQ swap trade protocol details (post v1). private BsqSwapTradeInfo bsqSwapTradeInfo; + private final String statusDescription; public TradeInfo(TradeInfoV1Builder builder) { this.offer = builder.getOffer(); @@ -87,6 +90,8 @@ public TradeInfo(TradeInfoV1Builder builder) { this.tradeAmountAsLong = builder.getTradeAmountAsLong(); this.tradePrice = builder.getTradePrice(); this.tradeVolume = builder.getTradeVolume(); + this.buyerDeposit = builder.getBuyerDeposit(); + this.sellerDeposit = builder.getSellerDeposit(); this.tradingPeerNodeAddress = builder.getTradingPeerNodeAddress(); this.state = builder.getState(); this.phase = builder.getPhase(); @@ -100,6 +105,7 @@ public TradeInfo(TradeInfoV1Builder builder) { this.contractAsJson = builder.getContractAsJson(); this.contract = builder.getContract(); this.bsqSwapTradeInfo = null; + this.statusDescription = builder.getStatusDescription(); } public static TradeInfo toNewTradeInfo(BsqSwapTrade trade, String role) { @@ -135,16 +141,16 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, .withIsCurrencyForTakerFeeBtc(false) // BSQ Swap fees always paid in BSQ. .withTxFeeAsLong(bsqSwapTrade.getTxFee().value) .withTakerFeeAsLong(bsqSwapTrade.getTakerFeeAsLong()) - // N/A: .withTakerFeeTxId(""), .withDepositTxId(""), .withPayoutTxId("") + // N/A for bsq-swaps: .withTakerFeeTxId(""), .withDepositTxId(""), .withPayoutTxId("") .withTradeAmountAsLong(bsqSwapTrade.getAmountAsLong()) .withTradePrice(bsqSwapTrade.getPrice().getValue()) .withTradeVolume(bsqSwapTrade.getVolume() == null ? 0 : bsqSwapTrade.getVolume().getValue()) .withTradingPeerNodeAddress(requireNonNull(bsqSwapTrade.getTradingPeerNodeAddress().getFullAddress())) .withState(bsqSwapTrade.getTradeState().name()) .withPhase(bsqSwapTrade.getTradePhase().name()) - // N/A: .withTradePeriodState(""), .withIsDepositPublished(false), .withIsDepositConfirmed(false) - // N/A: .withIsFiatSent(false), .withIsFiatReceived(false), .withIsPayoutPublished(false) - // N/A: .withIsWithdrawn(false), .withContractAsJson(""), .withContract(null) + // N/A for bsq-swaps: .withTradePeriodState(""), .withIsDepositPublished(false), .withIsDepositConfirmed(false) + // N/A for bsq-swaps: .withIsFiatSent(false), .withIsFiatReceived(false), .withIsPayoutPublished(false) + // N/A for bsq-swaps: .withIsWithdrawn(false), .withContractAsJson(""), .withContract(null) .build(); tradeInfo.bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, isMyOffer, numConfirmations); return tradeInfo; @@ -296,6 +302,8 @@ public String toString() { ", tradeAmountAsLong='" + tradeAmountAsLong + '\'' + "\n" + ", tradePrice='" + tradePrice + '\'' + "\n" + ", tradeVolume='" + tradeVolume + '\'' + "\n" + + ", buyerDeposit='" + buyerDeposit + '\'' + "\n" + + ", sellerDeposit='" + sellerDeposit + '\'' + "\n" + ", tradingPeerNodeAddress='" + tradingPeerNodeAddress + '\'' + "\n" + ", state='" + state + '\'' + "\n" + ", phase='" + phase + '\'' + "\n" + @@ -310,6 +318,7 @@ public String toString() { ", contractAsJson=" + contractAsJson + "\n" + ", contract=" + contract + "\n" + ", bsqSwapTradeInfo=" + bsqSwapTradeInfo + "\n" + + ", statusDescription=" + statusDescription + "\n" + '}'; } } From fdd94fe1f2a95538987a5d8e1b06d8257d1d4360 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:44:18 -0300 Subject: [PATCH 08/35] Remove commented code --- .../core/api/model/CanceledTradeInfo.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java diff --git a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java new file mode 100644 index 00000000000..d3479a406ff --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java @@ -0,0 +1,57 @@ +package bisq.core.api.model; + +import bisq.core.api.model.builder.TradeInfoV1Builder; +import bisq.core.offer.Offer; +import bisq.core.offer.OpenOffer; + +import static bisq.core.api.model.ContractInfo.emptyContract; +import static bisq.core.api.model.OfferInfo.toMyOfferInfo; +import static bisq.core.offer.OpenOffer.State.CANCELED; +import static java.lang.String.format; +import static org.apache.commons.lang3.StringUtils.capitalize; + +/** + * Builds a TradeInfo instance from an OpenOffer with State = CANCELED. + */ +public class CanceledTradeInfo { + + public static TradeInfo toCanceledTradeInfo(OpenOffer myCanceledOpenOffer) { + if (!myCanceledOpenOffer.getState().equals(CANCELED)) + throw new IllegalArgumentException(format("offer '%s' is not canceled", myCanceledOpenOffer.getId())); + + Offer offer = myCanceledOpenOffer.getOffer(); + OfferInfo offerInfo = toMyOfferInfo(offer); + + return new TradeInfoV1Builder() // TODO May need to use BsqSwapTradeInfoBuilder? + .withOffer(offerInfo) + .withTradeId(myCanceledOpenOffer.getId()) + .withShortId(myCanceledOpenOffer.getShortId()) + .withDate(myCanceledOpenOffer.getDate().getTime()) + .withRole("") + .withIsCurrencyForTakerFeeBtc(offer.isCurrencyForMakerFeeBtc()) + .withTxFeeAsLong(offer.getTxFee().value) + .withTakerFeeAsLong(offer.getMakerFee().value) + .withTakerFeeTxId("") // Ignored + .withDepositTxId("") // Ignored + .withPayoutTxId("") // Ignored + .withTradeAmountAsLong(0) // Ignored + .withTradePrice(offer.getPrice().getValue()) + .withTradeVolume(0) // Ignored + .withBuyerDeposit(offer.getBuyerSecurityDeposit().value) + .withSellerDeposit(offer.getSellerSecurityDeposit().value) + .withTradingPeerNodeAddress("") // Ignored + .withState("") // Ignored + .withPhase("") // Ignored + .withTradePeriodState("") // Ignored + .withIsDepositPublished(false) // Ignored + .withIsDepositConfirmed(false) // Ignored + .withIsFiatSent(false) // Ignored + .withIsFiatReceived(false) // Ignored + .withIsPayoutPublished(false) // Ignored + .withIsWithdrawn(false) // Ignored + .withContractAsJson("") // Ignored + .withContract(emptyContract.get()) // Ignored + .withStatusDescription(capitalize(CANCELED.name().toLowerCase())) + .build(); + } +} From d98d795c77b455b2e970fafdc3f999938e06445b Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:46:45 -0300 Subject: [PATCH 09/35] Add server impl for gettrades (open|closed), refactor getrole methods --- core/src/main/java/bisq/core/api/CoreApi.java | 19 +++++-- .../java/bisq/core/api/CoreTradesService.java | 55 ++++++++++++++++--- .../main/resources/help/gettrades-help.txt | 27 +++++++++ 3 files changed, 89 insertions(+), 12 deletions(-) create mode 100644 core/src/main/resources/help/gettrades-help.txt diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 57c9a07a503..3b21324fab6 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -37,6 +37,8 @@ import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ResultHandler; +import bisq.proto.grpc.GetTradesRequest; + import org.bitcoinj.core.Transaction; import javax.inject.Inject; @@ -49,6 +51,7 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import java.util.stream.Collectors; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -327,12 +330,16 @@ public TradeModel getTradeModel(String tradeId) { return coreTradesService.getTradeModel(tradeId); } - public String getTradeRole(String tradeId) { - return coreTradesService.getTradeRole(tradeId); + public List getOpenTrades() { + return coreTradesService.getOpenTrades().stream().map(t -> (TradeModel) t).collect(Collectors.toList()); + } + + public List getTradeHistory(GetTradesRequest.Category category) { + return coreTradesService.getTradeHistory(category); } - public String getBsqSwapTradeRole(BsqSwapTrade bsqSwapTrade) { - return coreTradesService.getBsqSwapTradeRole(bsqSwapTrade); + public String getTradeRole(TradeModel tradeModel) { + return coreTradesService.getTradeRole(tradeModel); } public void failTrade(String tradeId) { @@ -343,6 +350,10 @@ public void unFailTrade(String tradeId) { coreTradesService.unFailTrade(tradeId); } + public List getCanceledOpenOffers() { + return coreTradesService.getCanceledOpenOffers(); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Wallets /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index d28ce99c736..722ee353ea3 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -21,6 +21,7 @@ import bisq.core.btc.wallet.BtcWalletService; import bisq.core.offer.Offer; import bisq.core.offer.OfferUtil; +import bisq.core.offer.OpenOffer; import bisq.core.offer.bisq_v1.TakeOfferModel; import bisq.core.offer.bsq_swap.BsqSwapTakeOfferModel; import bisq.core.trade.ClosedTradableManager; @@ -28,6 +29,7 @@ import bisq.core.trade.bisq_v1.FailedTradesManager; import bisq.core.trade.bisq_v1.TradeResultHandler; import bisq.core.trade.bisq_v1.TradeUtil; +import bisq.core.trade.bsq_swap.BsqSwapTradeManager; import bisq.core.trade.model.Tradable; import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Trade; @@ -39,17 +41,22 @@ import bisq.common.handlers.ErrorMessageHandler; +import bisq.proto.grpc.GetTradesRequest; + import org.bitcoinj.core.Coin; import javax.inject.Inject; import javax.inject.Singleton; +import java.util.List; import java.util.Optional; import java.util.function.Consumer; +import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; import static bisq.core.btc.model.AddressEntry.Context.TRADE_PAYOUT; +import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; import static java.lang.String.format; @Singleton @@ -63,6 +70,7 @@ class CoreTradesService { private final CoreWalletsService coreWalletsService; private final BtcWalletService btcWalletService; private final OfferUtil offerUtil; + private final BsqSwapTradeManager bsqSwapTradeManager; private final ClosedTradableManager closedTradableManager; private final FailedTradesManager failedTradesManager; private final TakeOfferModel takeOfferModel; @@ -76,6 +84,7 @@ public CoreTradesService(CoreContext coreContext, CoreWalletsService coreWalletsService, BtcWalletService btcWalletService, OfferUtil offerUtil, + BsqSwapTradeManager bsqSwapTradeManager, ClosedTradableManager closedTradableManager, FailedTradesManager failedTradesManager, TakeOfferModel takeOfferModel, @@ -87,6 +96,7 @@ public CoreTradesService(CoreContext coreContext, this.coreWalletsService = coreWalletsService; this.btcWalletService = btcWalletService; this.offerUtil = offerUtil; + this.bsqSwapTradeManager = bsqSwapTradeManager; this.closedTradableManager = closedTradableManager; this.failedTradesManager = failedTradesManager; this.takeOfferModel = takeOfferModel; @@ -252,16 +262,13 @@ TradeModel getTradeModel(String tradeId) { new IllegalArgumentException(format("trade with id '%s' not found", tradeId))); } - String getBsqSwapTradeRole(BsqSwapTrade bsqSwapTrade) { - coreWalletsService.verifyWalletsAreAvailable(); - coreWalletsService.verifyEncryptedWalletIsUnlocked(); - return tradeUtil.getRole(bsqSwapTrade); - } - - String getTradeRole(String tradeId) { + String getTradeRole(TradeModel tradeModel) { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); - return tradeUtil.getRole(getTrade(tradeId)); + var isBsqSwapTrade = tradeModel instanceof BsqSwapTrade; + return isBsqSwapTrade + ? tradeUtil.getRole((BsqSwapTrade) tradeModel) + : tradeUtil.getRole((Trade) tradeModel); } Trade getTrade(String tradeId) { @@ -273,6 +280,28 @@ Trade getTrade(String tradeId) { )); } + List getOpenTrades() { + coreWalletsService.verifyWalletsAreAvailable(); + coreWalletsService.verifyEncryptedWalletIsUnlocked(); + return tradeManager.getTrades(); + } + + List getTradeHistory(GetTradesRequest.Category category) { + coreWalletsService.verifyWalletsAreAvailable(); + coreWalletsService.verifyEncryptedWalletIsUnlocked(); + if (category.equals(CLOSED)) { + var closedTrades = closedTradableManager.getClosedTrades().stream() + .map(t -> (TradeModel) t) + .collect(Collectors.toList()); + closedTrades.addAll(bsqSwapTradeManager.getBsqSwapTrades()); + // TODO Sort closedTrades by date. + return closedTrades; + } else { + var failedV1Trades = failedTradesManager.getTrades(); + return failedV1Trades.stream().map(t -> (TradeModel) t).collect(Collectors.toList()); + } + } + void failTrade(String tradeId) { // TODO Recommend that API users should use this method with extra care because // the API lacks methods for diagnosing trade problems, and does not support @@ -304,6 +333,16 @@ void unFailTrade(String tradeId) { }); } + List getCanceledOpenOffers() { + return closedTradableManager.getCanceledOpenOffers(); + } + + /* + String getClosedTradeStateAsString(Tradable tradable) { + return closedTradeUtil.getStateAsString(tradable); + } + */ + private Optional getOpenTrade(String tradeId) { return tradeManager.getTradeById(tradeId); } diff --git a/core/src/main/resources/help/gettrades-help.txt b/core/src/main/resources/help/gettrades-help.txt new file mode 100644 index 00000000000..0f140b1d526 --- /dev/null +++ b/core/src/main/resources/help/gettrades-help.txt @@ -0,0 +1,27 @@ +gettrades + +NAME +---- +gettrades - get trade history + +SYNOPSIS +-------- +gettrades + --currency-code= + +DESCRIPTION +----------- +TODO + +OPTIONS +------- +--currency-code + TODO + +EXAMPLES +-------- +To see ... : +$ ./bisq-cli --password=xyz --port=9998 gettrades --currency-code + +To see ... : +$ ./bisq-cli --password=xyz --port=9998 gettrades ---currency-code From 4c7ae49e18526a088ae1caf1fdc44c922540b733 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:48:58 -0300 Subject: [PATCH 10/35] Format gettrades CLI outout --- .../builder/ClosedTradeTableBuilder.java | 61 +++++++++++++++++++ .../builder/FailedTradeTableBuilder.java | 4 +- .../table/builder/OpenTradeTableBuilder.java | 4 +- .../bisq/cli/table/builder/TableBuilder.java | 12 ++-- .../bisq/cli/table/builder/TableType.java | 6 +- .../builder/TradeTableColumnSupplier.java | 12 ++-- 6 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java diff --git a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java new file mode 100644 index 00000000000..ca7cca30b76 --- /dev/null +++ b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java @@ -0,0 +1,61 @@ +package bisq.cli.table.builder; + +import java.util.List; + +import static bisq.cli.table.builder.TableType.CLOSED_TRADES_TBL; + + + +import bisq.cli.table.Table; +import bisq.cli.table.column.MixedPriceColumn; + +class ClosedTradeTableBuilder extends AbstractTradeListBuilder { + + ClosedTradeTableBuilder(List protos) { + super(CLOSED_TRADES_TBL, protos); + } + + public Table build() { + populateColumns(); + return new Table(colTradeId, + colCreateDate.asStringColumn(), + colMarket, + colPrice.asStringColumn(), + colPriceDeviation.justify(), + colAmountInBtc.asStringColumn(), + colMixedAmount.asStringColumn(), + colCurrency, + colMinerTxFee.asStringColumn(), + colMixedTradeFee.asStringColumn(), + colBuyerDeposit.asStringColumn(), + colSellerDeposit.asStringColumn(), + colOfferType, + colStatusDescription); + } + + private void populateColumns() { + trades.stream().forEachOrdered(t -> { + colTradeId.addRow(t.getTradeId()); + colCreateDate.addRow(t.getDate()); + colMarket.addRow(toMarket.apply(t)); + ((MixedPriceColumn) colPrice).addRow(t.getTradePrice(), isFiatTrade.test(t)); + colPriceDeviation.addRow(toPriceDeviation.apply(t)); + colAmountInBtc.addRow(t.getTradeAmountAsLong()); + colMixedAmount.addRow(t.getTradeVolume(), toDisplayedVolumePrecision.apply(t)); + colCurrency.addRow(toPaymentCurrencyCode.apply(t)); + colMinerTxFee.addRow(toMyMinerTxFee.apply(t)); + + long tradeFeeBsq = toTradeFeeBsq.apply(t, true); + long tradeFeeBtc = toTradeFeeBtc.apply(t, true); + if (tradeFeeBsq > 0) + colMixedTradeFee.addRow(tradeFeeBsq, true); + else + colMixedTradeFee.addRow(tradeFeeBtc, false); + + colBuyerDeposit.addRow(t.getBuyerDeposit()); + colSellerDeposit.addRow(t.getSellerDeposit()); + colOfferType.addRow(toOfferType.apply(t)); + colStatusDescription.addRow(t.getStatusDescription()); + }); + } +} diff --git a/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java index a4a60843476..89c73a97514 100644 --- a/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java @@ -2,7 +2,7 @@ import java.util.List; -import static bisq.cli.table.builder.TableType.FAILED_TRADE_TBL; +import static bisq.cli.table.builder.TableType.FAILED_TRADES_TBL; @@ -15,7 +15,7 @@ class FailedTradeTableBuilder extends AbstractTradeListBuilder { FailedTradeTableBuilder(List protos) { - super(FAILED_TRADE_TBL, protos); + super(FAILED_TRADES_TBL, protos); } public Table build() { diff --git a/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java index a388d036640..9530ae66967 100644 --- a/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java @@ -2,7 +2,7 @@ import java.util.List; -import static bisq.cli.table.builder.TableType.OPEN_TRADE_TBL; +import static bisq.cli.table.builder.TableType.OPEN_TRADES_TBL; @@ -15,7 +15,7 @@ class OpenTradeTableBuilder extends AbstractTradeListBuilder { OpenTradeTableBuilder(List protos) { - super(OPEN_TRADE_TBL, protos); + super(OPEN_TRADES_TBL, protos); } public Table build() { diff --git a/cli/src/main/java/bisq/cli/table/builder/TableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/TableBuilder.java index 4084be33a95..566d3b2a436 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/TableBuilder.java @@ -49,14 +49,14 @@ public Table build() { return new BsqBalanceTableBuilder(protos).build(); case BTC_BALANCE_TBL: return new BtcBalanceTableBuilder(protos).build(); - case CLOSED_TRADE_TBL: - throw new UnsupportedOperationException("TODO return new ClosedTradeTableBuilder(protos).build()"); - case FAILED_TRADE_TBL: - throw new UnsupportedOperationException("TODO return new FailedTradeTableBuilder(protos).build()"); + case CLOSED_TRADES_TBL: + return new ClosedTradeTableBuilder(protos).build(); + case FAILED_TRADES_TBL: + return new FailedTradeTableBuilder(protos).build(); case OFFER_TBL: return new OfferTableBuilder(protos).build(); - case OPEN_TRADE_TBL: - throw new UnsupportedOperationException("TODO return new OpenTradeTableBuilder(protos).build()"); + case OPEN_TRADES_TBL: + return new OpenTradeTableBuilder(protos).build(); case PAYMENT_ACCOUNT_TBL: return new PaymentAccountTableBuilder(protos).build(); case TRADE_DETAIL_TBL: diff --git a/cli/src/main/java/bisq/cli/table/builder/TableType.java b/cli/src/main/java/bisq/cli/table/builder/TableType.java index 0f6afef25b9..a08003335c2 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TableType.java +++ b/cli/src/main/java/bisq/cli/table/builder/TableType.java @@ -25,10 +25,10 @@ public enum TableType { ADDRESS_BALANCE_TBL, BSQ_BALANCE_TBL, BTC_BALANCE_TBL, - CLOSED_TRADE_TBL, - FAILED_TRADE_TBL, + CLOSED_TRADES_TBL, + FAILED_TRADES_TBL, OFFER_TBL, - OPEN_TRADE_TBL, + OPEN_TRADES_TBL, PAYMENT_ACCOUNT_TBL, TRADE_DETAIL_TBL, TRANSACTION_TBL diff --git a/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java b/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java index ae03cb14e87..9f3155fa2c1 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java +++ b/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java @@ -32,9 +32,9 @@ import javax.annotation.Nullable; import static bisq.cli.table.builder.TableBuilderConstants.*; -import static bisq.cli.table.builder.TableType.CLOSED_TRADE_TBL; -import static bisq.cli.table.builder.TableType.FAILED_TRADE_TBL; -import static bisq.cli.table.builder.TableType.OPEN_TRADE_TBL; +import static bisq.cli.table.builder.TableType.CLOSED_TRADES_TBL; +import static bisq.cli.table.builder.TableType.FAILED_TRADES_TBL; +import static bisq.cli.table.builder.TableType.OPEN_TRADES_TBL; import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; import static bisq.cli.table.column.AltcoinColumn.DISPLAY_MODE.ALTCOIN_OFFER_VOLUME; import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT; @@ -75,9 +75,9 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { } private final Supplier isTradeDetailTblBuilder = () -> getTableType().equals(TRADE_DETAIL_TBL); - private final Supplier isOpenTradeTblBuilder = () -> getTableType().equals(OPEN_TRADE_TBL); - private final Supplier isClosedTradeTblBuilder = () -> getTableType().equals(CLOSED_TRADE_TBL); - private final Supplier isFailedTradeTblBuilder = () -> getTableType().equals(FAILED_TRADE_TBL); + private final Supplier isOpenTradeTblBuilder = () -> getTableType().equals(OPEN_TRADES_TBL); + private final Supplier isClosedTradeTblBuilder = () -> getTableType().equals(CLOSED_TRADES_TBL); + private final Supplier isFailedTradeTblBuilder = () -> getTableType().equals(FAILED_TRADES_TBL); private final Supplier firstRow = () -> getTrades().get(0); private final Predicate isFiatOffer = (o) -> o.getBaseCurrencyCode().equals("BTC"); private final Predicate isFiatTrade = (t) -> isFiatOffer.test(t.getOffer()); From ffa31b0af7b06e38abe3dbe625da3e96227294c1 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 16 Jan 2022 16:51:28 -0300 Subject: [PATCH 11/35] Support gRPC gettrades call in CLI --- cli/src/main/java/bisq/cli/CliMain.java | 25 ++++++ cli/src/main/java/bisq/cli/GrpcClient.java | 9 ++ cli/src/main/java/bisq/cli/Method.java | 1 + .../bisq/cli/opts/GetTradesOptionParser.java | 84 +++++++++++++++++++ cli/src/main/java/bisq/cli/opts/OptLabel.java | 1 + .../cli/request/TradesServiceRequest.java | 22 +++++ .../java/bisq/cli/GetTradesSmokeTest.java | 52 ++++++++++++ 7 files changed, 194 insertions(+) create mode 100644 cli/src/main/java/bisq/cli/opts/GetTradesOptionParser.java create mode 100644 cli/src/test/java/bisq/cli/GetTradesSmokeTest.java diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index e42619df6b8..4086b8cbcdf 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -47,6 +47,8 @@ import static bisq.cli.opts.OptLabel.*; import static bisq.cli.table.builder.TableType.*; import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.BSQ_SWAP; +import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; +import static bisq.proto.grpc.GetTradesRequest.Category.OPEN; import static java.lang.String.format; import static java.lang.System.err; import static java.lang.System.exit; @@ -67,6 +69,7 @@ import bisq.cli.opts.GetOffersOptionParser; import bisq.cli.opts.GetPaymentAcctFormOptionParser; import bisq.cli.opts.GetTradeOptionParser; +import bisq.cli.opts.GetTradesOptionParser; import bisq.cli.opts.GetTransactionOptionParser; import bisq.cli.opts.OfferIdOptionParser; import bisq.cli.opts.RegisterDisputeAgentOptionParser; @@ -503,6 +506,26 @@ public static void run(String[] args) { return; } + case gettrades: { + var opts = new GetTradesOptionParser(args).parse(); + if (opts.isForHelp()) { + out.println(client.getMethodHelp(method)); + return; + } + var category = opts.getCategory(); + var trades = category.equals(OPEN) + ? client.getOpenTrades() + : client.getTradeHistory(category); + if (trades.isEmpty()) { + out.println(format("no %s trades found", category.name().toLowerCase())); + } else { + var tableType = category.equals(OPEN) + ? OPEN_TRADES_TBL + : category.equals(CLOSED) ? CLOSED_TRADES_TBL : FAILED_TRADES_TBL; + new TableBuilder(tableType, trades).build().print(out); + } + return; + } case confirmpaymentstarted: { var opts = new GetTradeOptionParser(args).parse(); if (opts.isForHelp()) { @@ -882,6 +905,8 @@ private static void printHelp(OptionParser parser, @SuppressWarnings("SameParame stream.format(rowFormat, gettrade.name(), "--trade-id= \\", "Get trade summary or full contract"); stream.format(rowFormat, "", "[--show-contract=]", ""); stream.println(); + stream.format(rowFormat, gettrades.name(), "[--category=]", "Get open (default), closed, or failed trades"); + stream.println(); stream.format(rowFormat, confirmpaymentstarted.name(), "--trade-id=", "Confirm payment started"); stream.println(); stream.format(rowFormat, confirmpaymentreceived.name(), "--trade-id=", "Confirm payment received"); diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java index 1d93187353d..897599c66fa 100644 --- a/cli/src/main/java/bisq/cli/GrpcClient.java +++ b/cli/src/main/java/bisq/cli/GrpcClient.java @@ -22,6 +22,7 @@ import bisq.proto.grpc.BsqBalanceInfo; import bisq.proto.grpc.BtcBalanceInfo; import bisq.proto.grpc.GetMethodHelpRequest; +import bisq.proto.grpc.GetTradesRequest; import bisq.proto.grpc.GetVersionRequest; import bisq.proto.grpc.OfferInfo; import bisq.proto.grpc.RegisterDisputeAgentRequest; @@ -364,6 +365,14 @@ public TradeInfo getTrade(String tradeId) { return tradesServiceRequest.getTrade(tradeId); } + public List getOpenTrades() { + return tradesServiceRequest.getOpenTrades(); + } + + public List getTradeHistory(GetTradesRequest.Category category) { + return tradesServiceRequest.getTradeHistory(category); + } + public void confirmPaymentStarted(String tradeId) { tradesServiceRequest.confirmPaymentStarted(tradeId); } diff --git a/cli/src/main/java/bisq/cli/Method.java b/cli/src/main/java/bisq/cli/Method.java index 55207203846..56677d7c523 100644 --- a/cli/src/main/java/bisq/cli/Method.java +++ b/cli/src/main/java/bisq/cli/Method.java @@ -42,6 +42,7 @@ public enum Method { getpaymentaccts, getpaymentmethods, gettrade, + gettrades, failtrade, unfailtrade, gettransaction, diff --git a/cli/src/main/java/bisq/cli/opts/GetTradesOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetTradesOptionParser.java new file mode 100644 index 00000000000..c2f402b968c --- /dev/null +++ b/cli/src/main/java/bisq/cli/opts/GetTradesOptionParser.java @@ -0,0 +1,84 @@ +/* + * 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.cli.opts; + + +import bisq.proto.grpc.GetTradesRequest; + +import joptsimple.OptionSpec; + +import java.util.function.Predicate; + +import static bisq.cli.opts.OptLabel.OPT_CATEGORY; +import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; +import static bisq.proto.grpc.GetTradesRequest.Category.FAILED; +import static bisq.proto.grpc.GetTradesRequest.Category.OPEN; +import static java.util.Arrays.stream; + +public class GetTradesOptionParser extends AbstractMethodOptionParser implements MethodOpts { + + // Map valid CLI option values to gRPC request parameters. + private enum CATEGORY { + // Lower case enum fits CLI method and parameter style. + open(OPEN), + closed(CLOSED), + failed(FAILED); + + private final GetTradesRequest.Category grpcRequestCategory; + + CATEGORY(GetTradesRequest.Category grpcRequestCategory) { + this.grpcRequestCategory = grpcRequestCategory; + } + } + + final OptionSpec categoryOpt = parser.accepts(OPT_CATEGORY, + "category of trades (open|closed|failed)") + .withRequiredArg() + .defaultsTo(CATEGORY.open.name()); + + private final Predicate isValidCategory = (c) -> + stream(CATEGORY.values()).anyMatch(v -> v.name().equalsIgnoreCase(c)); + + public GetTradesOptionParser(String[] args) { + super(args); + } + + public GetTradesOptionParser parse() { + super.parse(); + + // Short circuit opt validation if user just wants help. + if (options.has(helpOpt)) + return this; + + if (options.has(categoryOpt)) { + String category = options.valueOf(categoryOpt); + if (category.isEmpty()) + throw new IllegalArgumentException("no category (open|closed|failed) specified"); + + if (!isValidCategory.test(category)) + throw new IllegalArgumentException("category must be open|closed|failed"); + } + + return this; + } + + public GetTradesRequest.Category getCategory() { + String cliOpt = options.valueOf(categoryOpt); + return CATEGORY.valueOf(cliOpt).grpcRequestCategory; + } +} diff --git a/cli/src/main/java/bisq/cli/opts/OptLabel.java b/cli/src/main/java/bisq/cli/opts/OptLabel.java index 5caeb1f05e6..32b9484e845 100644 --- a/cli/src/main/java/bisq/cli/opts/OptLabel.java +++ b/cli/src/main/java/bisq/cli/opts/OptLabel.java @@ -24,6 +24,7 @@ public class OptLabel { public final static String OPT_ACCOUNT_NAME = "account-name"; public final static String OPT_ADDRESS = "address"; public final static String OPT_AMOUNT = "amount"; + public final static String OPT_CATEGORY = "category"; public final static String OPT_CURRENCY_CODE = "currency-code"; public final static String OPT_DIRECTION = "direction"; public final static String OPT_DISPUTE_AGENT_TYPE = "dispute-agent-type"; diff --git a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java index 69b535a77e0..42ec3486fdd 100644 --- a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java +++ b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java @@ -22,12 +22,18 @@ import bisq.proto.grpc.ConfirmPaymentStartedRequest; import bisq.proto.grpc.FailTradeRequest; import bisq.proto.grpc.GetTradeRequest; +import bisq.proto.grpc.GetTradesRequest; import bisq.proto.grpc.TakeOfferReply; import bisq.proto.grpc.TakeOfferRequest; import bisq.proto.grpc.TradeInfo; import bisq.proto.grpc.UnFailTradeRequest; import bisq.proto.grpc.WithdrawFundsRequest; +import java.util.List; + +import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; +import static bisq.proto.grpc.GetTradesRequest.Category.FAILED; + import bisq.cli.GrpcStubs; @@ -72,6 +78,22 @@ public TradeInfo getTrade(String tradeId) { return grpcStubs.tradesService.getTrade(request).getTrade(); } + public List getOpenTrades() { + var request = GetTradesRequest.newBuilder() + .build(); + return grpcStubs.tradesService.getTrades(request).getTradesList(); + } + + public List getTradeHistory(GetTradesRequest.Category category) { + if (!category.equals(CLOSED) && !category.equals(FAILED)) + throw new IllegalStateException("unrecognized gettrades category parameter " + category.name()); + + var request = GetTradesRequest.newBuilder() + .setCategory(category) + .build(); + return grpcStubs.tradesService.getTrades(request).getTradesList(); + } + public void confirmPaymentStarted(String tradeId) { var request = ConfirmPaymentStartedRequest.newBuilder() .setTradeId(tradeId) diff --git a/cli/src/test/java/bisq/cli/GetTradesSmokeTest.java b/cli/src/test/java/bisq/cli/GetTradesSmokeTest.java new file mode 100644 index 00000000000..a95b734a870 --- /dev/null +++ b/cli/src/test/java/bisq/cli/GetTradesSmokeTest.java @@ -0,0 +1,52 @@ +package bisq.cli; + +import bisq.proto.grpc.TradeInfo; + +import java.util.List; + +import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; +import static java.lang.System.out; + + + +import bisq.cli.table.builder.TableBuilder; + +@SuppressWarnings("unused") +public class GetTradesSmokeTest extends AbstractCliTest { + + public static void main(String[] args) { + GetTradesSmokeTest test = new GetTradesSmokeTest(); + test.printAlicesTrades(); + test.printBobsTrades(); + } + + private final List openTrades; + private final List closedTrades; + + public GetTradesSmokeTest() { + super(); + this.openTrades = aliceClient.getOpenTrades(); + this.closedTrades = aliceClient.getTradeHistory(CLOSED); + } + + private void printAlicesTrades() { + out.println("ALICE'S OPEN TRADES"); + openTrades.stream().forEachOrdered(t -> printTrade(aliceClient, t.getTradeId())); + out.println("ALICE'S CLOSED TRADES"); + closedTrades.stream().forEachOrdered(t -> printTrade(aliceClient, t.getTradeId())); + } + + private void printBobsTrades() { + out.println("BOB'S OPEN TRADES"); + openTrades.stream().forEachOrdered(t -> printTrade(bobClient, t.getTradeId())); + out.println("BOB'S CLOSED TRADES"); + closedTrades.stream().forEachOrdered(t -> printTrade(bobClient, t.getTradeId())); + } + + private void printTrade(GrpcClient client, String tradeId) { + var trade = client.getTrade(tradeId); + var tbl = new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString(); + out.println(tbl); + } +} From 3cd1e05a9f45ba6c3c841c8e17ce3805c813033b Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Tue, 18 Jan 2022 13:01:42 -0300 Subject: [PATCH 12/35] Try to display accurate status of closed trades, or "Pending" if still open --- .../method/trade/AbstractTradeTest.java | 14 ++++++++ .../method/trade/BsqSwapTradeTest.java | 9 ++--- .../method/trade/TakeBuyBTCOfferTest.java | 5 ++- .../scenario/LongRunningTradesTest.java | 2 +- .../java/bisq/apitest/scenario/TradeTest.java | 2 +- apitest/src/test/resources/logback.xml | 2 +- .../builder/AbstractTradeListBuilder.java | 4 +-- .../builder/ClosedTradeTableBuilder.java | 4 +-- .../builder/FailedTradeTableBuilder.java | 4 +-- core/src/main/java/bisq/core/api/CoreApi.java | 5 +++ .../java/bisq/core/api/CoreTradesService.java | 8 +++-- .../core/api/model/CanceledTradeInfo.java | 2 +- .../java/bisq/core/api/model/TradeInfo.java | 34 ++++++++++++------- .../api/model/builder/TradeInfoV1Builder.java | 6 ++-- .../core/trade/ClosedTradableFormatter.java | 8 +++-- .../resources/i18n/displayStrings.properties | 1 + .../bisq/daemon/grpc/GrpcTradesService.java | 28 +++++++++++---- proto/src/main/proto/grpc.proto | 2 +- 18 files changed, 98 insertions(+), 42 deletions(-) diff --git a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java index 2c1fb29d320..5ceebe026a2 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java @@ -264,4 +264,18 @@ protected static void runCliGetTrade(String tradeId) { out.println("Bob's CLI 'gettrade' response:"); CliMain.main(new String[]{"--password=xyz", "--port=9999", "gettrade", "--trade-id=" + tradeId}); } + + protected static void runCliGetOpenTrades() { + out.println("Alice's CLI 'gettrades --category=open' response:"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "gettrades", "--category=open"}); + out.println("Bob's CLI 'gettrades --category=open' response:"); + CliMain.main(new String[]{"--password=xyz", "--port=9999", "gettrades", "--category=open"}); + } + + protected static void runCliGetClosedTrades() { + out.println("Alice's CLI 'gettrades --category=closed' response:"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "gettrades", "--category=closed"}); + out.println("Bob's CLI 'gettrades --category=closed' response:"); + CliMain.main(new String[]{"--password=xyz", "--port=9999", "gettrades", "--category=closed"}); + } } diff --git a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java index bbf2074c7be..fb0f5e6b92c 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java @@ -28,7 +28,6 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -51,7 +50,7 @@ import bisq.apitest.method.offer.AbstractOfferTest; import bisq.cli.GrpcClient; -@Disabled +// @Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class BsqSwapTradeTest extends AbstractTradeTest { @@ -118,7 +117,7 @@ public void testBobTakesBsqSwapOffer() { var availableOfferCategory = bobClient.getAvailableOfferCategory(availableSwapOffer.getId()); assertTrue(availableOfferCategory.equals(BSQ_SWAP)); - sleep(30_000); + sleep(3_000); var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId()); tradeId = swapTrade.getTradeId(); // Cache the tradeId for following test case(s). @@ -130,7 +129,7 @@ public void testBobTakesBsqSwapOffer() { log.debug("BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(swapTrade)); assertEquals(COMPLETED.name(), swapTrade.getState()); - runCliGetTrade(tradeId); + runCliGetClosedTrades(); } @Test @@ -151,6 +150,8 @@ public void testCompletedSwapTxConfirmations() { bobsTrade = getBsqSwapTrade(bobClient, tradeId); log.debug("Bob's BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(bobsTrade)); assertEquals(2, bobsTrade.getBsqSwapTradeInfo().getNumConfirmations()); + + runCliGetClosedTrades(); } @Test diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java index 26ac9d24666..df5f3bf0f67 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java @@ -133,7 +133,7 @@ public void testBobsConfirmPaymentReceived(final TestInfo testInfo) { @Test @Order(4) - public void testKeepFunds(final TestInfo testInfo) { + public void testCloseTrade(final TestInfo testInfo) { try { genBtcBlocksThenWait(1, 1_000); var trade = aliceClient.getTrade(tradeId); @@ -147,6 +147,9 @@ public void testKeepFunds(final TestInfo testInfo) { logTrade(log, testInfo, "Alice's Maker/Buyer View (Done)", aliceClient.getTrade(tradeId)); logTrade(log, testInfo, "Bob's Taker/Seller View (Done)", bobClient.getTrade(tradeId)); logBalances(log, testInfo); + + runCliGetClosedTrades(); + } catch (StatusRuntimeException e) { fail(e); } diff --git a/apitest/src/test/java/bisq/apitest/scenario/LongRunningTradesTest.java b/apitest/src/test/java/bisq/apitest/scenario/LongRunningTradesTest.java index ae9c3ae3de0..4322a11bbd3 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/LongRunningTradesTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/LongRunningTradesTest.java @@ -71,7 +71,7 @@ public void testTakeBuyBTCOffer(final TestInfo testInfo) { test.testTakeAlicesBuyOffer(testInfo); test.testAlicesConfirmPaymentStarted(testInfo); test.testBobsConfirmPaymentReceived(testInfo); - test.testKeepFunds(testInfo); + test.testCloseTrade(testInfo); } public void testTakeSellBTCOffer(final TestInfo testInfo) { diff --git a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java index 6e91705b4d8..a5aa9e90ee6 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java @@ -56,7 +56,7 @@ public void testTakeBuyBTCOffer(final TestInfo testInfo) { test.testTakeAlicesBuyOffer(testInfo); test.testAlicesConfirmPaymentStarted(testInfo); test.testBobsConfirmPaymentReceived(testInfo); - test.testKeepFunds(testInfo); + test.testCloseTrade(testInfo); } @Test diff --git a/apitest/src/test/resources/logback.xml b/apitest/src/test/resources/logback.xml index 28279faa118..4f5ca59d99d 100644 --- a/apitest/src/test/resources/logback.xml +++ b/apitest/src/test/resources/logback.xml @@ -12,7 +12,7 @@ - + diff --git a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java index 5c7b109e574..dd09d1e8f97 100644 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java @@ -79,7 +79,7 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { @Nullable protected final Column colOfferType; @Nullable - protected final Column colStatusDescription; + protected final Column colClosingStatus; // Trade detail tbl specific columns @@ -133,7 +133,7 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { this.colPaymentMethod = colSupplier.paymentMethodColumn.get(); this.colRole = colSupplier.roleColumn.get(); this.colOfferType = colSupplier.offerTypeColumn.get(); - this.colStatusDescription = colSupplier.statusDescriptionColumn.get(); + this.colClosingStatus = colSupplier.statusDescriptionColumn.get(); // Trade detail specific columns, some in common with BSQ swap trades detail. diff --git a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java index ca7cca30b76..f9c637296a5 100644 --- a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java @@ -30,7 +30,7 @@ public Table build() { colBuyerDeposit.asStringColumn(), colSellerDeposit.asStringColumn(), colOfferType, - colStatusDescription); + colClosingStatus); } private void populateColumns() { @@ -55,7 +55,7 @@ private void populateColumns() { colBuyerDeposit.addRow(t.getBuyerDeposit()); colSellerDeposit.addRow(t.getSellerDeposit()); colOfferType.addRow(toOfferType.apply(t)); - colStatusDescription.addRow(t.getStatusDescription()); + colClosingStatus.addRow(t.getClosingStatus()); }); } } diff --git a/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java index 89c73a97514..f3478f76d85 100644 --- a/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java @@ -29,7 +29,7 @@ public Table build() { colCurrency, colOfferType, colRole, - colStatusDescription); + colClosingStatus); } private void populateColumns() { @@ -43,7 +43,7 @@ private void populateColumns() { colCurrency.addRow(toPaymentCurrencyCode.apply(t)); colOfferType.addRow(toOfferType.apply(t)); colRole.addRow(t.getRole()); - colStatusDescription.addRow("Failed"); + colClosingStatus.addRow("Failed"); }); } } diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 3b21324fab6..8133ca63fc1 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -26,6 +26,7 @@ import bisq.core.payment.PaymentAccount; import bisq.core.payment.payload.PaymentMethod; import bisq.core.trade.bisq_v1.TradeResultHandler; +import bisq.core.trade.model.Tradable; import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Trade; import bisq.core.trade.model.bsq_swap.BsqSwapTrade; @@ -354,6 +355,10 @@ public List getCanceledOpenOffers() { return coreTradesService.getCanceledOpenOffers(); } + public String getClosedTradeStateAsString(Tradable tradable) { + return coreTradesService.getClosedTradeStateAsString(tradable); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Wallets /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index 722ee353ea3..9e800e6c425 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -24,6 +24,7 @@ import bisq.core.offer.OpenOffer; import bisq.core.offer.bisq_v1.TakeOfferModel; import bisq.core.offer.bsq_swap.BsqSwapTakeOfferModel; +import bisq.core.trade.ClosedTradableFormatter; import bisq.core.trade.ClosedTradableManager; import bisq.core.trade.TradeManager; import bisq.core.trade.bisq_v1.FailedTradesManager; @@ -72,6 +73,7 @@ class CoreTradesService { private final OfferUtil offerUtil; private final BsqSwapTradeManager bsqSwapTradeManager; private final ClosedTradableManager closedTradableManager; + private final ClosedTradableFormatter closedTradableFormatter; private final FailedTradesManager failedTradesManager; private final TakeOfferModel takeOfferModel; private final BsqSwapTakeOfferModel bsqSwapTakeOfferModel; @@ -86,6 +88,7 @@ public CoreTradesService(CoreContext coreContext, OfferUtil offerUtil, BsqSwapTradeManager bsqSwapTradeManager, ClosedTradableManager closedTradableManager, + ClosedTradableFormatter closedTradableFormatter, FailedTradesManager failedTradesManager, TakeOfferModel takeOfferModel, BsqSwapTakeOfferModel bsqSwapTakeOfferModel, @@ -98,6 +101,7 @@ public CoreTradesService(CoreContext coreContext, this.offerUtil = offerUtil; this.bsqSwapTradeManager = bsqSwapTradeManager; this.closedTradableManager = closedTradableManager; + this.closedTradableFormatter = closedTradableFormatter; this.failedTradesManager = failedTradesManager; this.takeOfferModel = takeOfferModel; this.bsqSwapTakeOfferModel = bsqSwapTakeOfferModel; @@ -337,11 +341,9 @@ List getCanceledOpenOffers() { return closedTradableManager.getCanceledOpenOffers(); } - /* String getClosedTradeStateAsString(Tradable tradable) { - return closedTradeUtil.getStateAsString(tradable); + return closedTradableFormatter.getStateAsString(tradable); } - */ private Optional getOpenTrade(String tradeId) { return tradeManager.getTradeById(tradeId); diff --git a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java index d3479a406ff..b4525492fe2 100644 --- a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java @@ -51,7 +51,7 @@ public static TradeInfo toCanceledTradeInfo(OpenOffer myCanceledOpenOffer) { .withIsWithdrawn(false) // Ignored .withContractAsJson("") // Ignored .withContract(emptyContract.get()) // Ignored - .withStatusDescription(capitalize(CANCELED.name().toLowerCase())) + .withClosingStatus(capitalize(CANCELED.name().toLowerCase())) .build(); } } diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java index 283efc2e5f3..0eb990fb06e 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -73,7 +73,7 @@ public class TradeInfo implements Payload { private final ContractInfo contract; // Optional BSQ swap trade protocol details (post v1). private BsqSwapTradeInfo bsqSwapTradeInfo; - private final String statusDescription; + private final String closingStatus; public TradeInfo(TradeInfoV1Builder builder) { this.offer = builder.getOffer(); @@ -105,24 +105,27 @@ public TradeInfo(TradeInfoV1Builder builder) { this.contractAsJson = builder.getContractAsJson(); this.contract = builder.getContract(); this.bsqSwapTradeInfo = null; - this.statusDescription = builder.getStatusDescription(); + this.closingStatus = builder.getClosingStatus(); } public static TradeInfo toNewTradeInfo(BsqSwapTrade trade, String role) { // Always called by the taker, isMyOffer=false. - return toTradeInfo(trade, role, false, 0); + return toTradeInfo(trade, role, false, 0, "Pending"); } public static TradeInfo toNewTradeInfo(Trade trade) { // Always called by the taker, isMyOffer=false. - return toTradeInfo(trade, null, false); + return toTradeInfo(trade, null, false, "Pending"); } - public static TradeInfo toTradeInfo(TradeModel tradeModel, String role, boolean isMyOffer) { + public static TradeInfo toTradeInfo(TradeModel tradeModel, + String role, + boolean isMyOffer, + String closingStatus) { if (tradeModel instanceof Trade) - return toTradeInfo((Trade) tradeModel, role, isMyOffer); + return toTradeInfo((Trade) tradeModel, role, isMyOffer, closingStatus); else if (tradeModel instanceof BsqSwapTrade) - return toTradeInfo(tradeModel, role, isMyOffer); + return toTradeInfo(tradeModel, role, isMyOffer, closingStatus); else throw new IllegalStateException("unsupported trade type: " + tradeModel.getClass().getSimpleName()); } @@ -130,7 +133,8 @@ else if (tradeModel instanceof BsqSwapTrade) public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, String role, boolean isMyOffer, - int numConfirmations) { + int numConfirmations, + String closingStatus) { OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(bsqSwapTrade.getOffer()) : toOfferInfo(bsqSwapTrade.getOffer()); TradeInfo tradeInfo = new TradeInfoV1Builder() .withOffer(offerInfo) @@ -151,12 +155,16 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, // N/A for bsq-swaps: .withTradePeriodState(""), .withIsDepositPublished(false), .withIsDepositConfirmed(false) // N/A for bsq-swaps: .withIsFiatSent(false), .withIsFiatReceived(false), .withIsPayoutPublished(false) // N/A for bsq-swaps: .withIsWithdrawn(false), .withContractAsJson(""), .withContract(null) + .withClosingStatus(closingStatus) .build(); tradeInfo.bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, isMyOffer, numConfirmations); return tradeInfo; } - private static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer) { + private static TradeInfo toTradeInfo(Trade trade, + String role, + boolean isMyOffer, + String closingStatus) { ContractInfo contractInfo; if (trade.getContract() != null) { Contract contract = trade.getContract(); @@ -204,6 +212,7 @@ private static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer .withIsWithdrawn(trade.isWithdrawn()) .withContractAsJson(trade.getContractAsJson()) .withContract(contractInfo) + .withClosingStatus(closingStatus) .build(); } @@ -238,8 +247,8 @@ public bisq.proto.grpc.TradeInfo toProtoMessage() { .setIsFiatSent(isFiatSent) .setIsFiatReceived(isFiatReceived) .setIsPayoutPublished(isPayoutPublished) - .setIsWithdrawn(isWithdrawn); - + .setIsWithdrawn(isWithdrawn) + .setClosingStatus(closingStatus); if (offer.isBsqSwapOffer()) { protoBuilder.setBsqSwapTradeInfo(bsqSwapTradeInfo.toProtoMessage()); } else { @@ -278,6 +287,7 @@ public static TradeInfo fromProto(bisq.proto.grpc.TradeInfo proto) { .withIsWithdrawn(proto.getIsWithdrawn()) .withContractAsJson(proto.getContractAsJson()) .withContract((ContractInfo.fromProto(proto.getContract()))) + .withClosingStatus(proto.getClosingStatus()) .build(); if (proto.getOffer().getIsBsqSwapOffer()) @@ -318,7 +328,7 @@ public String toString() { ", contractAsJson=" + contractAsJson + "\n" + ", contract=" + contract + "\n" + ", bsqSwapTradeInfo=" + bsqSwapTradeInfo + "\n" + - ", statusDescription=" + statusDescription + "\n" + + ", closingStatus=" + closingStatus + "\n" + '}'; } } diff --git a/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java index 3b37e5de4c3..a0b4690ed53 100644 --- a/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java +++ b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java @@ -60,7 +60,7 @@ public final class TradeInfoV1Builder { private boolean isWithdrawn; private String contractAsJson; private ContractInfo contract; - private String statusDescription; + private String closingStatus; public TradeInfoV1Builder withOffer(OfferInfo offer) { this.offer = offer; @@ -203,8 +203,8 @@ public TradeInfoV1Builder withContract(ContractInfo contract) { } - public TradeInfoV1Builder withStatusDescription(String statusDescription) { - this.statusDescription = statusDescription; + public TradeInfoV1Builder withClosingStatus(String closingStatus) { + this.closingStatus = closingStatus; return this; } diff --git a/core/src/main/java/bisq/core/trade/ClosedTradableFormatter.java b/core/src/main/java/bisq/core/trade/ClosedTradableFormatter.java index cbe8e5b88a0..23a6059a062 100644 --- a/core/src/main/java/bisq/core/trade/ClosedTradableFormatter.java +++ b/core/src/main/java/bisq/core/trade/ClosedTradableFormatter.java @@ -53,6 +53,8 @@ import static bisq.core.util.FormattingUtils.formatToPercentWithSymbol; import static bisq.core.util.VolumeUtil.formatVolume; import static bisq.core.util.VolumeUtil.formatVolumeWithCode; +import static org.bitcoinj.core.TransactionConfidence.ConfidenceType.BUILDING; +import static org.bitcoinj.core.TransactionConfidence.ConfidenceType.PENDING; @Slf4j @Singleton @@ -212,11 +214,13 @@ public String getStateAsString(Tradable tradable) { } else if (isBsqSwapTrade(tradable)) { String txId = castToBsqSwapTrade(tradable).getTxId(); TransactionConfidence confidence = bsqWalletService.getConfidenceForTxId(txId); - if (confidence != null && confidence.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING) { + if (confidence != null && confidence.getConfidenceType() == BUILDING) { return Res.get("confidence.confirmed.short"); + } else if (confidence != null && confidence.getConfidenceType() == PENDING) { + return Res.get("confidence.pending"); } else { log.warn("Unexpected confidence in a BSQ swap trade which has been moved to closed trades. " + - "This could happen at a wallet SPV resycn or a reorg. confidence={} tradeID={}", + "This could happen at a wallet SPV resync or a reorg. confidence={} tradeID={}", confidence, tradable.getId()); } } diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 840cca5aad0..5f675eb87cb 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -3232,6 +3232,7 @@ confidence.seen=Seen by {0} peer(s) / 0 confirmations confidence.confirmed=Confirmed in {0} block(s) confidence.invalid=Transaction is invalid confidence.confirmed.short=Confirmed +confidence.pending=Pending peerInfo.title=Peer info peerInfo.nrOfTrades=Number of completed trades diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 36f3a4c8eb8..87de7b6f477 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -59,6 +59,7 @@ import static bisq.core.api.model.TradeInfo.toNewTradeInfo; import static bisq.core.api.model.TradeInfo.toTradeInfo; +import static bisq.core.trade.model.bsq_swap.BsqSwapTrade.State.COMPLETED; import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; import static bisq.proto.grpc.GetTradesRequest.Category.OPEN; @@ -149,6 +150,8 @@ public void getTrades(GetTradesRequest req, var reply = buildGetTradesReply(trades, category); responseObserver.onNext(reply); responseObserver.onCompleted(); + } catch (IllegalArgumentException cause) { + exceptionHandler.handleExceptionAsWarning(log, "getTrades", cause, responseObserver); } catch (Throwable cause) { exceptionHandler.handleException(log, cause, responseObserver); } @@ -243,6 +246,7 @@ final Optional rateMeteringInterceptor() { .or(() -> Optional.of(CallRateMeteringInterceptor.valueOf( new HashMap<>() {{ put(getGetTradeMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); + put(getGetTradesMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getTakeOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getConfirmPaymentStartedMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getConfirmPaymentReceivedMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); @@ -270,10 +274,14 @@ private GetTradeReply buildGetTradeReply(BsqSwapTrade bsqSwapTrade) { boolean wasMyOffer = wasMyOffer(bsqSwapTrade); String role = getMyRole(bsqSwapTrade); var numConfirmations = coreApi.getTransactionConfirmations(bsqSwapTrade.getTxId()); + var closingStatus = bsqSwapTrade.getTradeState().equals(COMPLETED) + ? coreApi.getClosedTradeStateAsString(bsqSwapTrade) + : "Pending"; var tradeInfo = toTradeInfo(bsqSwapTrade, role, wasMyOffer, - numConfirmations); + numConfirmations, + closingStatus); return GetTradeReply.newBuilder() .setTrade(tradeInfo.toProtoMessage()) .build(); @@ -282,26 +290,34 @@ private GetTradeReply buildGetTradeReply(BsqSwapTrade bsqSwapTrade) { private GetTradeReply buildGetTradeReply(Trade trade) { boolean wasMyOffer = wasMyOffer(trade); String role = getMyRole(trade); + var closingStatus = trade.isCompleted() + ? coreApi.getClosedTradeStateAsString(trade) + : "Pending"; return GetTradeReply.newBuilder() - .setTrade(toTradeInfo(trade, role, wasMyOffer).toProtoMessage()) + .setTrade(toTradeInfo(trade, + role, + wasMyOffer, + closingStatus).toProtoMessage()) .build(); } private GetTradesReply buildGetTradesReply(List trades, GetTradesRequest.Category category) { + // Build trade history list, starting with closed BsqSwap and v1 trades. List result = trades.stream() .map(tradeModel -> { - var role = coreApi.getTradeRole(tradeModel); var isMyOffer = coreApi.isMyOffer(tradeModel.getOffer()); - var isBsqSwapTrade = tradeModel instanceof BsqSwapTrade; var numConfirmations = isBsqSwapTrade ? coreApi.getTransactionConfirmations(((BsqSwapTrade) tradeModel).getTxId()) : 0; + var closingStatus = category.equals(OPEN) + ? "Pending" + : coreApi.getClosedTradeStateAsString(tradeModel); return isBsqSwapTrade - ? toTradeInfo((BsqSwapTrade) tradeModel, role, isMyOffer, numConfirmations) - : toTradeInfo(tradeModel, role, isMyOffer); + ? toTradeInfo((BsqSwapTrade) tradeModel, role, isMyOffer, numConfirmations, closingStatus) + : toTradeInfo(tradeModel, role, isMyOffer, closingStatus); }) .collect(Collectors.toList()); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index 5316597655c..bbfcfe57bc3 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -517,7 +517,7 @@ message TradeInfo { // Needed by open/closed/failed trade list items. uint64 buyerDeposit = 29; uint64 sellerDeposit = 30; - string statusDescription = 31; + string closingStatus = 31; // TODO? Field for displaying correct precision per coin type, e.g., int32 coinPrecision = 32; } From a5857e26f67dbefaa7597392816984a73577be9d Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Tue, 18 Jan 2022 13:04:10 -0300 Subject: [PATCH 13/35] Re-set log level to INFO in apitest cases --- apitest/src/test/resources/logback.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apitest/src/test/resources/logback.xml b/apitest/src/test/resources/logback.xml index 4f5ca59d99d..28279faa118 100644 --- a/apitest/src/test/resources/logback.xml +++ b/apitest/src/test/resources/logback.xml @@ -12,7 +12,7 @@ - + From 0ec095220bda47740fe39da2cb33cd2613966189 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:06:06 -0300 Subject: [PATCH 14/35] Remove trailing spaces --- .../java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java index df5f3bf0f67..55487f740bd 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyBTCOfferTest.java @@ -149,7 +149,7 @@ public void testCloseTrade(final TestInfo testInfo) { logBalances(log, testInfo); runCliGetClosedTrades(); - + } catch (StatusRuntimeException e) { fail(e); } From 2e7346a695079339af7494e16be1b26d9a8e242f Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:54:19 -0300 Subject: [PATCH 15/35] Fix comments --- core/src/main/java/bisq/core/api/CoreTradesService.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index 9e800e6c425..e3941d95d00 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -298,7 +298,7 @@ List getTradeHistory(GetTradesRequest.Category category) { .map(t -> (TradeModel) t) .collect(Collectors.toList()); closedTrades.addAll(bsqSwapTradeManager.getBsqSwapTrades()); - // TODO Sort closedTrades by date. + // TODO Sort closedTrades by date? return closedTrades; } else { var failedV1Trades = failedTradesManager.getTrades(); @@ -311,10 +311,6 @@ void failTrade(String tradeId) { // the API lacks methods for diagnosing trade problems, and does not support // interaction with mediators. Users may accidentally fail valid trades, // although they can easily be un-failed with the 'unfailtrade' method. - // The 'failtrade' and 'unfailtrade' methods are implemented at this early - // stage of API development to help efficiently test a new - // 'gettrades --category=' - // method currently in development. coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); From b10cb119f2309c787da787590be6fdb9fcdf9d35 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 20 Jan 2022 15:54:50 -0300 Subject: [PATCH 16/35] Display trade.txFee for BSQ swap trades --- .../bisq/cli/table/builder/AbstractTradeListBuilder.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java index dd09d1e8f97..79059ae4962 100644 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java @@ -206,10 +206,15 @@ protected void validate() { ? formatToPercent(t.getOffer().getMarketPriceMargin()) : "N/A"; - protected final Function toMyMinerTxFee = (t) -> - isTaker.test(t) + protected final Function toMyMinerTxFee = (t) -> { + if (isBsqSwapTrade.test(t)) { + return t.getTxFeeAsLong(); // TODO What is trader's 'share' of tx-fee? + } else { + return isTaker.test(t) ? t.getTxFeeAsLong() : t.getOffer().getTxFee(); + } + }; protected final BiFunction toTradeFeeBsq = (t, isMyOffer) -> { if (isMyOffer) { From 69e6c9ce704158f5a4366a9b82151d0a920d0464 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 20 Jan 2022 16:50:08 -0300 Subject: [PATCH 17/35] Remove duplicated buyer/seller proto fields (already exist no OfferInfo) --- .../java/bisq/core/api/model/CanceledTradeInfo.java | 2 -- .../src/main/java/bisq/core/api/model/TradeInfo.java | 6 ------ .../core/api/model/builder/TradeInfoV1Builder.java | 12 ------------ proto/src/main/proto/grpc.proto | 4 +--- 4 files changed, 1 insertion(+), 23 deletions(-) diff --git a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java index b4525492fe2..552dd1000e7 100644 --- a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java @@ -37,8 +37,6 @@ public static TradeInfo toCanceledTradeInfo(OpenOffer myCanceledOpenOffer) { .withTradeAmountAsLong(0) // Ignored .withTradePrice(offer.getPrice().getValue()) .withTradeVolume(0) // Ignored - .withBuyerDeposit(offer.getBuyerSecurityDeposit().value) - .withSellerDeposit(offer.getSellerSecurityDeposit().value) .withTradingPeerNodeAddress("") // Ignored .withState("") // Ignored .withPhase("") // Ignored diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java index 0eb990fb06e..e86f11c7e0b 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -57,8 +57,6 @@ public class TradeInfo implements Payload { private final long tradeAmountAsLong; private final long tradePrice; private final long tradeVolume; - private final long buyerDeposit; - private final long sellerDeposit; private final String tradingPeerNodeAddress; private final String state; private final String phase; @@ -90,8 +88,6 @@ public TradeInfo(TradeInfoV1Builder builder) { this.tradeAmountAsLong = builder.getTradeAmountAsLong(); this.tradePrice = builder.getTradePrice(); this.tradeVolume = builder.getTradeVolume(); - this.buyerDeposit = builder.getBuyerDeposit(); - this.sellerDeposit = builder.getSellerDeposit(); this.tradingPeerNodeAddress = builder.getTradingPeerNodeAddress(); this.state = builder.getState(); this.phase = builder.getPhase(); @@ -312,8 +308,6 @@ public String toString() { ", tradeAmountAsLong='" + tradeAmountAsLong + '\'' + "\n" + ", tradePrice='" + tradePrice + '\'' + "\n" + ", tradeVolume='" + tradeVolume + '\'' + "\n" + - ", buyerDeposit='" + buyerDeposit + '\'' + "\n" + - ", sellerDeposit='" + sellerDeposit + '\'' + "\n" + ", tradingPeerNodeAddress='" + tradingPeerNodeAddress + '\'' + "\n" + ", state='" + state + '\'' + "\n" + ", phase='" + phase + '\'' + "\n" + diff --git a/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java index a0b4690ed53..3fcc29eff8c 100644 --- a/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java +++ b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java @@ -46,8 +46,6 @@ public final class TradeInfoV1Builder { private long tradeAmountAsLong; private long tradePrice; private long tradeVolume; - private long buyerDeposit; - private long sellerDeposit; private String tradingPeerNodeAddress; private String state; private String phase; @@ -132,16 +130,6 @@ public TradeInfoV1Builder withTradeVolume(long tradeVolume) { return this; } - public TradeInfoV1Builder withBuyerDeposit(long buyerDeposit) { - this.buyerDeposit = buyerDeposit; - return this; - } - - public TradeInfoV1Builder withSellerDeposit(long sellerDeposit) { - this.sellerDeposit = sellerDeposit; - return this; - } - public TradeInfoV1Builder withTradePeriodState(String tradePeriodState) { this.tradePeriodState = tradePeriodState; return this; diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index bbfcfe57bc3..43d377f0de5 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -515,9 +515,7 @@ message TradeInfo { BsqSwapTradeInfo bsqSwapTradeInfo = 28; // Needed by open/closed/failed trade list items. - uint64 buyerDeposit = 29; - uint64 sellerDeposit = 30; - string closingStatus = 31; + string closingStatus = 29; // TODO? Field for displaying correct precision per coin type, e.g., int32 coinPrecision = 32; } From 2a3a2988ecd77f24f9e5d45b00ad0a1d4cf53e21 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 20 Jan 2022 16:57:45 -0300 Subject: [PATCH 18/35] Fix maker/taker fee column headers & value func defs --- .../bisq/cli/table/builder/AbstractTradeListBuilder.java | 7 ++++--- .../bisq/cli/table/builder/ClosedTradeTableBuilder.java | 8 ++++---- .../bisq/cli/table/builder/TableBuilderConstants.java | 4 ++-- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java index 79059ae4962..7b91abf131d 100644 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java @@ -26,7 +26,6 @@ import java.math.RoundingMode; import java.util.List; -import java.util.function.BiFunction; import java.util.function.Function; import java.util.function.Predicate; import java.util.function.Supplier; @@ -216,7 +215,8 @@ protected void validate() { } }; - protected final BiFunction toTradeFeeBsq = (t, isMyOffer) -> { + protected final Function toTradeFeeBsq = (t) -> { + var isMyOffer = t.getOffer().getIsMyOffer(); if (isMyOffer) { return t.getOffer().getIsCurrencyForMakerFeeBtc() ? 0L // Maker paid BTC fee, return 0. @@ -228,7 +228,8 @@ protected void validate() { } }; - protected final BiFunction toTradeFeeBtc = (t, isMyOffer) -> { + protected final Function toTradeFeeBtc = (t) -> { + var isMyOffer = t.getOffer().getIsMyOffer(); if (isMyOffer) { return t.getOffer().getIsCurrencyForMakerFeeBtc() ? t.getOffer().getMakerFee() diff --git a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java index f9c637296a5..98329206afa 100644 --- a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java @@ -45,15 +45,15 @@ private void populateColumns() { colCurrency.addRow(toPaymentCurrencyCode.apply(t)); colMinerTxFee.addRow(toMyMinerTxFee.apply(t)); - long tradeFeeBsq = toTradeFeeBsq.apply(t, true); - long tradeFeeBtc = toTradeFeeBtc.apply(t, true); + long tradeFeeBsq = toTradeFeeBsq.apply(t); + long tradeFeeBtc = toTradeFeeBtc.apply(t); if (tradeFeeBsq > 0) colMixedTradeFee.addRow(tradeFeeBsq, true); else colMixedTradeFee.addRow(tradeFeeBtc, false); - colBuyerDeposit.addRow(t.getBuyerDeposit()); - colSellerDeposit.addRow(t.getSellerDeposit()); + colBuyerDeposit.addRow(t.getOffer().getBuyerSecurityDeposit()); + colSellerDeposit.addRow(t.getOffer().getSellerSecurityDeposit()); colOfferType.addRow(toOfferType.apply(t)); colClosingStatus.addRow(t.getClosingStatus()); }); diff --git a/cli/src/main/java/bisq/cli/table/builder/TableBuilderConstants.java b/cli/src/main/java/bisq/cli/table/builder/TableBuilderConstants.java index b29aa3e3ec9..33800b5d945 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TableBuilderConstants.java +++ b/cli/src/main/java/bisq/cli/table/builder/TableBuilderConstants.java @@ -36,8 +36,8 @@ class TableBuilderConstants { static final String COL_HEADER_UNLOCKING_BONDS_BALANCE = "Unlocking Bonds Balance"; static final String COL_HEADER_UNVERIFIED_BALANCE = "Unverified Balance"; static final String COL_HEADER_BSQ_SWAP_TRADE_ROLE = "My BSQ Swap Role"; - static final String COL_HEADER_BUYER_DEPOSIT = "Buyer Deposit"; - static final String COL_HEADER_SELLER_DEPOSIT = "Seller Deposit"; + static final String COL_HEADER_BUYER_DEPOSIT = "Buyer Deposit (BTC)"; + static final String COL_HEADER_SELLER_DEPOSIT = "Seller Deposit (BTC)"; static final String COL_HEADER_CONFIRMATIONS = "Confirmations"; static final String COL_HEADER_DEVIATION = "Deviation"; static final String COL_HEADER_IS_USED_ADDRESS = "Is Used"; From 6e0299642e0b14b0a338510083d2e9cdb8a04585 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 22 Jan 2022 16:03:34 -0300 Subject: [PATCH 19/35] Fill out gettrades-help.txt --- .../main/resources/help/gettrades-help.txt | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/core/src/main/resources/help/gettrades-help.txt b/core/src/main/resources/help/gettrades-help.txt index 0f140b1d526..d23608d8091 100644 --- a/core/src/main/resources/help/gettrades-help.txt +++ b/core/src/main/resources/help/gettrades-help.txt @@ -2,26 +2,32 @@ gettrades NAME ---- -gettrades - get trade history +gettrades - get open, closed, or failed trades SYNOPSIS -------- gettrades - --currency-code= + --category= DESCRIPTION ----------- -TODO +Displays list of all currently open, closed, or failed trades. OPTIONS ------- ---currency-code - TODO +--category + The category of trade summaries: open (pending), closed (historical), and failed. + The default category option value is open. EXAMPLES -------- -To see ... : -$ ./bisq-cli --password=xyz --port=9998 gettrades --currency-code +To see summaries of all open (pending) trades: +$ ./bisq-cli --password=xyz --port=9998 gettrades +OR +$ ./bisq-cli --password=xyz --port=9998 gettrades --category=open -To see ... : -$ ./bisq-cli --password=xyz --port=9998 gettrades ---currency-code +To see summaries of all closed (historical) trades: +$ ./bisq-cli --password=xyz --port=9998 gettrades ---category=closed + +To see summaries of all failed trades: +$ ./bisq-cli --password=xyz --port=9998 gettrades ---category=failed From fadaaf9151f4915edd4cbcc53cfc2624456a949e Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 22 Jan 2022 16:22:41 -0300 Subject: [PATCH 20/35] Display tx-fee from BsqSwapProtocolModel --- core/src/main/java/bisq/core/api/model/TradeInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java index e86f11c7e0b..82749fd9de6 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -139,7 +139,7 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, .withDate(bsqSwapTrade.getDate().getTime()) .withRole(role == null ? "" : role) .withIsCurrencyForTakerFeeBtc(false) // BSQ Swap fees always paid in BSQ. - .withTxFeeAsLong(bsqSwapTrade.getTxFee().value) + .withTxFeeAsLong(bsqSwapTrade.getBsqSwapProtocolModel().getTxFee()) .withTakerFeeAsLong(bsqSwapTrade.getTakerFeeAsLong()) // N/A for bsq-swaps: .withTakerFeeTxId(""), .withDepositTxId(""), .withPayoutTxId("") .withTradeAmountAsLong(bsqSwapTrade.getAmountAsLong()) From 0e020e8987beefe6e48e5712f9f47386f124fcc9 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 23 Jan 2022 17:03:43 -0300 Subject: [PATCH 21/35] Add payout fields to trade proto msgs, show optional trade/tx fees in swaps --- .../bisq/core/api/model/BsqSwapTradeInfo.java | 28 +++++++++++++++++-- .../java/bisq/core/api/model/TradeInfo.java | 18 ++++++++++-- .../builder/BsqSwapTradeInfoBuilder.java | 12 ++++++++ proto/src/main/proto/grpc.proto | 2 ++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java b/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java index d46cf877a02..6780d9e2895 100644 --- a/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java @@ -25,6 +25,9 @@ import lombok.EqualsAndHashCode; import lombok.Getter; +import static bisq.core.offer.OfferDirection.BUY; +import static bisq.core.offer.OfferDirection.SELL; + @EqualsAndHashCode @Getter public class BsqSwapTradeInfo implements Payload { @@ -41,6 +44,8 @@ public class BsqSwapTradeInfo implements Payload { private final String takerBtcAddress; private final long numConfirmations; private final String errorMessage; + private final long payout; + private final long swapPeerPayout; public BsqSwapTradeInfo(BsqSwapTradeInfoBuilder builder) { this.txId = builder.getTxId(); @@ -55,6 +60,8 @@ public BsqSwapTradeInfo(BsqSwapTradeInfoBuilder builder) { this.takerBtcAddress = builder.getTakerBtcAddress(); this.numConfirmations = builder.getNumConfirmations(); this.errorMessage = builder.getErrorMessage(); + this.payout = builder.getPayout(); + this.swapPeerPayout = builder.getSwapPeerPayout(); } public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, @@ -66,12 +73,20 @@ public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, var makerBtcAddress = wasMyOffer ? protocolModel.getBtcAddress() : swapPeer.getBtcAddress(); var takerBsqAddress = wasMyOffer ? swapPeer.getBsqAddress() : protocolModel.getBsqAddress(); var takerBtcAddress = wasMyOffer ? swapPeer.getBtcAddress() : protocolModel.getBtcAddress(); + // A BSQ Swap trade fee is paid in full by the BTC buyer (selling BSQ). + // The transferred BSQ (payout) is reduced by the fee of the peer. + var makerTradeFee = wasMyOffer && trade.getOffer().getDirection().equals(BUY) + ? trade.getMakerFeeAsLong() + : 0L; + var takerTradeFee = !wasMyOffer && trade.getOffer().getDirection().equals(SELL) + ? trade.getTakerFeeAsLong() + : 0L; return new BsqSwapTradeInfoBuilder() .withTxId(trade.getTxId()) .withBsqTradeAmount(trade.getBsqTradeAmount()) .withBtcTradeAmount(trade.getAmountAsLong()) - .withBsqMakerTradeFee(trade.getMakerFeeAsLong()) - .withBsqTakerTradeFee(trade.getTakerFeeAsLong()) + .withBsqMakerTradeFee(makerTradeFee) + .withBsqTakerTradeFee(takerTradeFee) .withTxFeePerVbyte(trade.getTxFeePerVbyte()) .withMakerBsqAddress(makerBsqAddress) .withMakerBtcAddress(makerBtcAddress) @@ -79,6 +94,8 @@ public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, .withTakerBtcAddress(takerBtcAddress) .withNumConfirmations(numConfirmations) .withErrorMessage(trade.getErrorMessage()) + .withPayout(protocolModel.getPayout()) + .withSwapPeerPayout(protocolModel.getTradePeer().getPayout()) .build(); } @@ -101,6 +118,9 @@ public bisq.proto.grpc.BsqSwapTradeInfo toProtoMessage() { .setTakerBtcAddress(takerBtcAddress != null ? takerBtcAddress : "") .setTakerBtcAddress(takerBtcAddress != null ? takerBtcAddress : "") .setNumConfirmations(numConfirmations) + .setErrorMessage(errorMessage != null ? errorMessage : "") + .setPayout(payout) + .setSwapPeerPayout(swapPeerPayout) .build(); } @@ -118,6 +138,8 @@ public static BsqSwapTradeInfo fromProto(bisq.proto.grpc.BsqSwapTradeInfo proto) .withTakerBtcAddress(proto.getTakerBtcAddress()) .withNumConfirmations(proto.getNumConfirmations()) .withErrorMessage(proto.getErrorMessage()) + .withPayout(proto.getPayout()) + .withSwapPeerPayout(proto.getSwapPeerPayout()) .build(); } @@ -136,6 +158,8 @@ public String toString() { ", takerBtcAddress='" + takerBtcAddress + '\'' + ", numConfirmations='" + numConfirmations + '\'' + ", errorMessage='" + errorMessage + '\'' + + ", payout=" + payout + + ", swapPeerPayout=" + swapPeerPayout + '}'; } } diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java index 82749fd9de6..ecc1bb50687 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -32,6 +32,8 @@ import static bisq.core.api.model.OfferInfo.toMyOfferInfo; import static bisq.core.api.model.OfferInfo.toOfferInfo; import static bisq.core.api.model.PaymentAccountPayloadInfo.toPaymentAccountPayloadInfo; +import static bisq.core.offer.OfferDirection.BUY; +import static bisq.core.offer.OfferDirection.SELL; import static java.util.Objects.requireNonNull; @EqualsAndHashCode @@ -132,6 +134,18 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, int numConfirmations, String closingStatus) { OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(bsqSwapTrade.getOffer()) : toOfferInfo(bsqSwapTrade.getOffer()); + // A BSQ Swap miner tx fee is paid in full by the BTC seller (buying BSQ). + // The BTC buyer's payout = tradeamount minus his share of miner fee. + var iAmBtcSeller = (isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(SELL)) + || (!isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(BUY)); + var txFeeInBtc = iAmBtcSeller + ? bsqSwapTrade.getTxFee().value + : 0L; + // A BSQ Swap trade fee is paid in full by the BTC buyer (selling BSQ). + // The transferred BSQ (payout) is reduced by the peer's trade fee. + var takerFeeInBsq = !isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(SELL) + ? bsqSwapTrade.getTakerFeeAsLong() + : 0L; TradeInfo tradeInfo = new TradeInfoV1Builder() .withOffer(offerInfo) .withTradeId(bsqSwapTrade.getId()) @@ -139,8 +153,8 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, .withDate(bsqSwapTrade.getDate().getTime()) .withRole(role == null ? "" : role) .withIsCurrencyForTakerFeeBtc(false) // BSQ Swap fees always paid in BSQ. - .withTxFeeAsLong(bsqSwapTrade.getBsqSwapProtocolModel().getTxFee()) - .withTakerFeeAsLong(bsqSwapTrade.getTakerFeeAsLong()) + .withTxFeeAsLong(txFeeInBtc) + .withTakerFeeAsLong(takerFeeInBsq) // N/A for bsq-swaps: .withTakerFeeTxId(""), .withDepositTxId(""), .withPayoutTxId("") .withTradeAmountAsLong(bsqSwapTrade.getAmountAsLong()) .withTradePrice(bsqSwapTrade.getPrice().getValue()) diff --git a/core/src/main/java/bisq/core/api/model/builder/BsqSwapTradeInfoBuilder.java b/core/src/main/java/bisq/core/api/model/builder/BsqSwapTradeInfoBuilder.java index 3ae01d4ea8c..0df52ee9f7b 100644 --- a/core/src/main/java/bisq/core/api/model/builder/BsqSwapTradeInfoBuilder.java +++ b/core/src/main/java/bisq/core/api/model/builder/BsqSwapTradeInfoBuilder.java @@ -45,6 +45,8 @@ public final class BsqSwapTradeInfoBuilder { private String takerBtcAddress; private long numConfirmations; private String errorMessage; + private long payout; + private long swapPeerPayout; public BsqSwapTradeInfoBuilder withTxId(String txId) { this.txId = txId; @@ -106,6 +108,16 @@ public BsqSwapTradeInfoBuilder withErrorMessage(String errorMessage) { return this; } + public BsqSwapTradeInfoBuilder withPayout(long payout) { + this.payout = payout; + return this; + } + + public BsqSwapTradeInfoBuilder withSwapPeerPayout(long swapPeerPayout) { + this.swapPeerPayout = swapPeerPayout; + return this; + } + public BsqSwapTradeInfo build() { return new BsqSwapTradeInfo(this); } diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index 43d377f0de5..0a0c4921f33 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -549,6 +549,8 @@ message BsqSwapTradeInfo { string takerBtcAddress = 10; uint64 numConfirmations = 11; string errorMessage = 12; + uint64 payout = 13; + uint64 swapPeerPayout = 14; } message PaymentAccountPayloadInfo { From 3e14bd3fbe56cb5b6c5926ffdfe08cad1a41f3ba Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 23 Jan 2022 17:05:55 -0300 Subject: [PATCH 22/35] Change var name --- core/src/main/java/bisq/core/api/model/TradeInfo.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/api/model/TradeInfo.java b/core/src/main/java/bisq/core/api/model/TradeInfo.java index ecc1bb50687..0bf0d791916 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -136,9 +136,9 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(bsqSwapTrade.getOffer()) : toOfferInfo(bsqSwapTrade.getOffer()); // A BSQ Swap miner tx fee is paid in full by the BTC seller (buying BSQ). // The BTC buyer's payout = tradeamount minus his share of miner fee. - var iAmBtcSeller = (isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(SELL)) + var isBtcSeller = (isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(SELL)) || (!isMyOffer && bsqSwapTrade.getOffer().getDirection().equals(BUY)); - var txFeeInBtc = iAmBtcSeller + var txFeeInBtc = isBtcSeller ? bsqSwapTrade.getTxFee().value : 0L; // A BSQ Swap trade fee is paid in full by the BTC buyer (selling BSQ). From fdd91a82930ecc3a0dd1ff16f14271558a5611ec Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 23 Jan 2022 17:06:48 -0300 Subject: [PATCH 23/35] Fix displayed bsq-swap's optional trade & tx fees --- .../table/builder/AbstractTradeListBuilder.java | 12 +++++++++++- .../table/builder/ClosedTradeTableBuilder.java | 16 ++++++++++------ 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java index 7b91abf131d..7c70d1d0653 100644 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java @@ -36,6 +36,7 @@ import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_BUYER_DEPOSIT; import static bisq.cli.table.builder.TableBuilderConstants.COL_HEADER_SELLER_DEPOSIT; import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static protobuf.OfferDirection.SELL; @@ -167,7 +168,15 @@ protected void validate() { private final Supplier isTradeDetailTblBuilder = () -> tableType.equals(TRADE_DETAIL_TBL); protected final Predicate isFiatTrade = (t) -> isFiatOffer.test(t.getOffer()); protected final Predicate isBsqSwapTrade = (t) -> t.getOffer().getIsBsqSwapOffer(); + protected final Predicate isMyOffer = (t) -> t.getOffer().getIsMyOffer(); protected final Predicate isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); + protected final Predicate isSellOffer = (t) -> t.getOffer().getDirection().equals(SELL.name()); + protected final Predicate isBtcSeller = (t) -> (isMyOffer.test(t) && isSellOffer.test(t)) + || (!isMyOffer.test(t) && !isSellOffer.test(t)); + protected final Predicate isTradeFeeBtc = (t) -> isMyOffer.test(t) + ? t.getOffer().getIsCurrencyForMakerFeeBtc() + : t.getIsCurrencyForTakerFeeBtc(); + // Column Value Functions @@ -207,7 +216,8 @@ protected void validate() { protected final Function toMyMinerTxFee = (t) -> { if (isBsqSwapTrade.test(t)) { - return t.getTxFeeAsLong(); // TODO What is trader's 'share' of tx-fee? + // The BTC seller pays the miner fee for both sides. + return isBtcSeller.test(t) ? t.getTxFeeAsLong() : 0L; } else { return isTaker.test(t) ? t.getTxFeeAsLong() diff --git a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java index 98329206afa..9141cdfd133 100644 --- a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java @@ -45,12 +45,16 @@ private void populateColumns() { colCurrency.addRow(toPaymentCurrencyCode.apply(t)); colMinerTxFee.addRow(toMyMinerTxFee.apply(t)); - long tradeFeeBsq = toTradeFeeBsq.apply(t); - long tradeFeeBtc = toTradeFeeBtc.apply(t); - if (tradeFeeBsq > 0) - colMixedTradeFee.addRow(tradeFeeBsq, true); - else - colMixedTradeFee.addRow(tradeFeeBtc, false); + if (t.getOffer().getIsBsqSwapOffer()) { + // For BSQ Swaps, BTC buyer pays the BSQ trade fee for both sides (BTC seller pays no fee). + var optionalTradeFeeBsq = isBtcSeller.test(t) ? 0L : toTradeFeeBsq.apply(t); + colMixedTradeFee.addRow(optionalTradeFeeBsq, true); + } else if (isTradeFeeBtc.test(t)) { + colMixedTradeFee.addRow(toTradeFeeBsq.apply(t), false); + } else { + // V1 trade fee paid in BSQ. + colMixedTradeFee.addRow(toTradeFeeBsq.apply(t), true); + } colBuyerDeposit.addRow(t.getOffer().getBuyerSecurityDeposit()); colSellerDeposit.addRow(t.getOffer().getSellerSecurityDeposit()); From 2db20c0df11f4fd18f49cd91a59e5f9e99718834 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 23 Jan 2022 17:08:49 -0300 Subject: [PATCH 24/35] Split BsqSwap tests into "buy" and "sell" tests --- .../method/offer/AbstractOfferTest.java | 35 ++++++++++++++ ...eTest.java => BsqSwapBuyBtcTradeTest.java} | 46 ++++--------------- .../scenario/LongRunningBsqSwapTest.java | 6 +-- .../java/bisq/apitest/scenario/TradeTest.java | 19 ++++++-- 4 files changed, 63 insertions(+), 43 deletions(-) rename apitest/src/test/java/bisq/apitest/method/trade/{BsqSwapTradeTest.java => BsqSwapBuyBtcTradeTest.java} (80%) diff --git a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java index addd0733453..57495bb696a 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java @@ -17,12 +17,15 @@ package bisq.apitest.method.offer; +import bisq.core.offer.OfferDirection; + import bisq.proto.grpc.OfferInfo; import protobuf.PaymentAccount; import java.math.BigDecimal; +import java.util.ArrayList; import java.util.List; import java.util.function.BiFunction; import java.util.function.Function; @@ -42,12 +45,16 @@ import static bisq.apitest.config.BisqAppConfig.seednode; import static bisq.cli.table.builder.TableType.OFFER_TBL; import static bisq.common.util.MathUtils.exactMultiply; +import static java.lang.String.format; import static java.lang.System.out; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.fail; import bisq.apitest.method.MethodTest; import bisq.cli.CliMain; +import bisq.cli.GrpcClient; import bisq.cli.table.builder.TableBuilder; @Slf4j @@ -122,6 +129,34 @@ public static void setUp() { protected final Function, String> toOffersTable = (offers) -> new TableBuilder(OFFER_TBL, offers).build().toString(); + protected OfferInfo getAvailableBsqSwapOffer(GrpcClient client, + OfferDirection direction, + boolean checkForLoggedExceptions) { + List bsqSwapOffers = new ArrayList<>(); + int numFetchAttempts = 0; + while (bsqSwapOffers.size() == 0) { + bsqSwapOffers.addAll(client.getBsqSwapOffers(direction.name())); + numFetchAttempts++; + if (bsqSwapOffers.size() == 0) { + log.warn("No available bsq swap offers found after {} fetch attempts.", numFetchAttempts); + if (numFetchAttempts > 9) { + if (checkForLoggedExceptions) { + printNodeExceptionMessages(log); + } + fail(format("Bob gave up on fetching available bsq swap offers after %d attempts.", numFetchAttempts)); + } + sleep(1_000); + } else { + assertEquals(1, bsqSwapOffers.size()); + log.debug("Bob found new available bsq swap offer on attempt # {}.", numFetchAttempts); + break; + } + } + var bsqSwapOffer = bobClient.getBsqSwapOffer(bsqSwapOffers.get(0).getId()); + assertEquals(bsqSwapOffers.get(0).getId(), bsqSwapOffer.getId()); + return bsqSwapOffer; + } + @SuppressWarnings("ConstantConditions") public static void initSwapPaymentAccounts() { // A bot may not know what the default 'BSQ Swap' account name is, diff --git a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapBuyBtcTradeTest.java similarity index 80% rename from apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java rename to apitest/src/test/java/bisq/apitest/method/trade/BsqSwapBuyBtcTradeTest.java index fb0f5e6b92c..cfa330e7478 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapBuyBtcTradeTest.java @@ -17,17 +17,16 @@ package bisq.apitest.method.trade; -import bisq.proto.grpc.OfferInfo; import bisq.proto.grpc.TradeInfo; -import java.util.ArrayList; -import java.util.List; +import protobuf.OfferDirection; import lombok.Setter; import lombok.extern.slf4j.Slf4j; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.MethodOrderer; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -35,6 +34,7 @@ import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.BTC; +import static bisq.core.offer.OfferDirection.BUY; import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.BSQ_SWAP; import static java.lang.String.format; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -43,17 +43,16 @@ import static org.junit.jupiter.api.Assertions.fail; import static protobuf.BsqSwapTrade.State.COMPLETED; import static protobuf.BsqSwapTrade.State.PREPARATION; -import static protobuf.OfferDirection.BUY; import bisq.apitest.method.offer.AbstractOfferTest; import bisq.cli.GrpcClient; -// @Disabled +@Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class BsqSwapTradeTest extends AbstractTradeTest { +public class BsqSwapBuyBtcTradeTest extends AbstractTradeTest { // Long-running swap trade tests might want to check node logs for exceptions. @Setter @@ -80,15 +79,16 @@ public void testGetBalancesBeforeTrade() { @Test @Order(2) - public void testAliceCreateBsqSwapBuyOffer() { - var mySwapOffer = aliceClient.createBsqSwapOffer(BUY.name(), + public void testAliceCreateBsqSwapBuyBtcOffer() { + // Alice buys BTC, pays trade fee. Bob (BTC seller) pays miner tx fee. + var mySwapOffer = aliceClient.createBsqSwapOffer(OfferDirection.BUY.name(), 1_000_000L, // 0.01 BTC 1_000_000L, "0.00005"); log.debug("Pending BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); var newOfferId = mySwapOffer.getId(); assertNotEquals("", newOfferId); - assertEquals(BUY.name(), mySwapOffer.getDirection()); + assertEquals(OfferDirection.BUY.name(), mySwapOffer.getDirection()); assertEquals(5_000, mySwapOffer.getPrice()); assertEquals(1_000_000L, mySwapOffer.getAmount()); assertEquals(1_000_000L, mySwapOffer.getMinAmount()); @@ -107,7 +107,7 @@ public void testAliceCreateBsqSwapBuyOffer() { @Test @Order(3) public void testBobTakesBsqSwapOffer() { - var availableSwapOffer = getAvailableBsqSwapOffer(bobClient); + var availableSwapOffer = getAvailableBsqSwapOffer(bobClient, BUY, true); // Before sending a TakeOfferRequest, the CLI needs to know what kind of Offer // it is taking (v1 or BsqSwap). Only BSQ swap offers can be taken with a @@ -163,32 +163,6 @@ public void testGetBalancesAfterTrade() { log.debug("Bob's After Trade Balance:\n{}", formatBalancesTbls(bobsBalances)); } - private OfferInfo getAvailableBsqSwapOffer(GrpcClient client) { - List bsqSwapOffers = new ArrayList<>(); - int numFetchAttempts = 0; - while (bsqSwapOffers.size() == 0) { - bsqSwapOffers.addAll(client.getBsqSwapOffers(BUY.name())); - numFetchAttempts++; - if (bsqSwapOffers.size() == 0) { - log.warn("No available bsq swap offers found after {} fetch attempts.", numFetchAttempts); - if (numFetchAttempts > 9) { - if (checkForLoggedExceptions) { - printNodeExceptionMessages(log); - } - fail(format("Bob gave up on fetching available bsq swap offers after %d attempts.", numFetchAttempts)); - } - sleep(1_000); - } else { - assertEquals(1, bsqSwapOffers.size()); - log.debug("Bob found new available bsq swap offer on attempt # {}.", numFetchAttempts); - break; - } - } - var bsqSwapOffer = bobClient.getBsqSwapOffer(bsqSwapOffers.get(0).getId()); - assertEquals(bsqSwapOffers.get(0).getId(), bsqSwapOffer.getId()); - return bsqSwapOffer; - } - private TradeInfo getBsqSwapTrade(GrpcClient client, String tradeId) { int numFetchAttempts = 0; while (true) { diff --git a/apitest/src/test/java/bisq/apitest/scenario/LongRunningBsqSwapTest.java b/apitest/src/test/java/bisq/apitest/scenario/LongRunningBsqSwapTest.java index d56c98e5b31..42a63e0136c 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/LongRunningBsqSwapTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/LongRunningBsqSwapTest.java @@ -31,7 +31,7 @@ import bisq.apitest.method.offer.AbstractOfferTest; -import bisq.apitest.method.trade.BsqSwapTradeTest; +import bisq.apitest.method.trade.BsqSwapBuyBtcTradeTest; @EnabledIf("envLongRunningTestEnabled") @Slf4j @@ -49,7 +49,7 @@ public static void setUp() { @Order(1) public void testBsqSwaps() { // TODO Fix wallet inconsistency bugs after N(?) trades. - BsqSwapTradeTest test = new BsqSwapTradeTest(); + BsqSwapBuyBtcTradeTest test = new BsqSwapBuyBtcTradeTest(); test.setCheckForLoggedExceptions(true); for (int swapCount = 1; swapCount <= MAX_SWAPS; swapCount++) { @@ -57,7 +57,7 @@ public void testBsqSwaps() { test.testGetBalancesBeforeTrade(); - test.testAliceCreateBsqSwapBuyOffer(); + test.testAliceCreateBsqSwapBuyBtcOffer(); genBtcBlocksThenWait(1, 8_000); test.testBobTakesBsqSwapOffer(); diff --git a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java index a5aa9e90ee6..d8fa9a6aa77 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java @@ -29,7 +29,8 @@ import bisq.apitest.method.trade.AbstractTradeTest; -import bisq.apitest.method.trade.BsqSwapTradeTest; +import bisq.apitest.method.trade.BsqSwapBuyBtcTradeTest; +import bisq.apitest.method.trade.BsqSwapSellBtcTradeTest; import bisq.apitest.method.trade.FailUnfailTradeTest; import bisq.apitest.method.trade.TakeBuyBSQOfferTest; import bisq.apitest.method.trade.TakeBuyBTCOfferTest; @@ -124,16 +125,26 @@ public void testTakeSellXMROffer(final TestInfo testInfo) { @Test @Order(8) - public void testBsqSwapTradeTest(final TestInfo testInfo) { - BsqSwapTradeTest test = new BsqSwapTradeTest(); + public void testBsqSwapBuyBtcTrade(final TestInfo testInfo) { + BsqSwapBuyBtcTradeTest test = new BsqSwapBuyBtcTradeTest(); test.testGetBalancesBeforeTrade(); - test.testAliceCreateBsqSwapBuyOffer(); + test.testAliceCreateBsqSwapBuyBtcOffer(); test.testBobTakesBsqSwapOffer(); test.testGetBalancesAfterTrade(); } @Test @Order(9) + public void testBsqSwapSellBtcTrade(final TestInfo testInfo) { + BsqSwapSellBtcTradeTest test = new BsqSwapSellBtcTradeTest(); + test.testGetBalancesBeforeTrade(); + test.testAliceCreateBsqSwapSellBtcOffer(); + test.testBobTakesBsqSwapOffer(); + test.testGetBalancesAfterTrade(); + } + + @Test + @Order(10) public void testFailUnfailTrade(final TestInfo testInfo) { FailUnfailTradeTest test = new FailUnfailTradeTest(); test.testFailAndUnFailBuyBTCTrade(testInfo); From fd0c4c2794e99184640c34024e07053e024e4491 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 23 Jan 2022 17:09:34 -0300 Subject: [PATCH 25/35] Add BsqSwapSellBtcTradeTest --- .../method/trade/BsqSwapSellBtcTradeTest.java | 183 ++++++++++++++++++ 1 file changed, 183 insertions(+) create mode 100644 apitest/src/test/java/bisq/apitest/method/trade/BsqSwapSellBtcTradeTest.java diff --git a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapSellBtcTradeTest.java b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapSellBtcTradeTest.java new file mode 100644 index 00000000000..80cd48ec1f0 --- /dev/null +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapSellBtcTradeTest.java @@ -0,0 +1,183 @@ +/* + * 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.method.trade; + +import bisq.proto.grpc.TradeInfo; + +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +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.ApiTestConfig.BSQ; +import static bisq.apitest.config.ApiTestConfig.BTC; +import static bisq.core.offer.OfferDirection.SELL; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.BSQ_SWAP; +import static java.lang.String.format; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; +import static protobuf.BsqSwapTrade.State.COMPLETED; +import static protobuf.BsqSwapTrade.State.PREPARATION; + + + +import bisq.apitest.method.offer.AbstractOfferTest; +import bisq.cli.GrpcClient; + +@Disabled +@Slf4j +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +public class BsqSwapSellBtcTradeTest extends AbstractTradeTest { + + // Long-running swap trade tests might want to check node logs for exceptions. + @Setter + private boolean checkForLoggedExceptions; + + @BeforeAll + public static void setUp() { + AbstractOfferTest.setUp(); + } + + @BeforeEach + public void generateBtcBlock() { + genBtcBlocksThenWait(1, 2_000); + } + + @Test + @Order(1) + public void testGetBalancesBeforeTrade() { + var alicesBalances = aliceClient.getBalances(); + log.debug("Alice's Before Trade Balance:\n{}", formatBalancesTbls(alicesBalances)); + var bobsBalances = bobClient.getBalances(); + log.debug("Bob's Before Trade Balance:\n{}", formatBalancesTbls(bobsBalances)); + } + + @Test + @Order(2) + public void testAliceCreateBsqSwapSellBtcOffer() { + // Alice sells BTC, pays miner tx fee. Bob (BTC buyer) pays trade fee. + var mySwapOffer = aliceClient.createBsqSwapOffer(SELL.name(), + 1_000_000L, // 0.01 BTC + 1_000_000L, + "0.00005"); + log.debug("Pending BsqSwap Buy BSQ (Sell BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); + var newOfferId = mySwapOffer.getId(); + assertNotEquals("", newOfferId); + assertEquals(SELL.name(), mySwapOffer.getDirection()); + assertEquals(5_000, mySwapOffer.getPrice()); + assertEquals(1_000_000L, mySwapOffer.getAmount()); + assertEquals(1_000_000L, mySwapOffer.getMinAmount()); + assertEquals(BSQ, mySwapOffer.getBaseCurrencyCode()); + assertEquals(BTC, mySwapOffer.getCounterCurrencyCode()); + + genBtcBlocksThenWait(1, 2_500); + + mySwapOffer = aliceClient.getOffer(newOfferId); + log.debug("My fetched BsqSwap Buy BSQ (Sell BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); + assertNotEquals(0, mySwapOffer.getMakerFee()); + + runCliGetOffer(newOfferId); + } + + @Test + @Order(3) + public void testBobTakesBsqSwapOffer() { + var availableSwapOffer = getAvailableBsqSwapOffer(bobClient, SELL, true); + + // Before sending a TakeOfferRequest, the CLI needs to know what kind of Offer + // it is taking (v1 or BsqSwap). Only BSQ swap offers can be taken with a + // single offerId parameter. Taking v1 offers requires an additional + // paymentAccountId param. The test case knows what kind of offer is being taken, + // but we test the gRPC GetOfferCategory service here. + var availableOfferCategory = bobClient.getAvailableOfferCategory(availableSwapOffer.getId()); + assertTrue(availableOfferCategory.equals(BSQ_SWAP)); + + sleep(10_000); + + var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId()); + tradeId = swapTrade.getTradeId(); // Cache the tradeId for following test case(s). + log.debug("BsqSwap Trade at PREPARATION:\n{}", toTradeDetailTable.apply(swapTrade)); + assertEquals(PREPARATION.name(), swapTrade.getState()); + genBtcBlocksThenWait(1, 3_000); + + swapTrade = getBsqSwapTrade(bobClient, tradeId); + log.debug("BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(swapTrade)); + assertEquals(COMPLETED.name(), swapTrade.getState()); + + runCliGetClosedTrades(); + } + + @Test + @Order(4) + public void testCompletedSwapTxConfirmations() { + sleep(2_000); // Wait for TX confirmation to happen on node. + + var alicesTrade = getBsqSwapTrade(aliceClient, tradeId); + log.debug("Alice's BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(alicesTrade)); + assertEquals(1, alicesTrade.getBsqSwapTradeInfo().getNumConfirmations()); + + var bobsTrade = getBsqSwapTrade(bobClient, tradeId); + log.debug("Bob's BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(bobsTrade)); + assertEquals(1, bobsTrade.getBsqSwapTradeInfo().getNumConfirmations()); + + genBtcBlocksThenWait(1, 2_000); + + bobsTrade = getBsqSwapTrade(bobClient, tradeId); + log.debug("Bob's BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(bobsTrade)); + assertEquals(2, bobsTrade.getBsqSwapTradeInfo().getNumConfirmations()); + + runCliGetClosedTrades(); + } + + @Test + @Order(5) + public void testGetBalancesAfterTrade() { + var alicesBalances = aliceClient.getBalances(); + log.debug("Alice's After Trade Balance:\n{}", formatBalancesTbls(alicesBalances)); + var bobsBalances = bobClient.getBalances(); + log.debug("Bob's After Trade Balance:\n{}", formatBalancesTbls(bobsBalances)); + } + + private TradeInfo getBsqSwapTrade(GrpcClient client, String tradeId) { + int numFetchAttempts = 0; + while (true) { + try { + numFetchAttempts++; + return client.getTrade(tradeId); + } catch (Exception ex) { + log.warn(ex.getMessage()); + if (numFetchAttempts > 9) { + if (checkForLoggedExceptions) { + printNodeExceptionMessages(log); + } + fail(format("Could not find new bsq swap trade after %d attempts.", numFetchAttempts)); + } else { + sleep(1_000); + } + } + } + } +} From 89c4ab3c615ac9267f813142351390d6dfa13601 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 23 Jan 2022 17:13:52 -0300 Subject: [PATCH 26/35] Use toTradeFeeBtc, not toTradeFeeBsq --- .../java/bisq/cli/table/builder/ClosedTradeTableBuilder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java index 9141cdfd133..9d3c8f06b5b 100644 --- a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java @@ -50,7 +50,7 @@ private void populateColumns() { var optionalTradeFeeBsq = isBtcSeller.test(t) ? 0L : toTradeFeeBsq.apply(t); colMixedTradeFee.addRow(optionalTradeFeeBsq, true); } else if (isTradeFeeBtc.test(t)) { - colMixedTradeFee.addRow(toTradeFeeBsq.apply(t), false); + colMixedTradeFee.addRow(toTradeFeeBtc.apply(t), false); } else { // V1 trade fee paid in BSQ. colMixedTradeFee.addRow(toTradeFeeBsq.apply(t), true); From d902e77710bd7ff2236688fd105fdfc85a065263 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Tue, 25 Jan 2022 11:44:42 -0300 Subject: [PATCH 27/35] Add Bisq license banners --- .../builder/AbstractTradeTableBuilder.java | 17 +++++++++++++++++ .../table/builder/ClosedTradeTableBuilder.java | 17 +++++++++++++++++ .../table/builder/FailedTradeTableBuilder.java | 17 +++++++++++++++++ .../table/builder/OpenTradeTableBuilder.java | 17 +++++++++++++++++ .../bisq/core/api/model/CanceledTradeInfo.java | 17 +++++++++++++++++ 5 files changed, 85 insertions(+) diff --git a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeTableBuilder.java index f94c19f63ec..a9a0a29091b 100644 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeTableBuilder.java @@ -1,3 +1,20 @@ +/* + * 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.cli.table.builder; import bisq.proto.grpc.TradeInfo; diff --git a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java index 9d3c8f06b5b..261cddc1117 100644 --- a/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/ClosedTradeTableBuilder.java @@ -1,3 +1,20 @@ +/* + * 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.cli.table.builder; import java.util.List; diff --git a/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java index f3478f76d85..13b4399070a 100644 --- a/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/FailedTradeTableBuilder.java @@ -1,3 +1,20 @@ +/* + * 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.cli.table.builder; import java.util.List; diff --git a/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java index 9530ae66967..b9c3f7df5da 100644 --- a/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/OpenTradeTableBuilder.java @@ -1,3 +1,20 @@ +/* + * 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.cli.table.builder; import java.util.List; diff --git a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java index 552dd1000e7..d44af9b0356 100644 --- a/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/CanceledTradeInfo.java @@ -1,3 +1,20 @@ +/* + * 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.core.api.model; import bisq.core.api.model.builder.TradeInfoV1Builder; From 210d96670233330e9139fe6e9643edcec7c99242 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Tue, 25 Jan 2022 12:12:07 -0300 Subject: [PATCH 28/35] Fix method name --- .../java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java | 2 +- apitest/src/test/java/bisq/apitest/scenario/TradeTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java index d59bc02bc6c..11f04fd890b 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/TakeBuyXMROfferTest.java @@ -152,7 +152,7 @@ public void testAlicesConfirmPaymentReceived(final TestInfo testInfo) { @Test @Order(4) - public void testKeepFunds(final TestInfo testInfo) { + public void testCloseTrade(final TestInfo testInfo) { try { genBtcBlocksThenWait(1, 1_000); var trade = bobClient.getTrade(tradeId); diff --git a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java index d8fa9a6aa77..cfe7e74e4aa 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java @@ -109,7 +109,7 @@ public void testTakeBuyXMROffer(final TestInfo testInfo) { test.testTakeAlicesSellBTCForXMROffer(testInfo); test.testBobsConfirmPaymentStarted(testInfo); test.testAlicesConfirmPaymentReceived(testInfo); - test.testKeepFunds(testInfo); + test.testCloseTrade(testInfo); } @Test From 4ab4c3d2136083a1f26c75f9903e35702de08a83 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Tue, 25 Jan 2022 12:21:22 -0300 Subject: [PATCH 29/35] Log exception and return "Not Available" when trade role cannot be found --- .../main/java/bisq/core/api/CoreTradesService.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index e3941d95d00..953dc220b01 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -270,9 +270,14 @@ String getTradeRole(TradeModel tradeModel) { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); var isBsqSwapTrade = tradeModel instanceof BsqSwapTrade; - return isBsqSwapTrade - ? tradeUtil.getRole((BsqSwapTrade) tradeModel) - : tradeUtil.getRole((Trade) tradeModel); + try { + return isBsqSwapTrade + ? tradeUtil.getRole((BsqSwapTrade) tradeModel) + : tradeUtil.getRole((Trade) tradeModel); + } catch (Exception ex) { + log.error("Role not found for trade with Id {}.", tradeModel.getId(), ex); + return "Not Available"; + } } Trade getTrade(String tradeId) { From ef00a2cc0fdf35c5de57360082e104b9bda1b668 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Tue, 25 Jan 2022 18:14:58 -0300 Subject: [PATCH 30/35] Remove unused class --- .../builder/AbstractTradeTableBuilder.java | 123 ------------------ 1 file changed, 123 deletions(-) delete mode 100644 cli/src/main/java/bisq/cli/table/builder/AbstractTradeTableBuilder.java diff --git a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeTableBuilder.java deleted file mode 100644 index a9a0a29091b..00000000000 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeTableBuilder.java +++ /dev/null @@ -1,123 +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.cli.table.builder; - -import bisq.proto.grpc.TradeInfo; - -import java.util.List; -import java.util.function.Function; -import java.util.function.Predicate; - -import javax.annotation.Nullable; - -import static bisq.cli.table.builder.TableBuilderConstants.*; -import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; -import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; - - - -import bisq.cli.table.column.BtcColumn; -import bisq.cli.table.column.Column; -import bisq.cli.table.column.Iso8601DateTimeColumn; -import bisq.cli.table.column.MixedPriceColumn; -import bisq.cli.table.column.MixedTradeFeeColumn; -import bisq.cli.table.column.MixedVolumeColumn; -import bisq.cli.table.column.SatoshiColumn; -import bisq.cli.table.column.StringColumn; - -/** - * Builds a {@code bisq.cli.table.Table} from one or more {@code bisq.proto.grpc.TradeInfo} objects. - */ -abstract class AbstractTradeTableBuilder extends AbstractTableBuilder { - - @Nullable - protected final Column colTradeId; - @Nullable - protected final Column colCreateDate; - @Nullable - protected final Column colMarket; - @Nullable - protected final MixedPriceColumn colMixedPrice; - @Nullable - protected final Column colPriceDeviation; - @Nullable - protected final Column colAmountInBtc; - @Nullable - protected final MixedVolumeColumn colMixedAmount; - @Nullable - protected final Column colCurrency; - @Nullable - protected final MixedTradeFeeColumn colMixedTradeFee; - @Nullable - protected final Column colBuyerDeposit; - @Nullable - protected final Column colSellerDeposit; - @Nullable - protected final Column colOfferType; - @Nullable - protected final Column colStatus; - protected final Column colMinerTxFee; - - AbstractTradeTableBuilder(TableType tableType, List protos) { - super(tableType, protos); - boolean isTradeDetail = tableType.equals(TRADE_DETAIL_TBL); - this.colTradeId = isTradeDetail ? null : new StringColumn(COL_HEADER_TRADE_ID); - this.colCreateDate = isTradeDetail ? null : new Iso8601DateTimeColumn(COL_HEADER_DATE_TIME); - this.colMarket = isTradeDetail ? null : new StringColumn(COL_HEADER_MARKET); - this.colMixedPrice = isTradeDetail ? null : new MixedPriceColumn(COL_HEADER_PRICE); - this.colPriceDeviation = isTradeDetail ? null : new StringColumn(COL_HEADER_DEVIATION, RIGHT); - this.colAmountInBtc = isTradeDetail ? null : new BtcColumn(COL_HEADER_AMOUNT_IN_BTC); - this.colMixedAmount = isTradeDetail ? null : new MixedVolumeColumn(COL_HEADER_AMOUNT); - this.colCurrency = isTradeDetail ? null : new StringColumn(COL_HEADER_CURRENCY); - this.colMixedTradeFee = isTradeDetail ? null : new MixedTradeFeeColumn(COL_HEADER_TRADE_FEE); - this.colBuyerDeposit = isTradeDetail ? null : new SatoshiColumn(COL_HEADER_BUYER_DEPOSIT); - this.colSellerDeposit = isTradeDetail ? null : new SatoshiColumn(COL_HEADER_SELLER_DEPOSIT); - this.colOfferType = isTradeDetail ? null : new StringColumn(COL_HEADER_OFFER_TYPE); - this.colStatus = isTradeDetail ? null : new StringColumn(COL_HEADER_STATUS); - this.colMinerTxFee = new SatoshiColumn(COL_HEADER_TX_FEE); - } - - protected final Predicate isFiatTrade = (t) -> isFiatOffer.test(t.getOffer()); - - protected final Predicate isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); - - protected final Function toPaymentCurrencyCode = (t) -> - isFiatTrade.test(t) - ? t.getOffer().getCounterCurrencyCode() - : t.getOffer().getBaseCurrencyCode(); - - protected final Function toAmount = (t) -> - isFiatTrade.test(t) - ? t.getTradeAmountAsLong() - : t.getTradeVolume(); - - protected final Function toMinerTxFee = (t) -> - isTaker.test(t) - ? t.getTxFeeAsLong() - : t.getOffer().getTxFee(); - - protected final Function toMakerTakerFee = (t) -> - isTaker.test(t) - ? t.getTakerFeeAsLong() - : t.getOffer().getMakerFee(); - - protected final Function toTradeCost = (t) -> - isFiatTrade.test(t) - ? t.getTradeVolume() - : t.getTradeAmountAsLong(); -} From 4c8cec54f3247947dc211d50156b2e2af6663340 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Tue, 25 Jan 2022 19:11:24 -0300 Subject: [PATCH 31/35] Push Trade->TradeModel mapping down from CoreApi into CoreTradesService --- core/src/main/java/bisq/core/api/CoreApi.java | 3 +-- core/src/main/java/bisq/core/api/CoreTradesService.java | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 8133ca63fc1..5740d47b88f 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -52,7 +52,6 @@ import java.util.Optional; import java.util.Set; import java.util.function.Consumer; -import java.util.stream.Collectors; import lombok.Getter; import lombok.extern.slf4j.Slf4j; @@ -332,7 +331,7 @@ public TradeModel getTradeModel(String tradeId) { } public List getOpenTrades() { - return coreTradesService.getOpenTrades().stream().map(t -> (TradeModel) t).collect(Collectors.toList()); + return coreTradesService.getOpenTrades(); } public List getTradeHistory(GetTradesRequest.Category category) { diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index 953dc220b01..f31efdce6d1 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -289,10 +289,10 @@ Trade getTrade(String tradeId) { )); } - List getOpenTrades() { + List getOpenTrades() { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); - return tradeManager.getTrades(); + return tradeManager.getTrades().stream().map(t -> (TradeModel) t).collect(Collectors.toList()); } List getTradeHistory(GetTradesRequest.Category category) { From f231aacb3138b77a826413e4ada170c9a1b643e8 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Wed, 26 Jan 2022 11:43:31 -0300 Subject: [PATCH 32/35] Sort API gettrades's response list by trade date --- .../java/bisq/core/api/CoreTradesService.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index f31efdce6d1..a169f27caae 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -49,9 +49,11 @@ import javax.inject.Inject; import javax.inject.Singleton; +import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.function.Consumer; +import java.util.function.Supplier; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -59,11 +61,15 @@ import static bisq.core.btc.model.AddressEntry.Context.TRADE_PAYOUT; import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; import static java.lang.String.format; +import static java.util.Comparator.comparing; @Singleton @Slf4j class CoreTradesService { + private final Supplier> dateComparator = () -> + comparing(TradeModel::getDate); + private final CoreContext coreContext; // Dependencies on core api services in this package must be kept to an absolute // minimum, but some trading functions require an unlocked wallet's key, so an @@ -292,7 +298,10 @@ Trade getTrade(String tradeId) { List getOpenTrades() { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); - return tradeManager.getTrades().stream().map(t -> (TradeModel) t).collect(Collectors.toList()); + return tradeManager.getTrades().stream() + .map(t -> (TradeModel) t) + .sorted(dateComparator.get()) + .collect(Collectors.toList()); } List getTradeHistory(GetTradesRequest.Category category) { @@ -303,16 +312,18 @@ List getTradeHistory(GetTradesRequest.Category category) { .map(t -> (TradeModel) t) .collect(Collectors.toList()); closedTrades.addAll(bsqSwapTradeManager.getBsqSwapTrades()); - // TODO Sort closedTrades by date? - return closedTrades; + return closedTrades.stream().sorted(dateComparator.get()).collect(Collectors.toList()); } else { var failedV1Trades = failedTradesManager.getTrades(); - return failedV1Trades.stream().map(t -> (TradeModel) t).collect(Collectors.toList()); + return failedV1Trades.stream() + .map(t -> (TradeModel) t) + .sorted(dateComparator.get()) + .collect(Collectors.toList()); } } void failTrade(String tradeId) { - // TODO Recommend that API users should use this method with extra care because + // TODO Recommend API users call this method with extra care because // the API lacks methods for diagnosing trade problems, and does not support // interaction with mediators. Users may accidentally fail valid trades, // although they can easily be un-failed with the 'unfailtrade' method. From ad638e33cda4f3aa985285c5b3bc904287494a5a Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 27 Jan 2022 16:36:14 -0300 Subject: [PATCH 33/35] Revert "Sort API gettrades's response list by trade date" This reverts commit f231aacb3138b77a826413e4ada170c9a1b643e8. Sorting cumulative trade history lists should be done once, in GrpcTradesService. --- .../java/bisq/core/api/CoreTradesService.java | 21 +++++-------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index a169f27caae..f31efdce6d1 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -49,11 +49,9 @@ import javax.inject.Inject; import javax.inject.Singleton; -import java.util.Comparator; import java.util.List; import java.util.Optional; import java.util.function.Consumer; -import java.util.function.Supplier; import java.util.stream.Collectors; import lombok.extern.slf4j.Slf4j; @@ -61,15 +59,11 @@ import static bisq.core.btc.model.AddressEntry.Context.TRADE_PAYOUT; import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; import static java.lang.String.format; -import static java.util.Comparator.comparing; @Singleton @Slf4j class CoreTradesService { - private final Supplier> dateComparator = () -> - comparing(TradeModel::getDate); - private final CoreContext coreContext; // Dependencies on core api services in this package must be kept to an absolute // minimum, but some trading functions require an unlocked wallet's key, so an @@ -298,10 +292,7 @@ Trade getTrade(String tradeId) { List getOpenTrades() { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); - return tradeManager.getTrades().stream() - .map(t -> (TradeModel) t) - .sorted(dateComparator.get()) - .collect(Collectors.toList()); + return tradeManager.getTrades().stream().map(t -> (TradeModel) t).collect(Collectors.toList()); } List getTradeHistory(GetTradesRequest.Category category) { @@ -312,18 +303,16 @@ List getTradeHistory(GetTradesRequest.Category category) { .map(t -> (TradeModel) t) .collect(Collectors.toList()); closedTrades.addAll(bsqSwapTradeManager.getBsqSwapTrades()); - return closedTrades.stream().sorted(dateComparator.get()).collect(Collectors.toList()); + // TODO Sort closedTrades by date? + return closedTrades; } else { var failedV1Trades = failedTradesManager.getTrades(); - return failedV1Trades.stream() - .map(t -> (TradeModel) t) - .sorted(dateComparator.get()) - .collect(Collectors.toList()); + return failedV1Trades.stream().map(t -> (TradeModel) t).collect(Collectors.toList()); } } void failTrade(String tradeId) { - // TODO Recommend API users call this method with extra care because + // TODO Recommend that API users should use this method with extra care because // the API lacks methods for diagnosing trade problems, and does not support // interaction with mediators. Users may accidentally fail valid trades, // although they can easily be un-failed with the 'unfailtrade' method. From 820642516a64ed85b0b365dc8c22305d45c54951 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 27 Jan 2022 17:27:20 -0300 Subject: [PATCH 34/35] Remove unnecessary casts --- core/src/main/java/bisq/core/api/CoreTradesService.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index f31efdce6d1..db206e2b4b4 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -292,7 +292,7 @@ Trade getTrade(String tradeId) { List getOpenTrades() { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); - return tradeManager.getTrades().stream().map(t -> (TradeModel) t).collect(Collectors.toList()); + return tradeManager.getTrades().stream().collect(Collectors.toList()); } List getTradeHistory(GetTradesRequest.Category category) { @@ -303,16 +303,15 @@ List getTradeHistory(GetTradesRequest.Category category) { .map(t -> (TradeModel) t) .collect(Collectors.toList()); closedTrades.addAll(bsqSwapTradeManager.getBsqSwapTrades()); - // TODO Sort closedTrades by date? return closedTrades; } else { var failedV1Trades = failedTradesManager.getTrades(); - return failedV1Trades.stream().map(t -> (TradeModel) t).collect(Collectors.toList()); + return failedV1Trades.stream().collect(Collectors.toList()); } } void failTrade(String tradeId) { - // TODO Recommend that API users should use this method with extra care because + // TODO Recommend API users call this method with extra care because // the API lacks methods for diagnosing trade problems, and does not support // interaction with mediators. Users may accidentally fail valid trades, // although they can easily be un-failed with the 'unfailtrade' method. From 111e39ffe877d38d6489ab66c16224d2ac7f79a6 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 27 Jan 2022 17:36:16 -0300 Subject: [PATCH 35/35] Sort the final, cumulative trades list in gRPC GetTrades service Resolves issue https://github.com/bisq-network/bisq/pull/5976#issuecomment-1023004215 --- .../bisq/daemon/grpc/GrpcTradesService.java | 20 ++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 87de7b6f477..9331e154cef 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -64,6 +64,7 @@ import static bisq.proto.grpc.GetTradesRequest.Category.CLOSED; import static bisq.proto.grpc.GetTradesRequest.Category.OPEN; import static bisq.proto.grpc.TradesGrpc.*; +import static java.util.Comparator.comparing; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -303,8 +304,9 @@ private GetTradeReply buildGetTradeReply(Trade trade) { private GetTradesReply buildGetTradesReply(List trades, GetTradesRequest.Category category) { - // Build trade history list, starting with closed BsqSwap and v1 trades. - List result = trades.stream() + // Build an unsorted List, starting with + // all pending, or all completed BsqSwap and v1 trades. + List unsortedTrades = trades.stream() .map(tradeModel -> { var role = coreApi.getTradeRole(tradeModel); var isMyOffer = coreApi.isMyOffer(tradeModel.getOffer()); @@ -321,7 +323,8 @@ private GetTradesReply buildGetTradesReply(List trades, GetTradesReq }) .collect(Collectors.toList()); - // Add canceled OpenOffers to returned closed trades list. + // If closed trades were requested, add any canceled + // OpenOffers (canceled trades) to the unsorted List. Optional> canceledOpenOffers = category.equals(CLOSED) ? Optional.of(coreApi.getCanceledOpenOffers()) : Optional.empty(); @@ -331,12 +334,15 @@ private GetTradesReply buildGetTradesReply(List trades, GetTradesReq .map(CanceledTradeInfo::toCanceledTradeInfo) .collect(Collectors.toList()) )); + unsortedTrades.addAll(canceledTrades); + + // Sort the cumulative List by date before sending it to the client. + List sortedTrades = unsortedTrades.stream() + .sorted(comparing(TradeInfo::getDate)) + .collect(Collectors.toList()); return GetTradesReply.newBuilder() - .addAllTrades(result.stream() - .map(TradeInfo::toProtoMessage) - .collect(Collectors.toList())) - .addAllTrades(canceledTrades.stream() + .addAllTrades(sortedTrades.stream() .map(TradeInfo::toProtoMessage) .collect(Collectors.toList())) .build();