From 5924312e6487e36d771f75e5f16349df63444908 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 11 Nov 2021 12:51:00 -0300 Subject: [PATCH 01/39] Adjust bsq-swap-offer related gRPC services & msgs - Adjust GetBsqSwapOffer(s) rpc services to remove currency param. - Adjust OfferInfo and remove BsqSwapOfferInfo. - Add GetOfferCategory service so CLI can determine what kind of takeoffer service is to be used. - Add comment about adding sub-message BsqSwapTradeInfo field to TradeInfo. --- proto/src/main/proto/grpc.proto | 101 +++++++++++++++++--------------- 1 file changed, 53 insertions(+), 48 deletions(-) diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index ec8235b4042..e050f951051 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -62,6 +62,8 @@ message GetMethodHelpReply { /////////////////////////////////////////////////////////////////////////////////////////// service Offers { + rpc GetOfferCategory (GetOfferCategoryRequest) returns (GetOfferCategoryReply) { + } rpc GetBsqSwapOffer (GetOfferRequest) returns (GetBsqSwapOfferReply) { } rpc GetOffer (GetOfferRequest) returns (GetOfferReply) { @@ -70,11 +72,11 @@ service Offers { } rpc GetMyOffer (GetMyOfferRequest) returns (GetMyOfferReply) { } - rpc GetBsqSwapOffers (GetOffersRequest) returns (GetBsqSwapOffersReply) { + rpc GetBsqSwapOffers (GetBsqSwapOffersRequest) returns (GetBsqSwapOffersReply) { } rpc GetOffers (GetOffersRequest) returns (GetOffersReply) { } - rpc GetMyBsqSwapOffers (GetMyOffersRequest) returns (GetMyBsqSwapOffersReply) { + rpc GetMyBsqSwapOffers (GetBsqSwapOffersRequest) returns (GetMyBsqSwapOffersReply) { } rpc GetMyOffers (GetMyOffersRequest) returns (GetMyOffersReply) { } @@ -88,8 +90,22 @@ service Offers { } } +message GetOfferCategoryRequest { + string id = 1; +} + +message GetOfferCategoryReply { + enum OfferCategory { + UNKNOWN = 0; + FIAT = 1; + ALTCOIN = 2; + BSQ_SWAP = 3; + } + OfferCategory offerCategory = 1; +} + message GetBsqSwapOfferReply { - BsqSwapOfferInfo bsqSwapOffer = 1; + OfferInfo bsqSwapOffer = 1; } message GetOfferRequest { @@ -101,7 +117,7 @@ message GetOfferReply { } message GetMyBsqSwapOfferReply { - BsqSwapOfferInfo bsqSwapOffer = 1; + OfferInfo bsqSwapOffer = 1; } message GetMyOfferRequest { @@ -121,8 +137,12 @@ message GetOffersReply { repeated OfferInfo offers = 1; } +message GetBsqSwapOffersRequest { + string direction = 1; +} + message GetBsqSwapOffersReply { - repeated BsqSwapOfferInfo bsqSwapOffers = 1; + repeated OfferInfo bsqSwapOffers = 1; } message GetMyOffersRequest { @@ -135,7 +155,7 @@ message GetMyOffersReply { } message GetMyBsqSwapOffersReply { - repeated BsqSwapOfferInfo bsqSwapOffers = 1; + repeated OfferInfo bsqSwapOffers = 1; } message CreateBsqSwapOfferRequest { @@ -147,7 +167,7 @@ message CreateBsqSwapOfferRequest { } message CreateBsqSwapOfferReply { - BsqSwapOfferInfo bsqSwapOffer = 1; + OfferInfo bsqSwapOffer = 1; } message CreateOfferRequest { @@ -204,25 +224,6 @@ message CancelOfferRequest { message CancelOfferReply { } -message BsqSwapOfferInfo { - string id = 1; - string direction = 2; - uint64 amount = 3; - uint64 minAmount = 4; - uint64 price = 5; - string makerPaymentAccountId = 6; - string paymentMethodId = 7; - string paymentMethodShortName = 8; - string baseCurrencyCode = 9; - string counterCurrencyCode = 10; - uint64 getMakerFee = 11; - uint64 date = 12; - string ownerNodeAddress = 13; - string pubKeyRing = 14; - string versionNr = 15; - int32 protocolVersion = 16; -} - message OfferInfo { string id = 1; string direction = 2; @@ -250,6 +251,11 @@ message OfferInfo { bool isActivated = 24; bool isMyOffer = 25; bool isMyPendingOffer = 26; + bool isBsqSwapOffer = 27; + string ownerNodeAddress = 28; + string pubKeyRing = 29; + string versionNr = 30; + int32 protocolVersion = 31; } message AvailabilityResultWithDescription { @@ -460,33 +466,30 @@ message WithdrawFundsReply { } message BsqSwapTradeInfo { - BsqSwapOfferInfo bsqSwapOfferInfo = 1; + OfferInfo offer = 1; string tradeId = 2; string tempTradingPeerNodeAddress = 3; string peerNodeAddress = 4; string txId = 5; uint64 bsqTradeAmount = 6; - uint64 bsqMaxTradeAmount = 7; - uint64 bsqMinTradeAmount = 8; - uint64 btcTradeAmount = 9; - uint64 btcMaxTradeAmount = 10; - uint64 btcMinTradeAmount = 11; - uint64 tradePrice = 12; - bool isCurrencyForMakerFeeBtc = 13; - bool isCurrencyForTakerFeeBtc = 14; - uint64 bsqMakerTradeFee = 15; - uint64 btcMakerTradeFee = 16; - uint64 bsqTakerTradeFee = 17; - uint64 btcTakerTradeFee = 18; - uint64 txFeePerVbyte = 19; - uint64 txFee = 20; - string makerBsqAddress = 21; - string makerBtcAddress = 22; - string takerBsqAddress = 23; - string takerBtcAddress = 24; - uint64 takeOfferDate = 25; - string state = 26; - string errorMessage = 27; + uint64 btcTradeAmount = 7; + uint64 tradePrice = 8; + bool isCurrencyForMakerFeeBtc = 9; + bool isCurrencyForTakerFeeBtc = 10; + uint64 bsqMakerTradeFee = 11; + uint64 btcMakerTradeFee = 12; + uint64 bsqTakerTradeFee = 13; + uint64 btcTakerTradeFee = 14; + uint64 txFeePerVbyte = 15; + uint64 txFee = 16; + string makerBsqAddress = 17; + string makerBtcAddress = 18; + string takerBsqAddress = 19; + string takerBtcAddress = 20; + uint64 takeOfferDate = 21; + string role = 22; + string state = 23; + string errorMessage = 24; } message TradeInfo { @@ -516,6 +519,8 @@ message TradeInfo { string contractAsJson = 24; ContractInfo contract = 25; uint64 tradeVolume = 26; + // TODO See if a sub-message BsqSwapTradeInfo field could be added here. + // Use the existing BsqSwapTradeInfo message, without the redundant fields. } message ContractInfo { From c6aceb0458a403a43106bd212f404ded3504c98b Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 11 Nov 2021 12:56:34 -0300 Subject: [PATCH 02/39] Adjust bsq-swap-offer related gRPC daemon services - Add GetOfferCategory service so CLI can determine what kind of takeoffer service is to be used. - Adjust to removal of BsqSwapOfferInfo proto. - Call new coreApi.getRole(BsqSwapTrade) before building BsqSwapTradeInfo proto. --- .../bisq/daemon/grpc/GrpcOffersService.java | 71 +++++++++++++------ .../bisq/daemon/grpc/GrpcTradesService.java | 18 +++-- 2 files changed, 63 insertions(+), 26 deletions(-) diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java index bafd4c17d03..4f2fef1556b 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java @@ -18,7 +18,6 @@ package bisq.daemon.grpc; import bisq.core.api.CoreApi; -import bisq.core.api.model.BsqSwapOfferInfo; import bisq.core.api.model.OfferInfo; import bisq.core.offer.Offer; import bisq.core.offer.OpenOffer; @@ -33,12 +32,15 @@ import bisq.proto.grpc.EditOfferRequest; import bisq.proto.grpc.GetBsqSwapOfferReply; import bisq.proto.grpc.GetBsqSwapOffersReply; +import bisq.proto.grpc.GetBsqSwapOffersRequest; import bisq.proto.grpc.GetMyBsqSwapOfferReply; import bisq.proto.grpc.GetMyBsqSwapOffersReply; import bisq.proto.grpc.GetMyOfferReply; import bisq.proto.grpc.GetMyOfferRequest; import bisq.proto.grpc.GetMyOffersReply; import bisq.proto.grpc.GetMyOffersRequest; +import bisq.proto.grpc.GetOfferCategoryReply; +import bisq.proto.grpc.GetOfferCategoryRequest; import bisq.proto.grpc.GetOfferReply; import bisq.proto.grpc.GetOfferRequest; import bisq.proto.grpc.GetOffersReply; @@ -56,10 +58,15 @@ import lombok.extern.slf4j.Slf4j; -import static bisq.core.api.model.BsqSwapOfferInfo.toBsqSwapOfferInfo; +import static bisq.core.api.model.OfferInfo.toMyPendingOfferInfo; import static bisq.core.api.model.OfferInfo.toOfferInfo; -import static bisq.core.api.model.OfferInfo.toPendingOfferInfo; import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.ALTCOIN; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.BSQ_SWAP; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.FIAT; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.UNKNOWN; +import static bisq.proto.grpc.GetOfferCategoryReply.newBuilder; import static bisq.proto.grpc.OffersGrpc.*; import static java.util.concurrent.TimeUnit.MINUTES; import static java.util.concurrent.TimeUnit.SECONDS; @@ -81,13 +88,28 @@ public GrpcOffersService(CoreApi coreApi, GrpcExceptionHandler exceptionHandler) this.exceptionHandler = exceptionHandler; } + @Override + public void getOfferCategory(GetOfferCategoryRequest req, + StreamObserver responseObserver) { + try { + OfferCategory category = getAvailableOfferCategory(req.getId()); + var reply = newBuilder() + .setOfferCategory(category) + .build(); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + } catch (Throwable cause) { + exceptionHandler.handleException(log, cause, responseObserver); + } + } + @Override public void getBsqSwapOffer(GetOfferRequest req, StreamObserver responseObserver) { try { Offer offer = coreApi.getOffer(req.getId()); var reply = GetBsqSwapOfferReply.newBuilder() - .setBsqSwapOffer(toBsqSwapOfferInfo(offer).toProtoMessage()) + .setBsqSwapOffer(toOfferInfo(offer).toProtoMessage()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -117,7 +139,7 @@ public void getMyBsqSwapOffer(GetMyOfferRequest req, try { Offer offer = coreApi.getMyBsqSwapOffer(req.getId()); var reply = GetMyBsqSwapOfferReply.newBuilder() - .setBsqSwapOffer(toBsqSwapOfferInfo(offer /* TODO support triggerPrice */).toProtoMessage()) + .setBsqSwapOffer(toOfferInfo(offer /* TODO support triggerPrice */).toProtoMessage()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -132,7 +154,7 @@ public void getMyOffer(GetMyOfferRequest req, try { OpenOffer openOffer = coreApi.getMyOffer(req.getId()); var reply = GetMyOfferReply.newBuilder() - .setOffer(toOfferInfo(openOffer).toProtoMessage()) + .setOffer(OfferInfo.toMyOfferInfo(openOffer).toProtoMessage()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -142,15 +164,15 @@ public void getMyOffer(GetMyOfferRequest req, } @Override - public void getBsqSwapOffers(GetOffersRequest req, + public void getBsqSwapOffers(GetBsqSwapOffersRequest req, StreamObserver responseObserver) { try { - List result = coreApi.getBsqSwapOffers(req.getDirection()) - .stream().map(BsqSwapOfferInfo::toBsqSwapOfferInfo) + List result = coreApi.getBsqSwapOffers(req.getDirection()) + .stream().map(OfferInfo::toOfferInfo) .collect(Collectors.toList()); var reply = GetBsqSwapOffersReply.newBuilder() .addAllBsqSwapOffers(result.stream() - .map(BsqSwapOfferInfo::toProtoMessage) + .map(OfferInfo::toProtoMessage) .collect(Collectors.toList())) .build(); responseObserver.onNext(reply); @@ -180,15 +202,15 @@ public void getOffers(GetOffersRequest req, } @Override - public void getMyBsqSwapOffers(GetMyOffersRequest req, + public void getMyBsqSwapOffers(GetBsqSwapOffersRequest req, StreamObserver responseObserver) { try { - List result = coreApi.getMyBsqSwapOffers(req.getDirection()) - .stream().map(BsqSwapOfferInfo::toBsqSwapOfferInfo) + List result = coreApi.getMyBsqSwapOffers(req.getDirection()) + .stream().map(OfferInfo::toOfferInfo) .collect(Collectors.toList()); var reply = GetMyBsqSwapOffersReply.newBuilder() .addAllBsqSwapOffers(result.stream() - .map(BsqSwapOfferInfo::toProtoMessage) + .map(OfferInfo::toProtoMessage) .collect(Collectors.toList())) .build(); responseObserver.onNext(reply); @@ -204,7 +226,7 @@ public void getMyOffers(GetMyOffersRequest req, try { List result = coreApi.getMyOffers(req.getDirection(), req.getCurrencyCode()) .stream() - .map(OfferInfo::toOfferInfo) + .map(OfferInfo::toMyOfferInfo) .collect(Collectors.toList()); var reply = GetMyOffersReply.newBuilder() .addAllOffers(result.stream() @@ -222,17 +244,15 @@ public void getMyOffers(GetMyOffersRequest req, public void createBsqSwapOffer(CreateBsqSwapOfferRequest req, StreamObserver responseObserver) { try { - //todo PaymentAccount for bsq swap not needed as its just a dummy account coreApi.createAndPlaceBsqSwapOffer( req.getDirection(), req.getAmount(), req.getMinAmount(), req.getPrice(), - /* req.getPaymentAccountId(),*/ offer -> { - BsqSwapOfferInfo bsqSwapOfferInfo = toBsqSwapOfferInfo(offer); + OfferInfo offerInfo = toMyPendingOfferInfo(offer); CreateBsqSwapOfferReply reply = CreateBsqSwapOfferReply.newBuilder() - .setBsqSwapOffer(bsqSwapOfferInfo.toProtoMessage()) + .setBsqSwapOffer(offerInfo.toProtoMessage()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -261,7 +281,7 @@ public void createOffer(CreateOfferRequest req, offer -> { // This result handling consumer's accept operation will return // the new offer to the gRPC client after async placement is done. - OfferInfo offerInfo = toPendingOfferInfo(offer); + OfferInfo offerInfo = toMyPendingOfferInfo(offer); CreateOfferReply reply = CreateOfferReply.newBuilder() .setOffer(offerInfo.toProtoMessage()) .build(); @@ -325,4 +345,15 @@ final Optional rateMeteringInterceptor() { }} ))); } + + private OfferCategory getAvailableOfferCategory(String offerId) { + if (coreApi.isAvailableAltcoinOffer(offerId)) + return ALTCOIN; + else if (coreApi.isAvailableFiatOffer(offerId)) + return FIAT; + else if (coreApi.isAvailableBsqSwapOffer(offerId)) + return BSQ_SWAP; + else + return UNKNOWN; + } } diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 5e712e253e1..00ec52305a9 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -78,9 +78,10 @@ public void getBsqSwapTrade(GetTradeRequest req, StreamObserver responseObserver) { try { var bsqSwapTrade = coreApi.getBsqSwapTrade(req.getTradeId()); - // String role = coreApi.getBsqSwapTradeRole(req.getTradeId()); + boolean wasMyOffer = coreApi.isMyOffer(bsqSwapTrade.getOffer().getId()); + String role = coreApi.getBsqSwapTradeRole(req.getTradeId()); var reply = GetBsqSwapTradeReply.newBuilder() - .setBsqSwapTrade(toBsqSwapTradeInfo(bsqSwapTrade).toProtoMessage()) + .setBsqSwapTrade(toBsqSwapTradeInfo(bsqSwapTrade, role, wasMyOffer).toProtoMessage()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -97,7 +98,7 @@ public void getBsqSwapTrade(GetTradeRequest req, public void takeBsqSwapOffer(TakeBsqSwapOfferRequest req, StreamObserver responseObserver) { GrpcErrorMessageHandler errorMessageHandler = - new GrpcErrorMessageHandler(getTakeOfferMethod().getFullMethodName(), + new GrpcErrorMessageHandler(getTakeBsqSwapOfferMethod().getFullMethodName(), responseObserver, exceptionHandler, log); @@ -105,7 +106,10 @@ public void takeBsqSwapOffer(TakeBsqSwapOfferRequest req, req.getPaymentAccountId(), req.getTakerFeeCurrencyCode(), bsqSwapTrade -> { - BsqSwapTradeInfo bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade); + String role = coreApi.getBsqSwapTradeRole(bsqSwapTrade); + BsqSwapTradeInfo bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, + role, + false); var reply = TakeBsqSwapOfferReply.newBuilder() .setBsqSwapTrade(bsqSwapTradeInfo.toProtoMessage()) .build(); @@ -123,10 +127,10 @@ public void getTrade(GetTradeRequest req, StreamObserver responseObserver) { try { Trade trade = coreApi.getTrade(req.getTradeId()); - boolean isMyOffer = coreApi.isMyOffer(trade.getOffer().getId()); + boolean wasMyOffer = coreApi.isMyOffer(trade.getOffer().getId()); String role = coreApi.getTradeRole(req.getTradeId()); var reply = GetTradeReply.newBuilder() - .setTrade(toTradeInfo(trade, role, isMyOffer).toProtoMessage()) + .setTrade(toTradeInfo(trade, role, wasMyOffer).toProtoMessage()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -227,7 +231,9 @@ final Optional rateMeteringInterceptor() { .or(() -> Optional.of(CallRateMeteringInterceptor.valueOf( new HashMap<>() {{ put(getGetTradeMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); + put(getGetBsqSwapTradeMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getTakeOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); + put(getTakeBsqSwapOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getConfirmPaymentStartedMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getConfirmPaymentReceivedMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getKeepFundsMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); From 5d63fd7e3952d00f71e264a5b9e26b5b1ba72204 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 11 Nov 2021 13:01:48 -0300 Subject: [PATCH 03/39] Partially adjust api proto wrappers for bsq-swap support - Complete BsqSwapTradeInfo impl. - Merge BsqSwapOfferInfo fields into OfferInfo and remove BsqSwapOfferInfo. - Change all model builders to private static class Builder. --- .../bisq/core/api/model/BsqSwapOfferInfo.java | 227 ------------------ .../bisq/core/api/model/BsqSwapTradeInfo.java | 154 +++++------- .../java/bisq/core/api/model/OfferInfo.java | 155 ++++++++---- .../java/bisq/core/api/model/TradeInfo.java | 66 ++--- .../main/java/bisq/core/api/model/TxInfo.java | 24 +- 5 files changed, 216 insertions(+), 410 deletions(-) delete mode 100644 core/src/main/java/bisq/core/api/model/BsqSwapOfferInfo.java diff --git a/core/src/main/java/bisq/core/api/model/BsqSwapOfferInfo.java b/core/src/main/java/bisq/core/api/model/BsqSwapOfferInfo.java deleted file mode 100644 index 487e4fbf23f..00000000000 --- a/core/src/main/java/bisq/core/api/model/BsqSwapOfferInfo.java +++ /dev/null @@ -1,227 +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.core.api.model; - -import bisq.core.offer.Offer; - -import bisq.common.Payload; - -import java.util.Objects; - -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; - -@EqualsAndHashCode -@ToString -@Getter -public class BsqSwapOfferInfo implements Payload { - private final String id; - private final String direction; - private final long amount; - private final long minAmount; - private final long price; - private final String makerPaymentAccountId; - private final String paymentMethodId; - private final String paymentMethodShortName; - private final String baseCurrencyCode; - private final String counterCurrencyCode; - private final long date; - private final String ownerNodeAddress; - private final String pubKeyRing; // TODO ? - private final String versionNumber; - private final int protocolVersion; - - public BsqSwapOfferInfo(BsqSwapOfferInfoBuilder builder) { - this.id = builder.id; - this.direction = builder.direction; - this.amount = builder.amount; - this.minAmount = builder.minAmount; - this.price = builder.price; - this.makerPaymentAccountId = builder.makerPaymentAccountId; - this.paymentMethodId = builder.paymentMethodId; - this.paymentMethodShortName = builder.paymentMethodShortName; - this.baseCurrencyCode = builder.baseCurrencyCode; - this.counterCurrencyCode = builder.counterCurrencyCode; - this.date = builder.date; - this.ownerNodeAddress = builder.ownerNodeAddress; - this.pubKeyRing = builder.pubKeyRing; - this.versionNumber = builder.versionNumber; - this.protocolVersion = builder.protocolVersion; - } - - public static BsqSwapOfferInfo toBsqSwapOfferInfo(Offer offer) { - // TODO support triggerPrice - return getAtomicOfferInfoBuilder(offer).build(); - } - - private static BsqSwapOfferInfoBuilder getAtomicOfferInfoBuilder(Offer offer) { - return new BsqSwapOfferInfoBuilder() - .withId(offer.getId()) - .withDirection(offer.getDirection().name()) - .withAmount(offer.getAmount().value) - .withMinAmount(offer.getMinAmount().value) - .withPrice(Objects.requireNonNull(offer.getPrice()).getValue()) - //.withMakerPaymentAccountId(offer.getOfferPayloadI().getMakerPaymentAccountId()) - //.withPaymentMethodId(offer.getOfferPayloadI().getPaymentMethodId()) - //.withPaymentMethodShortName(getPaymentMethodById(offer.getOfferPayloadI().getPaymentMethodId()).getShortName()) - .withBaseCurrencyCode(offer.getOfferPayloadBase().getBaseCurrencyCode()) - .withCounterCurrencyCode(offer.getOfferPayloadBase().getCounterCurrencyCode()) - .withDate(offer.getDate().getTime()) - .withOwnerNodeAddress(offer.getOfferPayloadBase().getOwnerNodeAddress().getFullAddress()) - .withPubKeyRing(offer.getOfferPayloadBase().getPubKeyRing().toString()) - .withVersionNumber(offer.getOfferPayloadBase().getVersionNr()) - .withProtocolVersion(offer.getOfferPayloadBase().getProtocolVersion()); - } - - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// - - @Override - public bisq.proto.grpc.BsqSwapOfferInfo toProtoMessage() { - return bisq.proto.grpc.BsqSwapOfferInfo.newBuilder() - .setId(id) - .setDirection(direction) - .setAmount(amount) - .setMinAmount(minAmount) - .setPrice(price) - .setBaseCurrencyCode(baseCurrencyCode) - .setCounterCurrencyCode(counterCurrencyCode) - .setDate(date) - .setOwnerNodeAddress(ownerNodeAddress) - .setPubKeyRing(pubKeyRing) - .setVersionNr(versionNumber) - .setProtocolVersion(protocolVersion) - .build(); - } - - public static BsqSwapOfferInfo fromProto(bisq.proto.grpc.BsqSwapOfferInfo proto) { - return new BsqSwapOfferInfoBuilder() - .withId(proto.getId()) - .withDirection(proto.getDirection()) - .withAmount(proto.getAmount()) - .withMinAmount(proto.getMinAmount()) - .withPrice(proto.getPrice()) - .withBaseCurrencyCode(proto.getBaseCurrencyCode()) - .withCounterCurrencyCode(proto.getCounterCurrencyCode()) - .withDate(proto.getDate()) - .withOwnerNodeAddress(proto.getOwnerNodeAddress()) - .withPubKeyRing(proto.getPubKeyRing()) - .withVersionNumber(proto.getVersionNr()) - .withProtocolVersion(proto.getProtocolVersion()) - .build(); - } - - public static class BsqSwapOfferInfoBuilder { - private String id; - private String direction; - private long amount; - private long minAmount; - private long price; - private String makerPaymentAccountId; - private String paymentMethodId; - private String paymentMethodShortName; - private String baseCurrencyCode; - private String counterCurrencyCode; - private long date; - private String ownerNodeAddress; - private String pubKeyRing; - private String versionNumber; - private int protocolVersion; - - public BsqSwapOfferInfoBuilder withId(String id) { - this.id = id; - return this; - } - - public BsqSwapOfferInfoBuilder withDirection(String direction) { - this.direction = direction; - return this; - } - - public BsqSwapOfferInfoBuilder withAmount(long amount) { - this.amount = amount; - return this; - } - - public BsqSwapOfferInfoBuilder withMinAmount(long minAmount) { - this.minAmount = minAmount; - return this; - } - - public BsqSwapOfferInfoBuilder withPrice(long price) { - this.price = price; - return this; - } - - public BsqSwapOfferInfoBuilder withMakerPaymentAccountId(String makerPaymentAccountId) { - this.makerPaymentAccountId = makerPaymentAccountId; - return this; - } - - public BsqSwapOfferInfoBuilder withPaymentMethodId(String paymentMethodId) { - this.paymentMethodId = paymentMethodId; - return this; - } - - public BsqSwapOfferInfoBuilder withPaymentMethodShortName(String paymentMethodShortName) { - this.paymentMethodShortName = paymentMethodShortName; - return this; - } - - public BsqSwapOfferInfoBuilder withBaseCurrencyCode(String baseCurrencyCode) { - this.baseCurrencyCode = baseCurrencyCode; - return this; - } - - public BsqSwapOfferInfoBuilder withCounterCurrencyCode(String counterCurrencyCode) { - this.counterCurrencyCode = counterCurrencyCode; - return this; - } - - public BsqSwapOfferInfoBuilder withDate(long date) { - this.date = date; - return this; - } - - public BsqSwapOfferInfoBuilder withOwnerNodeAddress(String ownerNodeAddress) { - this.ownerNodeAddress = ownerNodeAddress; - return this; - } - - public BsqSwapOfferInfoBuilder withPubKeyRing(String pubKeyRing) { - this.pubKeyRing = pubKeyRing; - return this; - } - - public BsqSwapOfferInfoBuilder withVersionNumber(String versionNumber) { - this.versionNumber = versionNumber; - return this; - } - - public BsqSwapOfferInfoBuilder withProtocolVersion(int protocolVersion) { - this.protocolVersion = protocolVersion; - return this; - } - - public BsqSwapOfferInfo build() { - return new BsqSwapOfferInfo(this); - } - } -} 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 c24dcca0163..ce1fa745ebb 100644 --- a/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java @@ -25,24 +25,21 @@ import lombok.Getter; import lombok.ToString; -import static bisq.core.api.model.BsqSwapOfferInfo.toBsqSwapOfferInfo; +import static bisq.core.api.model.OfferInfo.toMyOfferInfo; +import static bisq.core.api.model.OfferInfo.toOfferInfo; @EqualsAndHashCode @ToString @Getter public class BsqSwapTradeInfo implements Payload { - private final BsqSwapOfferInfo bsqSwapOffer; + private final OfferInfo bsqSwapOffer; private final String tradeId; private final String tempTradingPeerNodeAddress; private final String peerNodeAddress; private final String txId; private final long bsqTradeAmount; - private final long bsqMaxTradeAmount; - private final long bsqMinTradeAmount; private final long btcTradeAmount; - private final long btcMaxTradeAmount; - private final long btcMinTradeAmount; private final long tradePrice; private final long bsqMakerTradeFee; private final long bsqTakerTradeFee; @@ -53,21 +50,18 @@ public class BsqSwapTradeInfo implements Payload { private final String takerBsqAddress; private final String takerBtcAddress; private final long takeOfferDate; + private final String role; private final String state; private final String errorMessage; - public BsqSwapTradeInfo(BsqSwapTradeInfoBuilder builder) { - this.bsqSwapOffer = builder.bsqSwapOfferInfo; + public BsqSwapTradeInfo(Builder builder) { + this.bsqSwapOffer = builder.bsqSwapOffer; this.tradeId = builder.tradeId; this.tempTradingPeerNodeAddress = builder.tempTradingPeerNodeAddress; this.peerNodeAddress = builder.peerNodeAddress; this.txId = builder.txId; this.bsqTradeAmount = builder.bsqTradeAmount; - this.bsqMaxTradeAmount = builder.bsqMaxTradeAmount; - this.bsqMinTradeAmount = builder.bsqMinTradeAmount; this.btcTradeAmount = builder.btcTradeAmount; - this.btcMaxTradeAmount = builder.btcMaxTradeAmount; - this.btcMinTradeAmount = builder.btcMinTradeAmount; this.tradePrice = builder.tradePrice; this.bsqMakerTradeFee = builder.bsqMakerTradeFee; this.bsqTakerTradeFee = builder.bsqTakerTradeFee; @@ -78,38 +72,38 @@ public BsqSwapTradeInfo(BsqSwapTradeInfoBuilder builder) { this.takerBsqAddress = builder.takerBsqAddress; this.takerBtcAddress = builder.takerBtcAddress; this.takeOfferDate = builder.takeOfferDate; + this.role = builder.role; this.state = builder.state; this.errorMessage = builder.errorMessage; } - public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade) { - return toBsqSwapTradeInfo(trade, null); - } - - //TODO - public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, String role) { - return new BsqSwapTradeInfoBuilder() - .withBsqSwapOffer(toBsqSwapOfferInfo(trade.getOffer())) + public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, String role, boolean wasMyOffer) { + var protocolModel = trade.getBsqSwapProtocolModel(); + var swapPeer = protocolModel.getTradePeer(); + var makerBsqAddress = wasMyOffer ? protocolModel.getBsqAddress() : swapPeer.getBsqAddress(); + var makerBtcAddress = wasMyOffer ? protocolModel.getBtcAddress() : swapPeer.getBtcAddress(); + var takerBsqAddress = wasMyOffer ? swapPeer.getBsqAddress() : protocolModel.getBsqAddress(); + var takerBtcAddress = wasMyOffer ? swapPeer.getBtcAddress() : protocolModel.getBtcAddress(); + var offerInfo = wasMyOffer ? toMyOfferInfo(trade.getOffer()) : toOfferInfo(trade.getOffer()); + return new Builder() + .withBsqSwapOffer(offerInfo) .withTradeId(trade.getId()) .withTempTradingPeerNodeAddress(trade.getBsqSwapProtocolModel().getTempTradingPeerNodeAddress().getFullAddress()) .withPeerNodeAddress(trade.getTradingPeerNodeAddress().getFullAddress()) .withTxId(trade.getTxId()) - /* .withBsqTradeAmount(trade.getBsqSwapProtocolModel().getBsqTradeAmount()) - .withBsqMaxTradeAmount(trade.getBsqSwapProtocolModel().getBsqMaxTradeAmount()) - .withBsqMinTradeAmount(trade.getBsqSwapProtocolModel().getBsqMinTradeAmount()) - .withBtcTradeAmount(trade.getBsqSwapProtocolModel().getBtcTradeAmount()) - .withBtcMaxTradeAmount(trade.getBsqSwapProtocolModel().getBtcMaxTradeAmount()) - .withBtcMinTradeAmount(trade.getBsqSwapProtocolModel().getBtcMinTradeAmount()) - .withTradePrice(trade.getBsqSwapProtocolModel().getTradePrice()) - .withBsqMakerTradeFee(trade.getBsqSwapProtocolModel().getBsqMakerTradeFee()) - .withBsqTakerTradeFee(trade.getBsqSwapProtocolModel().getBsqTakerTradeFee()) - .withTxFeePerVbyte(trade.getBsqSwapProtocolModel().getTxFeePerVbyte()) - .withTxFee(trade.getBsqSwapProtocolModel().getTxFee()) - .withMakerBsqAddress(trade.getBsqSwapProtocolModel().getMakerBsqAddress()) - .withMakerBtcAddress(trade.getBsqSwapProtocolModel().getMakerBtcAddress()) - .withTakerBsqAddress(trade.getBsqSwapProtocolModel().getTakerBsqAddress()) - .withTakerBtcAddress(trade.getBsqSwapProtocolModel().getTakerBtcAddress())*/ + .withBsqTradeAmount(trade.getBsqTradeAmount()) + .withBtcTradeAmount(trade.getAmountAsLong()) + .withTradePrice(trade.getPrice().getValue()) + .withBsqMakerTradeFee(trade.getMakerFeeAsLong()) + .withBsqTakerTradeFee(trade.getTakerFeeAsLong()) + .withTxFeePerVbyte(trade.getTxFeePerVbyte()) + .withTxFee(trade.getTxFee().value) + .withMakerBsqAddress(makerBsqAddress) + .withMakerBtcAddress(makerBtcAddress) + .withTakerBsqAddress(takerBsqAddress) + .withTakerBtcAddress(takerBtcAddress) .withTakeOfferDate(trade.getTakeOfferDate()) + .withRole(role == null ? "" : role) .withState(trade.getTradeState().name()) .withErrorMessage(trade.getErrorMessage()) .build(); @@ -122,17 +116,13 @@ public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, String rol @Override public bisq.proto.grpc.BsqSwapTradeInfo toProtoMessage() { return bisq.proto.grpc.BsqSwapTradeInfo.newBuilder() - .setBsqSwapOfferInfo(bsqSwapOffer.toProtoMessage()) + .setOffer(bsqSwapOffer.toProtoMessage()) .setTradeId(tradeId) .setTempTradingPeerNodeAddress(tempTradingPeerNodeAddress != null ? tempTradingPeerNodeAddress : "") .setPeerNodeAddress(peerNodeAddress != null ? peerNodeAddress : "") .setTxId(txId != null ? txId : "") .setBsqTradeAmount(bsqTradeAmount) - .setBsqMaxTradeAmount(bsqMaxTradeAmount) - .setBsqMinTradeAmount(bsqMinTradeAmount) .setBtcTradeAmount(btcTradeAmount) - .setBtcMaxTradeAmount(btcMaxTradeAmount) - .setBtcMinTradeAmount(btcMinTradeAmount) .setTradePrice(tradePrice) .setBsqMakerTradeFee(bsqMakerTradeFee) .setBsqTakerTradeFee(bsqTakerTradeFee) @@ -143,24 +133,21 @@ public bisq.proto.grpc.BsqSwapTradeInfo toProtoMessage() { .setMakerBtcAddress(makerBtcAddress != null ? makerBtcAddress : "") .setTakerBtcAddress(takerBtcAddress != null ? takerBtcAddress : "") .setTakeOfferDate(takeOfferDate) + .setRole(role) .setState(state) .setErrorMessage(errorMessage != null ? errorMessage : "") .build(); } public static BsqSwapTradeInfo fromProto(bisq.proto.grpc.BsqSwapTradeInfo proto) { - return new BsqSwapTradeInfoBuilder() - .withBsqSwapOffer(BsqSwapOfferInfo.fromProto(proto.getBsqSwapOfferInfo())) + return new Builder() + .withBsqSwapOffer(OfferInfo.fromProto(proto.getOffer())) .withTradeId(proto.getTradeId()) .withTempTradingPeerNodeAddress(proto.getTempTradingPeerNodeAddress()) .withPeerNodeAddress(proto.getPeerNodeAddress()) .withTxId(proto.getTxId()) .withBsqTradeAmount(proto.getBsqTradeAmount()) - .withBsqMaxTradeAmount(proto.getBsqMaxTradeAmount()) - .withBsqMinTradeAmount(proto.getBsqMinTradeAmount()) .withBtcTradeAmount(proto.getBtcTradeAmount()) - .withBtcMaxTradeAmount(proto.getBtcMaxTradeAmount()) - .withBtcMinTradeAmount(proto.getBtcMinTradeAmount()) .withTradePrice(proto.getTradePrice()) .withBsqMakerTradeFee(proto.getBsqMakerTradeFee()) .withBsqTakerTradeFee(proto.getBsqTakerTradeFee()) @@ -171,23 +158,20 @@ public static BsqSwapTradeInfo fromProto(bisq.proto.grpc.BsqSwapTradeInfo proto) .withTakerBsqAddress(proto.getTakerBsqAddress()) .withTakerBtcAddress(proto.getTakerBtcAddress()) .withTakeOfferDate(proto.getTakeOfferDate()) + .withRole(proto.getRole()) .withState(proto.getState()) .withErrorMessage(proto.getErrorMessage()) .build(); } - public static class BsqSwapTradeInfoBuilder { - private BsqSwapOfferInfo bsqSwapOfferInfo; + private static class Builder { + private OfferInfo bsqSwapOffer; private String tradeId; private String tempTradingPeerNodeAddress; private String peerNodeAddress; private String txId; private long bsqTradeAmount; - private long bsqMaxTradeAmount; - private long bsqMinTradeAmount; private long btcTradeAmount; - private long btcMaxTradeAmount; - private long btcMinTradeAmount; private long tradePrice; private long bsqMakerTradeFee; private long bsqTakerTradeFee; @@ -198,120 +182,106 @@ public static class BsqSwapTradeInfoBuilder { private String takerBsqAddress; private String takerBtcAddress; private long takeOfferDate; + private String role; private String state; private String errorMessage; - public BsqSwapTradeInfoBuilder withBsqSwapOffer(BsqSwapOfferInfo bsqSwapOfferInfo) { - this.bsqSwapOfferInfo = bsqSwapOfferInfo; + public Builder withBsqSwapOffer(OfferInfo bsqSwapOffer) { + this.bsqSwapOffer = bsqSwapOffer; return this; } - public BsqSwapTradeInfoBuilder withTradeId(String tradeId) { + public Builder withTradeId(String tradeId) { this.tradeId = tradeId; return this; } - public BsqSwapTradeInfoBuilder withTempTradingPeerNodeAddress(String tempTradingPeerNodeAddress) { + public Builder withTempTradingPeerNodeAddress(String tempTradingPeerNodeAddress) { this.tempTradingPeerNodeAddress = tempTradingPeerNodeAddress; return this; } - public BsqSwapTradeInfoBuilder withPeerNodeAddress(String peerNodeAddress) { + public Builder withPeerNodeAddress(String peerNodeAddress) { this.peerNodeAddress = peerNodeAddress; return this; } - public BsqSwapTradeInfoBuilder withTxId(String txId) { + public Builder withTxId(String txId) { this.txId = txId; return this; } - public BsqSwapTradeInfoBuilder withBsqTradeAmount(long bsqTradeAmount) { + public Builder withBsqTradeAmount(long bsqTradeAmount) { this.bsqTradeAmount = bsqTradeAmount; return this; } - public BsqSwapTradeInfoBuilder withBsqMaxTradeAmount(long bsqMaxTradeAmount) { - this.bsqMaxTradeAmount = bsqMaxTradeAmount; - return this; - } - - public BsqSwapTradeInfoBuilder withBsqMinTradeAmount(long bsqMinTradeAmount) { - this.bsqMinTradeAmount = bsqMinTradeAmount; - return this; - } - - public BsqSwapTradeInfoBuilder withBtcTradeAmount(long btcTradeAmount) { + public Builder withBtcTradeAmount(long btcTradeAmount) { this.btcTradeAmount = btcTradeAmount; return this; } - public BsqSwapTradeInfoBuilder withBtcMaxTradeAmount(long btcMaxTradeAmount) { - this.btcMaxTradeAmount = btcMaxTradeAmount; - return this; - } - - public BsqSwapTradeInfoBuilder withBtcMinTradeAmount(long btcMinTradeAmount) { - this.btcMinTradeAmount = btcMinTradeAmount; - return this; - } - - public BsqSwapTradeInfoBuilder withTradePrice(long tradePrice) { + public Builder withTradePrice(long tradePrice) { this.tradePrice = tradePrice; return this; } - public BsqSwapTradeInfoBuilder withBsqMakerTradeFee(long bsqMakerTradeFee) { + public Builder withBsqMakerTradeFee(long bsqMakerTradeFee) { this.bsqMakerTradeFee = bsqMakerTradeFee; return this; } - public BsqSwapTradeInfoBuilder withBsqTakerTradeFee(long bsqTakerTradeFee) { + public Builder withBsqTakerTradeFee(long bsqTakerTradeFee) { this.bsqTakerTradeFee = bsqTakerTradeFee; return this; } - public BsqSwapTradeInfoBuilder withTxFeePerVbyte(long txFeePerVbyte) { + public Builder withTxFeePerVbyte(long txFeePerVbyte) { this.txFeePerVbyte = txFeePerVbyte; return this; } - public BsqSwapTradeInfoBuilder withTxFee(long txFee) { + public Builder withTxFee(long txFee) { this.txFee = txFee; return this; } - public BsqSwapTradeInfoBuilder withMakerBsqAddress(String makerBsqAddress) { + public Builder withMakerBsqAddress(String makerBsqAddress) { this.makerBsqAddress = makerBsqAddress; return this; } - public BsqSwapTradeInfoBuilder withMakerBtcAddress(String makerBtcAddress) { + public Builder withMakerBtcAddress(String makerBtcAddress) { this.makerBtcAddress = makerBtcAddress; return this; } - public BsqSwapTradeInfoBuilder withTakerBsqAddress(String takerBsqAddress) { + public Builder withTakerBsqAddress(String takerBsqAddress) { this.takerBsqAddress = takerBsqAddress; return this; } - public BsqSwapTradeInfoBuilder withTakerBtcAddress(String takerBtcAddress) { + public Builder withTakerBtcAddress(String takerBtcAddress) { this.takerBtcAddress = takerBtcAddress; return this; } - public BsqSwapTradeInfoBuilder withTakeOfferDate(long takeOfferDate) { + public Builder withTakeOfferDate(long takeOfferDate) { this.takeOfferDate = takeOfferDate; return this; } - public BsqSwapTradeInfoBuilder withState(String state) { + public Builder withRole(String role) { + this.role = role; + return this; + } + + public Builder withState(String state) { this.state = state; return this; } - public BsqSwapTradeInfoBuilder withErrorMessage(String errorMessage) { + public Builder withErrorMessage(String errorMessage) { this.errorMessage = errorMessage; return this; } diff --git a/core/src/main/java/bisq/core/api/model/OfferInfo.java b/core/src/main/java/bisq/core/api/model/OfferInfo.java index 67cee988a3b..f84e69ad001 100644 --- a/core/src/main/java/bisq/core/api/model/OfferInfo.java +++ b/core/src/main/java/bisq/core/api/model/OfferInfo.java @@ -19,6 +19,7 @@ import bisq.core.offer.Offer; import bisq.core.offer.OpenOffer; +import bisq.core.util.coin.CoinUtil; import bisq.common.Payload; @@ -28,6 +29,8 @@ import lombok.Getter; import lombok.ToString; +import static java.util.Objects.requireNonNull; + @EqualsAndHashCode @ToString @Getter @@ -56,17 +59,22 @@ public class OfferInfo implements Payload { private final String paymentAccountId; private final String paymentMethodId; private final String paymentMethodShortName; - // For fiat offer the baseCurrencyCode is BTC and the counterCurrencyCode is the fiat currency - // For altcoin offers it is the opposite. baseCurrencyCode is the altcoin and the counterCurrencyCode is BTC. + // Fiat offer: baseCurrencyCode = BTC, counterCurrencyCode = fiat ccy code. + // Altcoin offer: baseCurrencyCode = altcoin ccy code, counterCurrencyCode = BTC. private final String baseCurrencyCode; private final String counterCurrencyCode; private final long date; private final String state; private final boolean isActivated; - private boolean isMyOffer; // Not final -- may be re-set after instantiation. + private final boolean isMyOffer; private final boolean isMyPendingOffer; + private final boolean isBsqSwapOffer; + private final String ownerNodeAddress; + private final String pubKeyRing; + private final String versionNumber; + private final int protocolVersion; - public OfferInfo(OfferInfoBuilder builder) { + public OfferInfo(Builder builder) { this.id = builder.id; this.direction = builder.direction; this.price = builder.price; @@ -93,39 +101,43 @@ public OfferInfo(OfferInfoBuilder builder) { this.isActivated = builder.isActivated; this.isMyOffer = builder.isMyOffer; this.isMyPendingOffer = builder.isMyPendingOffer; + this.isBsqSwapOffer = builder.isBsqSwapOffer; + this.ownerNodeAddress = builder.ownerNodeAddress; + this.pubKeyRing = builder.pubKeyRing; + this.versionNumber = builder.versionNumber; + this.protocolVersion = builder.protocolVersion; } - // Allow isMyOffer to be set on a new offer's OfferInfo instance. - public void setIsMyOffer(boolean isMyOffer) { - this.isMyOffer = isMyOffer; + public static OfferInfo toMyOfferInfo(Offer offer) { + return getBuilder(offer, true).build(); } public static OfferInfo toOfferInfo(Offer offer) { // Assume the offer is not mine, but isMyOffer can be reset to true, i.e., when // calling TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer); - return getOfferInfoBuilder(offer, false).build(); + return getBuilder(offer, false).build(); } - public static OfferInfo toPendingOfferInfo(Offer myNewOffer) { + public static OfferInfo toMyPendingOfferInfo(Offer myNewOffer) { // Use this to build an OfferInfo instance when a new OpenOffer is being // prepared, and no valid OpenOffer state (AVAILABLE, DEACTIVATED) exists. // It is needed for the CLI's 'createoffer' output, which has a boolean 'ENABLED' // column that will show a PENDING value when this.isMyPendingOffer = true. - return getOfferInfoBuilder(myNewOffer, true) + return getBuilder(myNewOffer, true) .withIsMyPendingOffer(true) .build(); } - public static OfferInfo toOfferInfo(OpenOffer openOffer) { + public static OfferInfo toMyOfferInfo(OpenOffer openOffer) { // An OpenOffer is always my offer. - return getOfferInfoBuilder(openOffer.getOffer(), true) + return getBuilder(openOffer.getOffer(), true) .withTriggerPrice(openOffer.getTriggerPrice()) .withIsActivated(!openOffer.isDeactivated()) .build(); } - private static OfferInfoBuilder getOfferInfoBuilder(Offer offer, boolean isMyOffer) { - return new OfferInfoBuilder() + private static Builder getBuilder(Offer offer, boolean isMyOffer) { + return new Builder() .withId(offer.getId()) .withDirection(offer.getDirection().name()) .withPrice(Objects.requireNonNull(offer.getPrice()).getValue()) @@ -135,7 +147,7 @@ private static OfferInfoBuilder getOfferInfoBuilder(Offer offer, boolean isMyOff .withMinAmount(offer.getMinAmount().value) .withVolume(Objects.requireNonNull(offer.getVolume()).getValue()) .withMinVolume(Objects.requireNonNull(offer.getMinVolume()).getValue()) - .withMakerFee(offer.getMakerFee().value) + .withMakerFee(getMakerFee(offer, isMyOffer)) .withTxFee(offer.getTxFee().value) .withOfferFeePaymentTxId(offer.getOfferFeePaymentTxId()) .withBuyerSecurityDeposit(offer.getBuyerSecurityDeposit().value) @@ -148,7 +160,18 @@ private static OfferInfoBuilder getOfferInfoBuilder(Offer offer, boolean isMyOff .withCounterCurrencyCode(offer.getCounterCurrencyCode()) .withDate(offer.getDate().getTime()) .withState(offer.getState().name()) - .withIsMyOffer(isMyOffer); + .withIsMyOffer(isMyOffer) + .withIsBsqSwapOffer(offer.isBsqSwapOffer()) + .withOwnerNodeAddress(offer.getOfferPayloadBase().getOwnerNodeAddress().getFullAddress()) + .withPubKeyRing(offer.getOfferPayloadBase().getPubKeyRing().toString()) + .withVersionNumber(offer.getOfferPayloadBase().getVersionNr()) + .withProtocolVersion(offer.getOfferPayloadBase().getProtocolVersion()); + } + + private static long getMakerFee(Offer offer, boolean isMyOffer) { + return isMyOffer + ? requireNonNull(CoinUtil.getMakerFee(false, offer.getAmount())).value + : 0; } /////////////////////////////////////////////////////////////////////////////////////////// @@ -169,7 +192,7 @@ public bisq.proto.grpc.OfferInfo toProtoMessage() { .setMinVolume(minVolume) .setMakerFee(makerFee) .setTxFee(txFee) - .setOfferFeePaymentTxId(offerFeePaymentTxId) + .setOfferFeePaymentTxId(isBsqSwapOffer ? "" : offerFeePaymentTxId) .setBuyerSecurityDeposit(buyerSecurityDeposit) .setSellerSecurityDeposit(sellerSecurityDeposit) .setTriggerPrice(triggerPrice) @@ -184,12 +207,17 @@ public bisq.proto.grpc.OfferInfo toProtoMessage() { .setIsActivated(isActivated) .setIsMyOffer(isMyOffer) .setIsMyPendingOffer(isMyPendingOffer) + .setIsBsqSwapOffer(isBsqSwapOffer) + .setOwnerNodeAddress(ownerNodeAddress) + .setPubKeyRing(pubKeyRing) + .setVersionNr(versionNumber) + .setProtocolVersion(protocolVersion) .build(); } @SuppressWarnings("unused") public static OfferInfo fromProto(bisq.proto.grpc.OfferInfo proto) { - return new OfferInfoBuilder() + return new Builder() .withId(proto.getId()) .withDirection(proto.getDirection()) .withPrice(proto.getPrice()) @@ -216,16 +244,21 @@ public static OfferInfo fromProto(bisq.proto.grpc.OfferInfo proto) { .withIsActivated(proto.getIsActivated()) .withIsMyOffer(proto.getIsMyOffer()) .withIsMyPendingOffer(proto.getIsMyPendingOffer()) + .withIsBsqSwapOffer(proto.getIsBsqSwapOffer()) + .withOwnerNodeAddress(proto.getOwnerNodeAddress()) + .withPubKeyRing(proto.getPubKeyRing()) + .withVersionNumber(proto.getVersionNr()) + .withProtocolVersion(proto.getProtocolVersion()) .build(); } /* - * OfferInfoBuilder helps avoid bungling use of a large OfferInfo constructor + * Builder helps avoid bungling use of a large OfferInfo constructor * argument list. If consecutive argument values of the same type are not * ordered correctly, the compiler won't complain but the resulting bugs could * be hard to find and fix. */ - public static class OfferInfoBuilder { + private static class Builder { private String id; private String direction; private long price; @@ -252,137 +285,167 @@ public static class OfferInfoBuilder { private boolean isActivated; private boolean isMyOffer; private boolean isMyPendingOffer; + private boolean isBsqSwapOffer; + private String ownerNodeAddress; + private String pubKeyRing; + private String versionNumber; + private int protocolVersion; - public OfferInfoBuilder withId(String id) { + public Builder withId(String id) { this.id = id; return this; } - public OfferInfoBuilder withDirection(String direction) { + public Builder withDirection(String direction) { this.direction = direction; return this; } - public OfferInfoBuilder withPrice(long price) { + public Builder withPrice(long price) { this.price = price; return this; } - public OfferInfoBuilder withUseMarketBasedPrice(boolean useMarketBasedPrice) { + public Builder withUseMarketBasedPrice(boolean useMarketBasedPrice) { this.useMarketBasedPrice = useMarketBasedPrice; return this; } - public OfferInfoBuilder withMarketPriceMargin(double useMarketBasedPrice) { + public Builder withMarketPriceMargin(double useMarketBasedPrice) { this.marketPriceMargin = useMarketBasedPrice; return this; } - public OfferInfoBuilder withAmount(long amount) { + public Builder withAmount(long amount) { this.amount = amount; return this; } - public OfferInfoBuilder withMinAmount(long minAmount) { + public Builder withMinAmount(long minAmount) { this.minAmount = minAmount; return this; } - public OfferInfoBuilder withVolume(long volume) { + public Builder withVolume(long volume) { this.volume = volume; return this; } - public OfferInfoBuilder withMinVolume(long minVolume) { + public Builder withMinVolume(long minVolume) { this.minVolume = minVolume; return this; } - public OfferInfoBuilder withTxFee(long txFee) { + public Builder withTxFee(long txFee) { this.txFee = txFee; return this; } - public OfferInfoBuilder withMakerFee(long makerFee) { + public Builder withMakerFee(long makerFee) { this.makerFee = makerFee; return this; } - public OfferInfoBuilder withOfferFeePaymentTxId(String offerFeePaymentTxId) { + public Builder withOfferFeePaymentTxId(String offerFeePaymentTxId) { this.offerFeePaymentTxId = offerFeePaymentTxId; return this; } - public OfferInfoBuilder withBuyerSecurityDeposit(long buyerSecurityDeposit) { + public Builder withBuyerSecurityDeposit(long buyerSecurityDeposit) { this.buyerSecurityDeposit = buyerSecurityDeposit; return this; } - public OfferInfoBuilder withSellerSecurityDeposit(long sellerSecurityDeposit) { + public Builder withSellerSecurityDeposit(long sellerSecurityDeposit) { this.sellerSecurityDeposit = sellerSecurityDeposit; return this; } - public OfferInfoBuilder withTriggerPrice(long triggerPrice) { + public Builder withTriggerPrice(long triggerPrice) { this.triggerPrice = triggerPrice; return this; } - public OfferInfoBuilder withIsCurrencyForMakerFeeBtc(boolean isCurrencyForMakerFeeBtc) { + public Builder withIsCurrencyForMakerFeeBtc(boolean isCurrencyForMakerFeeBtc) { this.isCurrencyForMakerFeeBtc = isCurrencyForMakerFeeBtc; return this; } - public OfferInfoBuilder withPaymentAccountId(String paymentAccountId) { + public Builder withPaymentAccountId(String paymentAccountId) { this.paymentAccountId = paymentAccountId; return this; } - public OfferInfoBuilder withPaymentMethodId(String paymentMethodId) { + public Builder withPaymentMethodId(String paymentMethodId) { this.paymentMethodId = paymentMethodId; return this; } - public OfferInfoBuilder withPaymentMethodShortName(String paymentMethodShortName) { + public Builder withPaymentMethodShortName(String paymentMethodShortName) { this.paymentMethodShortName = paymentMethodShortName; return this; } - public OfferInfoBuilder withBaseCurrencyCode(String baseCurrencyCode) { + public Builder withBaseCurrencyCode(String baseCurrencyCode) { this.baseCurrencyCode = baseCurrencyCode; return this; } - public OfferInfoBuilder withCounterCurrencyCode(String counterCurrencyCode) { + public Builder withCounterCurrencyCode(String counterCurrencyCode) { this.counterCurrencyCode = counterCurrencyCode; return this; } - public OfferInfoBuilder withDate(long date) { + public Builder withDate(long date) { this.date = date; return this; } - public OfferInfoBuilder withState(String state) { + public Builder withState(String state) { this.state = state; return this; } - public OfferInfoBuilder withIsActivated(boolean isActivated) { + public Builder withIsActivated(boolean isActivated) { this.isActivated = isActivated; return this; } - public OfferInfoBuilder withIsMyOffer(boolean isMyOffer) { + public Builder withIsMyOffer(boolean isMyOffer) { this.isMyOffer = isMyOffer; return this; } - public OfferInfoBuilder withIsMyPendingOffer(boolean isMyPendingOffer) { + public Builder withIsMyPendingOffer(boolean isMyPendingOffer) { this.isMyPendingOffer = isMyPendingOffer; return this; } + public Builder withIsBsqSwapOffer(boolean isBsqSwapOffer) { + this.isBsqSwapOffer = isBsqSwapOffer; + return this; + } + + public Builder withOwnerNodeAddress(String ownerNodeAddress) { + this.ownerNodeAddress = ownerNodeAddress; + return this; + } + + public Builder withPubKeyRing(String pubKeyRing) { + this.pubKeyRing = pubKeyRing; + return this; + } + + public Builder withVersionNumber(String versionNumber) { + this.versionNumber = versionNumber; + return this; + } + + public Builder withProtocolVersion(int protocolVersion) { + this.protocolVersion = protocolVersion; + return this; + } + public OfferInfo build() { return new OfferInfo(this); } 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 024eb69fa2f..3e2bc8b56ea 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -27,6 +27,7 @@ import lombok.EqualsAndHashCode; import lombok.Getter; +import static bisq.core.api.model.OfferInfo.toMyOfferInfo; import static bisq.core.api.model.OfferInfo.toOfferInfo; import static bisq.core.api.model.PaymentAccountPayloadInfo.toPaymentAccountPayloadInfo; @@ -65,7 +66,7 @@ public class TradeInfo implements Payload { private final String contractAsJson; private final ContractInfo contract; - public TradeInfo(TradeInfoBuilder builder) { + public TradeInfo(Builder builder) { this.offer = builder.offer; this.tradeId = builder.tradeId; this.shortId = builder.shortId; @@ -118,9 +119,8 @@ public static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer) contractInfo = ContractInfo.emptyContract.get(); } - OfferInfo offerInfo = toOfferInfo(trade.getOffer()); - offerInfo.setIsMyOffer(isMyOffer); - return new TradeInfoBuilder() + OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(trade.getOffer()) : toOfferInfo(trade.getOffer()); + return new Builder() .withOffer(offerInfo) .withTradeId(trade.getId()) .withShortId(trade.getShortId()) @@ -189,7 +189,7 @@ public bisq.proto.grpc.TradeInfo toProtoMessage() { } public static TradeInfo fromProto(bisq.proto.grpc.TradeInfo proto) { - return new TradeInfoBuilder() + return new Builder() .withOffer(OfferInfo.fromProto(proto.getOffer())) .withTradeId(proto.getTradeId()) .withShortId(proto.getShortId()) @@ -220,12 +220,12 @@ public static TradeInfo fromProto(bisq.proto.grpc.TradeInfo proto) { } /* - * TradeInfoBuilder helps avoid bungling use of a large TradeInfo constructor + * Builder helps avoid bungling use of a large TradeInfo constructor * argument list. If consecutive argument values of the same type are not * ordered correctly, the compiler won't complain but the resulting bugs could * be hard to find and fix. */ - public static class TradeInfoBuilder { + private static class Builder { private OfferInfo offer; private String tradeId; private String shortId; @@ -253,132 +253,132 @@ public static class TradeInfoBuilder { private String contractAsJson; private ContractInfo contract; - public TradeInfoBuilder withOffer(OfferInfo offer) { + public Builder withOffer(OfferInfo offer) { this.offer = offer; return this; } - public TradeInfoBuilder withTradeId(String tradeId) { + public Builder withTradeId(String tradeId) { this.tradeId = tradeId; return this; } - public TradeInfoBuilder withShortId(String shortId) { + public Builder withShortId(String shortId) { this.shortId = shortId; return this; } - public TradeInfoBuilder withDate(long date) { + public Builder withDate(long date) { this.date = date; return this; } - public TradeInfoBuilder withRole(String role) { + public Builder withRole(String role) { this.role = role; return this; } - public TradeInfoBuilder withIsCurrencyForTakerFeeBtc(boolean isCurrencyForTakerFeeBtc) { + public Builder withIsCurrencyForTakerFeeBtc(boolean isCurrencyForTakerFeeBtc) { this.isCurrencyForTakerFeeBtc = isCurrencyForTakerFeeBtc; return this; } - public TradeInfoBuilder withTxFeeAsLong(long txFeeAsLong) { + public Builder withTxFeeAsLong(long txFeeAsLong) { this.txFeeAsLong = txFeeAsLong; return this; } - public TradeInfoBuilder withTakerFeeAsLong(long takerFeeAsLong) { + public Builder withTakerFeeAsLong(long takerFeeAsLong) { this.takerFeeAsLong = takerFeeAsLong; return this; } - public TradeInfoBuilder withTakerFeeTxId(String takerFeeTxId) { + public Builder withTakerFeeTxId(String takerFeeTxId) { this.takerFeeTxId = takerFeeTxId; return this; } - public TradeInfoBuilder withDepositTxId(String depositTxId) { + public Builder withDepositTxId(String depositTxId) { this.depositTxId = depositTxId; return this; } - public TradeInfoBuilder withPayoutTxId(String payoutTxId) { + public Builder withPayoutTxId(String payoutTxId) { this.payoutTxId = payoutTxId; return this; } - public TradeInfoBuilder withTradeAmountAsLong(long tradeAmountAsLong) { + public Builder withTradeAmountAsLong(long tradeAmountAsLong) { this.tradeAmountAsLong = tradeAmountAsLong; return this; } - public TradeInfoBuilder withTradePrice(long tradePrice) { + public Builder withTradePrice(long tradePrice) { this.tradePrice = tradePrice; return this; } - public TradeInfoBuilder withTradeVolume(long tradeVolume) { + public Builder withTradeVolume(long tradeVolume) { this.tradeVolume = tradeVolume; return this; } - public TradeInfoBuilder withTradePeriodState(String tradePeriodState) { + public Builder withTradePeriodState(String tradePeriodState) { this.tradePeriodState = tradePeriodState; return this; } - public TradeInfoBuilder withState(String state) { + public Builder withState(String state) { this.state = state; return this; } - public TradeInfoBuilder withPhase(String phase) { + public Builder withPhase(String phase) { this.phase = phase; return this; } - public TradeInfoBuilder withTradingPeerNodeAddress(String tradingPeerNodeAddress) { + public Builder withTradingPeerNodeAddress(String tradingPeerNodeAddress) { this.tradingPeerNodeAddress = tradingPeerNodeAddress; return this; } - public TradeInfoBuilder withIsDepositPublished(boolean isDepositPublished) { + public Builder withIsDepositPublished(boolean isDepositPublished) { this.isDepositPublished = isDepositPublished; return this; } - public TradeInfoBuilder withIsDepositConfirmed(boolean isDepositConfirmed) { + public Builder withIsDepositConfirmed(boolean isDepositConfirmed) { this.isDepositConfirmed = isDepositConfirmed; return this; } - public TradeInfoBuilder withIsFiatSent(boolean isFiatSent) { + public Builder withIsFiatSent(boolean isFiatSent) { this.isFiatSent = isFiatSent; return this; } - public TradeInfoBuilder withIsFiatReceived(boolean isFiatReceived) { + public Builder withIsFiatReceived(boolean isFiatReceived) { this.isFiatReceived = isFiatReceived; return this; } - public TradeInfoBuilder withIsPayoutPublished(boolean isPayoutPublished) { + public Builder withIsPayoutPublished(boolean isPayoutPublished) { this.isPayoutPublished = isPayoutPublished; return this; } - public TradeInfoBuilder withIsWithdrawn(boolean isWithdrawn) { + public Builder withIsWithdrawn(boolean isWithdrawn) { this.isWithdrawn = isWithdrawn; return this; } - public TradeInfoBuilder withContractAsJson(String contractAsJson) { + public Builder withContractAsJson(String contractAsJson) { this.contractAsJson = contractAsJson; return this; } - public TradeInfoBuilder withContract(ContractInfo contract) { + public Builder withContract(ContractInfo contract) { this.contract = contract; return this; } diff --git a/core/src/main/java/bisq/core/api/model/TxInfo.java b/core/src/main/java/bisq/core/api/model/TxInfo.java index 3bd937c96e5..54023b2ec3a 100644 --- a/core/src/main/java/bisq/core/api/model/TxInfo.java +++ b/core/src/main/java/bisq/core/api/model/TxInfo.java @@ -46,7 +46,7 @@ public class TxInfo implements Payload { private final boolean isPending; private final String memo; - public TxInfo(TxInfoBuilder builder) { + public TxInfo(Builder builder) { this.txId = builder.txId; this.inputSum = builder.inputSum; this.outputSum = builder.outputSum; @@ -61,7 +61,7 @@ public static TxInfo toTxInfo(Transaction transaction) { throw new IllegalStateException("server created a null transaction"); if (transaction.getFee() != null) - return new TxInfoBuilder() + return new Builder() .withTxId(transaction.getTxId().toString()) .withInputSum(transaction.getInputSum().value) .withOutputSum(transaction.getOutputSum().value) @@ -71,7 +71,7 @@ public static TxInfo toTxInfo(Transaction transaction) { .withMemo(transaction.getMemo()) .build(); else - return new TxInfoBuilder() + return new Builder() .withTxId(transaction.getTxId().toString()) .withInputSum(transaction.getInputSum().value) .withOutputSum(transaction.getOutputSum().value) @@ -101,7 +101,7 @@ public bisq.proto.grpc.TxInfo toProtoMessage() { @SuppressWarnings("unused") public static TxInfo fromProto(bisq.proto.grpc.TxInfo proto) { - return new TxInfoBuilder() + return new Builder() .withTxId(proto.getTxId()) .withInputSum(proto.getInputSum()) .withOutputSum(proto.getOutputSum()) @@ -112,7 +112,7 @@ public static TxInfo fromProto(bisq.proto.grpc.TxInfo proto) { .build(); } - public static class TxInfoBuilder { + private static class Builder { private String txId; private long inputSum; private long outputSum; @@ -121,37 +121,37 @@ public static class TxInfoBuilder { private boolean isPending; private String memo; - public TxInfoBuilder withTxId(String txId) { + public Builder withTxId(String txId) { this.txId = txId; return this; } - public TxInfoBuilder withInputSum(long inputSum) { + public Builder withInputSum(long inputSum) { this.inputSum = inputSum; return this; } - public TxInfoBuilder withOutputSum(long outputSum) { + public Builder withOutputSum(long outputSum) { this.outputSum = outputSum; return this; } - public TxInfoBuilder withFee(long fee) { + public Builder withFee(long fee) { this.fee = fee; return this; } - public TxInfoBuilder withSize(int size) { + public Builder withSize(int size) { this.size = size; return this; } - public TxInfoBuilder withIsPending(boolean isPending) { + public Builder withIsPending(boolean isPending) { this.isPending = isPending; return this; } - public TxInfoBuilder withMemo(String memo) { + public Builder withMemo(String memo) { this.memo = memo; return this; } From 713b309f4a3f4141847e658b3a210702089052de Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 11 Nov 2021 13:06:23 -0300 Subject: [PATCH 04/39] Add String getRole(BsqSwapTrade trade) method --- .../bisq/core/trade/bisq_v1/TradeUtil.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/src/main/java/bisq/core/trade/bisq_v1/TradeUtil.java b/core/src/main/java/bisq/core/trade/bisq_v1/TradeUtil.java index 3f99e7dea9b..9d8212a060b 100644 --- a/core/src/main/java/bisq/core/trade/bisq_v1/TradeUtil.java +++ b/core/src/main/java/bisq/core/trade/bisq_v1/TradeUtil.java @@ -25,6 +25,7 @@ import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Contract; import bisq.core.trade.model.bisq_v1.Trade; +import bisq.core.trade.model.bsq_swap.BsqSwapTrade; import bisq.network.p2p.NodeAddress; @@ -57,6 +58,8 @@ @Singleton public class TradeUtil { + // TODO change non-state dependent instance methods to static methods. + private final BtcWalletService btcWalletService; private final KeyRing keyRing; @@ -203,6 +206,24 @@ public String getRole(Trade trade) { offer.getCurrencyCode()); } + /** + * Returns a string describing a trader's role for a given bsq swap. + * @param trade BsqSwapTrade + * @return String describing a trader's role for a given bsq swap + */ + public String getRole(BsqSwapTrade trade) { + Offer offer = trade.getOffer(); + if (offer == null) + throw new IllegalStateException( + format("could not get role because no offer was found for bsq swap '%s'", + trade.getShortId())); + + KeyRing keyRing = trade.getBsqSwapProtocolModel().getKeyRing(); + return getRole(offer.isBuyOffer(), + offer.isMyOffer(keyRing), + offer.getCurrencyCode()); + } + /** * Returns a string describing a trader's role. * From 521495c41d88ceb94cf9454342649a29f166e543 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 11 Nov 2021 13:07:49 -0300 Subject: [PATCH 05/39] Add conveniences isFiatOffer(offer), isAltcoinOffer(offer) --- core/src/main/java/bisq/core/offer/OfferUtil.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/java/bisq/core/offer/OfferUtil.java b/core/src/main/java/bisq/core/offer/OfferUtil.java index c676c57bbdc..d13d45742f7 100644 --- a/core/src/main/java/bisq/core/offer/OfferUtil.java +++ b/core/src/main/java/bisq/core/offer/OfferUtil.java @@ -488,6 +488,14 @@ private Optional getFeeInUserFiatCurrency(Coin makerFee, } } + public static boolean isFiatOffer(Offer offer) { + return offer.getBaseCurrencyCode().equals("BTC") && !offer.isBsqSwapOffer(); + } + + public static boolean isAltcoinOffer(Offer offer) { + return offer.getCounterCurrencyCode().equals("BTC") && !offer.isBsqSwapOffer(); + } + public static Optional getInvalidMakerFeeTxErrorMessage(Offer offer, BtcWalletService btcWalletService) { String offerFeePaymentTxId = offer.getOfferFeePaymentTxId(); if (offerFeePaymentTxId == null) { From 3dfbf3f6f0c2b08a86a8a9455f01ca90d7392779 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 11 Nov 2021 13:12:41 -0300 Subject: [PATCH 06/39] Partially adjust core api for bsq-swap support - Add core api methods to help CLI determine which type of offer to take for a given offerId. CLI's 'takeoffer` will need to determine which gRPC/proto request type to send to server. - Add implemetations for getBsqSwapTradeRole(), for tradeId or trade. --- core/src/main/java/bisq/core/api/CoreApi.java | 20 +++++++++++ .../java/bisq/core/api/CoreOffersService.java | 33 ++++++++++++++----- .../java/bisq/core/api/CoreTradesService.java | 16 +++++++-- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 510c4cfcbc7..409299c3330 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -119,6 +119,18 @@ public String getMethodHelp(String methodName) { // Offers /////////////////////////////////////////////////////////////////////////////////////////// + public boolean isAvailableFiatOffer(String id) { + return coreOffersService.isAvailableFiatOffer(id); + } + + public boolean isAvailableAltcoinOffer(String id) { + return coreOffersService.isAvailableAltcoinOffer(id); + } + + public boolean isAvailableBsqSwapOffer(String id) { + return coreOffersService.isAvailableBsqSwapOffer(id); + } + public Offer getBsqSwapOffer(String id) { return coreOffersService.getBsqSwapOffer(id); } @@ -317,6 +329,14 @@ public String getTradeRole(String tradeId) { return coreTradesService.getTradeRole(tradeId); } + public String getBsqSwapTradeRole(String tradeId) { + return coreTradesService.getBsqSwapTradeRole(tradeId); + } + + public String getBsqSwapTradeRole(BsqSwapTrade bsqSwapTrade) { + return coreTradesService.getBsqSwapTradeRole(bsqSwapTrade); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Wallets /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java index adbcee01dd4..74e8af6c304 100644 --- a/core/src/main/java/bisq/core/api/CoreOffersService.java +++ b/core/src/main/java/bisq/core/api/CoreOffersService.java @@ -60,6 +60,9 @@ import static bisq.core.locale.CurrencyUtil.isCryptoCurrency; import static bisq.core.offer.Offer.State; import static bisq.core.offer.OfferDirection.BUY; +import static bisq.core.offer.OfferUtil.getRandomOfferId; +import static bisq.core.offer.OfferUtil.isAltcoinOffer; +import static bisq.core.offer.OfferUtil.isFiatOffer; import static bisq.core.offer.OpenOffer.State.AVAILABLE; import static bisq.core.offer.OpenOffer.State.DEACTIVATED; import static bisq.core.payment.PaymentAccountUtil.isPaymentAccountValidForOffer; @@ -118,6 +121,19 @@ public CoreOffersService(CoreContext coreContext, this.user = user; } + boolean isAvailableFiatOffer(String id) { + return isFiatOffer(getOffer(id)); + } + + boolean isAvailableAltcoinOffer(String id) { + return isAltcoinOffer(getOffer(id)); + } + + boolean isAvailableBsqSwapOffer(String id) { + var offer = getOffer(id); + return offer.isBsqSwapOffer(); + } + Offer getBsqSwapOffer(String id) { return offerBookService.getOffers().stream() .filter(o -> o.getId().equals(id)) @@ -154,7 +170,6 @@ Offer getMyBsqSwapOffer(String id) { new IllegalStateException(format("offer with id '%s' not found", id))); } - List getBsqSwapOffers(String direction) { var offers = offerBookService.getOffers().stream() .filter(o -> !o.isMyOffer(keyRing)) @@ -208,9 +223,12 @@ OpenOffer getMyOpenOffer(String id) { } boolean isMyOffer(String id) { - return openOfferManager.getOpenOfferById(id) + boolean isMyOpenOffer = openOfferManager.getOpenOfferById(id) .filter(open -> open.getOffer().isMyOffer(keyRing)) .isPresent(); + boolean wasMyOffer = offerBookService.getOffers().stream() + .anyMatch(o -> o.getId().equals(id) && o.isMyOffer(keyRing)); + return isMyOpenOffer || wasMyOffer; } void createAndPlaceBsqSwapOffer(String directionAsString, @@ -222,7 +240,7 @@ void createAndPlaceBsqSwapOffer(String directionAsString, coreWalletsService.verifyEncryptedWalletIsUnlocked(); String currencyCode = "BSQ"; - String offerId = OfferUtil.getRandomOfferId(); + String offerId = getRandomOfferId(); OfferDirection direction = OfferDirection.valueOf(directionAsString.toUpperCase()); Coin amount = Coin.valueOf(amountAsLong); Coin minAmount = Coin.valueOf(minAmountAsLong); @@ -256,7 +274,7 @@ void createAndPlaceOffer(String currencyCode, throw new IllegalArgumentException(format("payment account with id %s not found", paymentAccountId)); String upperCaseCurrencyCode = currencyCode.toUpperCase(); - String offerId = OfferUtil.getRandomOfferId(); + String offerId = getRandomOfferId(); OfferDirection direction = OfferDirection.valueOf(directionAsString.toUpperCase()); Price price = Price.valueOf(upperCaseCurrencyCode, priceStringToLong(priceAsString, upperCaseCurrencyCode)); Coin amount = Coin.valueOf(amountAsLong); @@ -427,10 +445,9 @@ private void verifyPaymentAccountIsValidForNewOffer(Offer offer, PaymentAccount private boolean offerMatchesDirectionAndCurrency(Offer offer, String direction, String currencyCode) { - var offerOfWantedDirection = offer.getDirection().name().equalsIgnoreCase(direction); - var offerInWantedCurrency = offer.getCounterCurrencyCode() - .equalsIgnoreCase(currencyCode); - return offerOfWantedDirection && offerInWantedCurrency; + var isDirectionMatch = offer.getDirection().name().equalsIgnoreCase(direction); + var isCurrencyMatch = offer.getCounterCurrencyCode().equalsIgnoreCase(currencyCode); + return isDirectionMatch && isCurrencyMatch; } private Comparator openOfferPriceComparator(String direction) { diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index 5c08a61a48f..6fdb23434db 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -108,8 +108,10 @@ void takeBsqSwapOffer(Offer offer, log.info("Initiating take {} offer, {}", offer.isBuyOffer() ? "buy" : "sell", bsqSwapTakeOfferModel); - - bsqSwapTakeOfferModel.onTakeOffer(tradeResultHandler, log::warn, errorMessageHandler, coreContext.isApiUser()); + bsqSwapTakeOfferModel.onTakeOffer(tradeResultHandler, + log::warn, + errorMessageHandler, + coreContext.isApiUser()); } void takeOffer(Offer offer, @@ -239,6 +241,16 @@ BsqSwapTrade getBsqSwapTrade(String tradeId) { new IllegalArgumentException(format("trade with id '%s' not found", tradeId))); } + String getBsqSwapTradeRole(String tradeId) { + return getBsqSwapTradeRole(getBsqSwapTrade(tradeId)); + } + + String getBsqSwapTradeRole(BsqSwapTrade bsqSwapTrade) { + coreWalletsService.verifyWalletsAreAvailable(); + coreWalletsService.verifyEncryptedWalletIsUnlocked(); + return tradeUtil.getRole(bsqSwapTrade); + } + String getTradeRole(String tradeId) { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); From 16be35790b2af4dec60853639ad2c5002cee856a Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 11 Nov 2021 13:20:58 -0300 Subject: [PATCH 07/39] Paritally adjust bsq-swap-offer related gRPC CLI side classes - Adjust to removal of BsqSwapOfferInfo proto, use ammended OfferInfo instead. - Remove currency-code param from all get(My)BsqSwapOffer(s) requests. - Add new OfferCategory getAvailableOfferCategory(String offerId) service. CLI uses this to determine which kind of gRPC request object should be sent with a 'takeoffer' request. - Adjust GetOffersSmokeTest to help see offer/swap-offer CLI output. --- cli/src/main/java/bisq/cli/GrpcClient.java | 64 ++++++++--------- .../cli/request/OffersServiceRequest.java | 68 ++++++++++++------- .../cli/request/TradesServiceRequest.java | 13 ++++ .../java/bisq/cli/GetOffersSmokeTest.java | 37 +++++++++- 4 files changed, 119 insertions(+), 63 deletions(-) diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java index dcdaddc5508..fe27e1ea9c7 100644 --- a/cli/src/main/java/bisq/cli/GrpcClient.java +++ b/cli/src/main/java/bisq/cli/GrpcClient.java @@ -20,17 +20,15 @@ import bisq.proto.grpc.AddressBalanceInfo; import bisq.proto.grpc.BalancesInfo; import bisq.proto.grpc.BsqBalanceInfo; -import bisq.proto.grpc.BsqSwapOfferInfo; import bisq.proto.grpc.BsqSwapTradeInfo; import bisq.proto.grpc.BtcBalanceInfo; -import bisq.proto.grpc.CreateBsqSwapOfferRequest; import bisq.proto.grpc.GetMethodHelpRequest; +import bisq.proto.grpc.GetOfferCategoryReply; import bisq.proto.grpc.GetVersionRequest; import bisq.proto.grpc.OfferInfo; import bisq.proto.grpc.RegisterDisputeAgentRequest; import bisq.proto.grpc.StopRequest; import bisq.proto.grpc.TakeBsqSwapOfferReply; -import bisq.proto.grpc.TakeBsqSwapOfferRequest; import bisq.proto.grpc.TakeOfferReply; import bisq.proto.grpc.TradeInfo; import bisq.proto.grpc.TxFeeRateInfo; @@ -142,19 +140,20 @@ public TxInfo getTransaction(String txId) { return walletsServiceRequest.getTransaction(txId); } - public BsqSwapOfferInfo createBsqSwapOffer(String direction, - long amount, - long minAmount, - String fixedPrice, - String paymentAcctId) { - var request = CreateBsqSwapOfferRequest.newBuilder() - .setDirection(direction) - .setAmount(amount) - .setMinAmount(minAmount) - .setPrice(fixedPrice) - .setPaymentAccountId(paymentAcctId) - .build(); - return grpcStubs.offersService.createBsqSwapOffer(request).getBsqSwapOffer(); + public GetOfferCategoryReply.OfferCategory getAvailableOfferCategory(String offerId) { + return offersServiceRequest.getAvailableOfferCategory(offerId); + } + + public OfferInfo createBsqSwapOffer(String direction, + long amount, + long minAmount, + String fixedPrice, + String paymentAcctId) { + return offersServiceRequest.createBsqSwapOffer(direction, + amount, + minAmount, + fixedPrice, + paymentAcctId); } public OfferInfo createFixedPricedOffer(String direction, @@ -263,7 +262,7 @@ public void cancelOffer(String offerId) { offersServiceRequest.cancelOffer(offerId); } - public BsqSwapOfferInfo getBsqSwapOffer(String offerId) { + public OfferInfo getBsqSwapOffer(String offerId) { return offersServiceRequest.getBsqSwapOffer(offerId); } @@ -271,7 +270,7 @@ public OfferInfo getOffer(String offerId) { return offersServiceRequest.getOffer(offerId); } - public BsqSwapOfferInfo getMyBsqSwapOffer(String offerId) { + public OfferInfo getMyBsqSwapOffer(String offerId) { return offersServiceRequest.getMyBsqSwapOffer(offerId); } @@ -279,8 +278,8 @@ public OfferInfo getMyOffer(String offerId) { return offersServiceRequest.getMyOffer(offerId); } - public List getBsqSwapOffers(String direction, String currencyCode) { - return offersServiceRequest.getBsqSwapOffers(direction, currencyCode); + public List getBsqSwapOffers(String direction) { + return offersServiceRequest.getBsqSwapOffers(direction); } public List getOffers(String direction, String currencyCode) { @@ -303,12 +302,12 @@ public List getBsqOffersSortedByDate() { return offersServiceRequest.getBsqOffersSortedByDate(); } - public List getBsqSwapOffersSortedByDate() { + public List getBsqSwapOffersSortedByDate() { return offersServiceRequest.getBsqSwapOffersSortedByDate(); } - public List getMyBsqSwapOffers(String direction, String currencyCode) { - return offersServiceRequest.getMyBsqSwapOffers(direction, currencyCode); + public List getMyBsqSwapOffers(String direction) { + return offersServiceRequest.getMyBsqSwapOffers(direction); } public List getMyOffers(String direction, String currencyCode) { @@ -335,7 +334,7 @@ public List getMyBsqOffersSortedByDate() { return offersServiceRequest.getMyBsqOffersSortedByDate(); } - public List getMyBsqSwapBsqOffersSortedByDate() { + public List getMyBsqSwapBsqOffersSortedByDate() { return offersServiceRequest.getMyBsqSwapOffersSortedByDate(); } @@ -343,23 +342,20 @@ public OfferInfo getMostRecentOffer(String direction, String currencyCode) { return offersServiceRequest.getMostRecentOffer(direction, currencyCode); } - public List sortBsqSwapOffersByDate(List offerInfoList) { - return offersServiceRequest.sortBsqSwapOffersByDate(offerInfoList); + public List sortBsqSwapOffersByDate(List offers) { + return offersServiceRequest.sortOffersByDate(offers); } - public List sortOffersByDate(List offerInfoList) { - return offersServiceRequest.sortOffersByDate(offerInfoList); + public List sortOffersByDate(List offers) { + return offersServiceRequest.sortOffersByDate(offers); } public TakeBsqSwapOfferReply getTakeBsqSwapOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { - var request = TakeBsqSwapOfferRequest.newBuilder() - .setOfferId(offerId) - .setPaymentAccountId(paymentAccountId) - .setTakerFeeCurrencyCode(takerFeeCurrencyCode) - .build(); - return grpcStubs.tradesService.takeBsqSwapOffer(request); + return tradesServiceRequest.getTakeBsqSwapOfferReply(offerId, + paymentAccountId, + takerFeeCurrencyCode); } public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { diff --git a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java index 259ff8e142d..273d95073f4 100644 --- a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java +++ b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java @@ -17,12 +17,15 @@ package bisq.cli.request; -import bisq.proto.grpc.BsqSwapOfferInfo; import bisq.proto.grpc.CancelOfferRequest; +import bisq.proto.grpc.CreateBsqSwapOfferRequest; import bisq.proto.grpc.CreateOfferRequest; import bisq.proto.grpc.EditOfferRequest; +import bisq.proto.grpc.GetBsqSwapOffersRequest; import bisq.proto.grpc.GetMyOfferRequest; import bisq.proto.grpc.GetMyOffersRequest; +import bisq.proto.grpc.GetOfferCategoryReply; +import bisq.proto.grpc.GetOfferCategoryRequest; import bisq.proto.grpc.GetOfferRequest; import bisq.proto.grpc.GetOffersRequest; import bisq.proto.grpc.OfferInfo; @@ -60,6 +63,28 @@ public OffersServiceRequest(GrpcStubs grpcStubs) { this.grpcStubs = grpcStubs; } + public GetOfferCategoryReply.OfferCategory getAvailableOfferCategory(String offerId) { + var request = GetOfferCategoryRequest.newBuilder() + .setId(offerId) + .build(); + return grpcStubs.offersService.getOfferCategory(request).getOfferCategory(); + } + + public OfferInfo createBsqSwapOffer(String direction, + long amount, + long minAmount, + String fixedPrice, + String paymentAcctId) { + var request = CreateBsqSwapOfferRequest.newBuilder() + .setDirection(direction) + .setAmount(amount) + .setMinAmount(minAmount) + .setPrice(fixedPrice) + .setPaymentAccountId(paymentAcctId) + .build(); + return grpcStubs.offersService.createBsqSwapOffer(request).getBsqSwapOffer(); + } + @SuppressWarnings("unused") public OfferInfo createFixedPricedOffer(String direction, String currencyCode, @@ -210,7 +235,7 @@ public void cancelOffer(String offerId) { grpcStubs.offersService.cancelOffer(request); } - public BsqSwapOfferInfo getBsqSwapOffer(String offerId) { + public OfferInfo getBsqSwapOffer(String offerId) { var request = GetOfferRequest.newBuilder() .setId(offerId) .build(); @@ -224,7 +249,7 @@ public OfferInfo getOffer(String offerId) { return grpcStubs.offersService.getOffer(request).getOffer(); } - public BsqSwapOfferInfo getMyBsqSwapOffer(String offerId) { + public OfferInfo getMyBsqSwapOffer(String offerId) { var request = GetMyOfferRequest.newBuilder() .setId(offerId) .build(); @@ -239,10 +264,9 @@ public OfferInfo getMyOffer(String offerId) { return grpcStubs.offersService.getMyOffer(request).getOffer(); } - public List getBsqSwapOffers(String direction, String currencyCode) { - var request = GetOffersRequest.newBuilder() + public List getBsqSwapOffers(String direction) { + var request = GetBsqSwapOffersRequest.newBuilder() .setDirection(direction) - .setCurrencyCode(currencyCode) .build(); return grpcStubs.offersService.getBsqSwapOffers(request).getBsqSwapOffersList(); @@ -278,11 +302,11 @@ public List getOffersSortedByDate(String direction, String currencyCo return offers.isEmpty() ? offers : sortOffersByDate(offers); } - public List getBsqSwapOffersSortedByDate() { - ArrayList offers = new ArrayList<>(); - offers.addAll(getBsqSwapOffers(BUY.name(), "BSQ")); - offers.addAll(getBsqSwapOffers(SELL.name(), "BSQ")); - return sortBsqSwapOffersByDate(offers); + public List getBsqSwapOffersSortedByDate() { + ArrayList offers = new ArrayList<>(); + offers.addAll(getBsqSwapOffers(BUY.name())); + offers.addAll(getBsqSwapOffers(SELL.name())); + return sortOffersByDate(offers); } public List getBsqOffersSortedByDate() { @@ -292,10 +316,9 @@ public List getBsqOffersSortedByDate() { return sortOffersByDate(offers); } - public List getMyBsqSwapOffers(String direction, String currencyCode) { - var request = GetMyOffersRequest.newBuilder() + public List getMyBsqSwapOffers(String direction) { + var request = GetBsqSwapOffersRequest.newBuilder() .setDirection(direction) - .setCurrencyCode(currencyCode) .build(); return grpcStubs.offersService.getMyBsqSwapOffers(request).getBsqSwapOffersList(); } @@ -344,11 +367,11 @@ public List getMyBsqOffersSortedByDate() { return sortOffersByDate(offers); } - public List getMyBsqSwapOffersSortedByDate() { - ArrayList offers = new ArrayList<>(); - offers.addAll(getMyBsqSwapOffers(BUY.name(), "BSQ")); - offers.addAll(getMyBsqSwapOffers(SELL.name(), "BSQ")); - return sortBsqSwapOffersByDate(offers); + public List getMyBsqSwapOffersSortedByDate() { + ArrayList offers = new ArrayList<>(); + offers.addAll(getMyBsqSwapOffers(BUY.name())); + offers.addAll(getMyBsqSwapOffers(SELL.name())); + return sortOffersByDate(offers); } public OfferInfo getMostRecentOffer(String direction, String currencyCode) { @@ -356,13 +379,6 @@ public OfferInfo getMostRecentOffer(String direction, String currencyCode) { return offers.isEmpty() ? null : offers.get(offers.size() - 1); } - public List sortBsqSwapOffersByDate(List offerInfoList) { - return offerInfoList.stream() - .sorted(comparing(BsqSwapOfferInfo::getDate)) - .collect(toList()); - - } - public List sortOffersByDate(List offerInfoList) { return offerInfoList.stream() .sorted(comparing(OfferInfo::getDate)) diff --git a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java index 97d7d2a5efe..9c18685ca7f 100644 --- a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java +++ b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java @@ -22,6 +22,8 @@ import bisq.proto.grpc.ConfirmPaymentStartedRequest; import bisq.proto.grpc.GetTradeRequest; import bisq.proto.grpc.KeepFundsRequest; +import bisq.proto.grpc.TakeBsqSwapOfferReply; +import bisq.proto.grpc.TakeBsqSwapOfferRequest; import bisq.proto.grpc.TakeOfferReply; import bisq.proto.grpc.TakeOfferRequest; import bisq.proto.grpc.TradeInfo; @@ -39,6 +41,17 @@ public TradesServiceRequest(GrpcStubs grpcStubs) { this.grpcStubs = grpcStubs; } + public TakeBsqSwapOfferReply getTakeBsqSwapOfferReply(String offerId, + String paymentAccountId, + String takerFeeCurrencyCode) { + var request = TakeBsqSwapOfferRequest.newBuilder() + .setOfferId(offerId) + .setPaymentAccountId(paymentAccountId) + .setTakerFeeCurrencyCode(takerFeeCurrencyCode) + .build(); + return grpcStubs.tradesService.takeBsqSwapOffer(request); + } + public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { var request = TakeOfferRequest.newBuilder() .setOfferId(offerId) diff --git a/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java b/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java index f613aea358c..aa871c3e863 100644 --- a/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java +++ b/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java @@ -14,12 +14,44 @@ public class GetOffersSmokeTest { public static void main(String[] args) { + getMyBsqOffers(); + // getAvailableBsqOffers(); + // getMyUsdOffers(); + // getAvailableUsdOffers(); + } + + private static void getMyBsqOffers() { + out.println(">>> getmyoffers buy bsq"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=buy", "--currency-code=bsq"}); + out.println(">>> getmyoffers sell bsq"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=sell", "--currency-code=bsq"}); + out.println(">>> getmyoffer --offer-id=KRONTTMO-11cef1a9-c636-4dc7-b3f2-1616e4960c28-175"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffer", "--offer-id=KRONTTMO-11cef1a9-c636-4dc7-b3f2-1616e4960c28-175"}); + } + + private static void getAvailableBsqOffers() { + out.println(">>> getoffers buy bsq"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=buy", "--currency-code=bsq"}); + out.println(">>> getoffers sell bsq"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=sell", "--currency-code=bsq"}); + } + + private static void getMyUsdOffers() { + out.println(">>> getmyoffers buy usd"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=buy", "--currency-code=usd"}); + out.println(">>> getmyoffers sell usd"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffers", "--direction=sell", "--currency-code=usd"}); + } + + private static void getAvailableUsdOffers() { out.println(">>> getoffers buy usd"); - CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=buy", "--currency-code=usd"}); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=buy", "--currency-code=usd"}); out.println(">>> getoffers sell usd"); - CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=sell", "--currency-code=usd"}); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getoffers", "--direction=sell", "--currency-code=usd"}); + } + private static void TODO() { out.println(">>> getoffers buy eur"); CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=buy", "--currency-code=eur"}); out.println(">>> getoffers sell eur"); @@ -35,5 +67,4 @@ public static void main(String[] args) { out.println(">>> getoffers sell brl"); CliMain.main(new String[]{"--password=xyz", "getoffers", "--direction=sell", "--currency-code=brl"}); } - } From c5f50b53e485cfca81c98c4b663deb31f53939ee Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 11 Nov 2021 13:24:45 -0300 Subject: [PATCH 08/39] Paritally adjust bsq-swap-offer related apitest cases - Adjust to removal of BsqSwapOfferInfo proto, use ammended OfferInfo instead. - Remove currency-code param from all get(My)BsqSwapOffer(s) requests. --- .../method/offer/AbstractOfferTest.java | 6 --- .../method/offer/BsqSwapOfferTest.java | 15 +++--- .../method/trade/BsqSwapTradeTest.java | 51 ++++++++++--------- .../java/bisq/apitest/scenario/TradeTest.java | 2 + 4 files changed, 37 insertions(+), 37 deletions(-) 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 f5b8904815c..fb8c2d76330 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java @@ -17,7 +17,6 @@ package bisq.apitest.method.offer; -import bisq.proto.grpc.BsqSwapOfferInfo; import bisq.proto.grpc.OfferInfo; import protobuf.PaymentAccount; @@ -112,11 +111,6 @@ public static void setUp() { protected final Function, String> toOffersTable = (offers) -> new TableBuilder(OFFER_TBL, offers).build().toString(); - // TODO - protected final Function toBsqSwapOfferTable = (offer) -> - new TableBuilder(OFFER_TBL, offer).build().toString(); - - public static void initSwapPaymentAccounts() { // A bot may not know what the default 'BSQ Swap' account name is, // but API test cases do: the value of the i18n property 'BSQ_SWAP'. diff --git a/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java index bdb516c5157..2d65c55c35f 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java @@ -17,7 +17,7 @@ package bisq.apitest.method.offer; -import bisq.proto.grpc.BsqSwapOfferInfo; +import bisq.proto.grpc.OfferInfo; import lombok.extern.slf4j.Slf4j; @@ -121,7 +121,6 @@ private void createBsqSwapOffer() { assertEquals(5_000, bsqSwapOffer.getPrice()); assertEquals(1_000_000L, bsqSwapOffer.getAmount()); assertEquals(1_000_000L, bsqSwapOffer.getMinAmount()); - // assertEquals(alicesBsqAcct.getId(), atomicOffer.getMakerPaymentAccountId()); assertEquals(BSQ, bsqSwapOffer.getBaseCurrencyCode()); assertEquals(BTC, bsqSwapOffer.getCounterCurrencyCode()); @@ -129,13 +128,13 @@ private void createBsqSwapOffer() { testGetBsqSwapOffer(bsqSwapOffer); } - private void testGetMyBsqSwapOffer(BsqSwapOfferInfo bsqSwapOfferInfo) { + private void testGetMyBsqSwapOffer(OfferInfo bsqSwapOffer) { int numFetchAttempts = 0; while (true) { try { numFetchAttempts++; - var fetchedBsqSwapOffer = aliceClient.getMyBsqSwapOffer(bsqSwapOfferInfo.getId()); - assertEquals(bsqSwapOfferInfo.getId(), fetchedBsqSwapOffer.getId()); + var fetchedBsqSwapOffer = aliceClient.getMyOffer(bsqSwapOffer.getId()); + assertEquals(bsqSwapOffer.getId(), fetchedBsqSwapOffer.getId()); log.debug("Alice found her (my) new bsq swap offer on attempt # {}.", numFetchAttempts); break; } catch (Exception ex) { @@ -149,13 +148,13 @@ private void testGetMyBsqSwapOffer(BsqSwapOfferInfo bsqSwapOfferInfo) { } } - private void testGetBsqSwapOffer(BsqSwapOfferInfo bsqSwapOfferInfo) { + private void testGetBsqSwapOffer(OfferInfo bsqSwapOffer) { int numFetchAttempts = 0; while (true) { try { numFetchAttempts++; - var fetchedBsqSwapOffer = bobClient.getBsqSwapOffer(bsqSwapOfferInfo.getId()); - assertEquals(bsqSwapOfferInfo.getId(), fetchedBsqSwapOffer.getId()); + var fetchedBsqSwapOffer = bobClient.getOffer(bsqSwapOffer.getId()); + assertEquals(bsqSwapOffer.getId(), fetchedBsqSwapOffer.getId()); log.debug("Bob found new available bsq swap offer on attempt # {}.", numFetchAttempts); break; } catch (Exception ex) { 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 73261907db2..654ed4a594a 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java @@ -17,8 +17,8 @@ package bisq.apitest.method.trade; -import bisq.proto.grpc.BsqSwapOfferInfo; import bisq.proto.grpc.BsqSwapTradeInfo; +import bisq.proto.grpc.OfferInfo; import java.util.ArrayList; import java.util.List; @@ -81,54 +81,59 @@ public void testGetBalancesBeforeTrade() { @Test @Order(2) public void testAliceCreateBsqSwapBuyOffer() { - var bsqSwapOffer = aliceClient.createBsqSwapOffer(BUY.name(), - 1_000_000L, + var mySwapOffer = aliceClient.createBsqSwapOffer(BUY.name(), + 1_000_000L, // 0.01 BTC 1_000_000L, "0.00005", alicesBsqSwapAcct.getId()); - log.debug("BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", bsqSwapOffer); - var newOfferId = bsqSwapOffer.getId(); + log.debug("Pending BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); + var newOfferId = mySwapOffer.getId(); assertNotEquals("", newOfferId); - assertEquals(BUY.name(), bsqSwapOffer.getDirection()); - assertEquals(5_000, bsqSwapOffer.getPrice()); - assertEquals(1_000_000L, bsqSwapOffer.getAmount()); - assertEquals(1_000_000L, bsqSwapOffer.getMinAmount()); - // assertEquals(alicesBsqAcct.getId(), atomicOffer.getMakerPaymentAccountId()); - assertEquals(BSQ, bsqSwapOffer.getBaseCurrencyCode()); - assertEquals(BTC, bsqSwapOffer.getCounterCurrencyCode()); + assertEquals(BUY.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.getMyOffer(newOfferId); + log.debug("My fetched BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); + assertNotEquals(0, mySwapOffer.getMakerFee()); } @Test @Order(3) public void testBobTakesBsqSwapOffer() { - var bsqSwapOffer = getAvailableBsqSwapOffer(); - var bsqSwapTradeInfo = bobClient.takeBsqSwapOffer(bsqSwapOffer.getId(), + var availableSwapOffer = getAvailableBsqSwapOffer(); + var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId(), bobsBsqSwapAcct.getId(), BISQ_FEE_CURRENCY_CODE); - log.debug("Trade at t1: {}", bsqSwapTradeInfo); - assertEquals(PREPARATION.name(), bsqSwapTradeInfo.getState()); + log.debug("BsqSwap Trade at PREPARATION: {}", swapTrade); + assertEquals(PREPARATION.name(), swapTrade.getState()); genBtcBlocksThenWait(1, 3_000); - bsqSwapTradeInfo = getBsqSwapTrade(bsqSwapTradeInfo.getTradeId()); - log.debug("Trade at t2: {}", bsqSwapTradeInfo); - assertEquals(COMPLETED.name(), bsqSwapTradeInfo.getState()); + swapTrade = getBsqSwapTrade(swapTrade.getTradeId()); + log.debug("BsqSwap Trade at COMPLETION: {}", swapTrade); + assertEquals(COMPLETED.name(), swapTrade.getState()); } @Test @Order(4) public void testGetBalancesAfterTrade() { - sleep(2_500); // Give wallet time to finish processing TX. + genBtcBlocksThenWait(1, 5_000); 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 BsqSwapOfferInfo getAvailableBsqSwapOffer() { - List bsqSwapOffers = new ArrayList<>(); + private OfferInfo getAvailableBsqSwapOffer() { + List bsqSwapOffers = new ArrayList<>(); int numFetchAttempts = 0; while (bsqSwapOffers.size() == 0) { - bsqSwapOffers.addAll(bobClient.getBsqSwapOffers(BUY.name(), BSQ)); + bsqSwapOffers.addAll(bobClient.getBsqSwapOffers(BUY.name())); numFetchAttempts++; if (bsqSwapOffers.size() == 0) { log.warn("No available bsq swap offers found after {} fetch attempts.", numFetchAttempts); diff --git a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java index 63612712514..c4e03ffa2f2 100644 --- a/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java +++ b/apitest/src/test/java/bisq/apitest/scenario/TradeTest.java @@ -101,7 +101,9 @@ public void testTakeSellBSQOffer(final TestInfo testInfo) { @Order(6) public void testBsqSwapTradeTest(final TestInfo testInfo) { BsqSwapTradeTest test = new BsqSwapTradeTest(); + test.testGetBalancesBeforeTrade(); test.testAliceCreateBsqSwapBuyOffer(); test.testBobTakesBsqSwapOffer(); + test.testGetBalancesAfterTrade(); } } From 564303ac20f320fa22627bbb4b7d6d0a9b4ea0f3 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Fri, 12 Nov 2021 18:48:35 -0300 Subject: [PATCH 09/39] Normalize API gRPC bsq-swap related protos & wrappers This commit refactors the first cut of the BsqSwapTradeInfo and TradeInfo gRPC proto defs and wrappers. The change avoids duplication of fields between BsqSwapTradeInfo and TradeInfo, and adds a bsqSwapTradeInfo field to the old TradeInfo proto & wrapper. The immediate goal is moving towards getting the API's 'gettrade' method to work for both Bisq v1 trades and BSQ swap trades: the TradeInfo proto sent to the CLI should represent either a Bisq v1 trade or a BSQ swap trade. A mid-term term goal is to also make a new 'gettrades' method return a List to the CLI, where items in the List can be either v1 trades or bsq-swap trades. --- .../method/trade/AbstractTradeTest.java | 4 +- .../method/trade/BsqSwapTradeTest.java | 19 +- cli/src/main/java/bisq/cli/GrpcClient.java | 5 +- .../cli/request/TradesServiceRequest.java | 3 +- .../bisq/core/api/model/BsqSwapTradeInfo.java | 222 ++--------- .../bisq/core/api/model/ContractInfo.java | 1 + .../java/bisq/core/api/model/OfferInfo.java | 270 ++------------ .../java/bisq/core/api/model/TradeInfo.java | 350 ++++++------------ .../builder/BsqSwapTradeInfoBuilder.java | 107 ++++++ .../api/model/builder/OfferInfoBuilder.java | 223 +++++++++++ .../api/model/builder/TradeInfoV1Builder.java | 195 ++++++++++ .../bsq_swap/model/BsqSwapProtocolModel.java | 1 + .../bisq/daemon/grpc/GrpcTradesService.java | 11 +- proto/src/main/proto/grpc.proto | 51 +-- 14 files changed, 755 insertions(+), 707 deletions(-) create mode 100644 core/src/main/java/bisq/core/api/model/builder/BsqSwapTradeInfoBuilder.java create mode 100644 core/src/main/java/bisq/core/api/model/builder/OfferInfoBuilder.java create mode 100644 core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java 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 ba59c1a98f5..53b9f176f84 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java @@ -37,7 +37,9 @@ public class AbstractTradeTest extends AbstractOfferTest { protected static String tradeId; protected final Supplier maxTradeStateAndPhaseChecks = () -> isLongRunningTest ? 10 : 2; - private final Function toUserName = (client) -> client.equals(aliceClient) ? "Alice" : "Bob"; + protected final Function toTradeDetailTable = (trade) -> + new TableBuilder(TRADE_DETAIL_TBL, trade).build().toString(); + protected final Function toUserName = (client) -> client.equals(aliceClient) ? "Alice" : "Bob"; @BeforeAll public static void initStaticFixtures() { 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 654ed4a594a..efea480e7bd 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java @@ -17,8 +17,8 @@ package bisq.apitest.method.trade; -import bisq.proto.grpc.BsqSwapTradeInfo; import bisq.proto.grpc.OfferInfo; +import bisq.proto.grpc.TradeInfo; import java.util.ArrayList; import java.util.List; @@ -47,11 +47,12 @@ import bisq.apitest.method.offer.AbstractOfferTest; +import bisq.cli.GrpcClient; @Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) -public class BsqSwapTradeTest extends AbstractOfferTest { +public class BsqSwapTradeTest extends AbstractTradeTest { private static final String BISQ_FEE_CURRENCY_CODE = BSQ; @@ -106,16 +107,18 @@ public void testAliceCreateBsqSwapBuyOffer() { @Test @Order(3) public void testBobTakesBsqSwapOffer() { - var availableSwapOffer = getAvailableBsqSwapOffer(); + var availableSwapOffer = getAvailableBsqSwapOffer(bobClient); var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId(), bobsBsqSwapAcct.getId(), BISQ_FEE_CURRENCY_CODE); log.debug("BsqSwap Trade at PREPARATION: {}", swapTrade); + log.debug("BsqSwap Trade at PREPARATION:\n{}", toTradeDetailTable.apply(swapTrade)); assertEquals(PREPARATION.name(), swapTrade.getState()); genBtcBlocksThenWait(1, 3_000); - swapTrade = getBsqSwapTrade(swapTrade.getTradeId()); + swapTrade = getBsqSwapTrade(bobClient, swapTrade.getTradeId()); log.debug("BsqSwap Trade at COMPLETION: {}", swapTrade); + log.debug("BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(swapTrade)); assertEquals(COMPLETED.name(), swapTrade.getState()); } @@ -129,11 +132,11 @@ public void testGetBalancesAfterTrade() { log.debug("Bob's After Trade Balance:\n{}", formatBalancesTbls(bobsBalances)); } - private OfferInfo getAvailableBsqSwapOffer() { + private OfferInfo getAvailableBsqSwapOffer(GrpcClient client) { List bsqSwapOffers = new ArrayList<>(); int numFetchAttempts = 0; while (bsqSwapOffers.size() == 0) { - bsqSwapOffers.addAll(bobClient.getBsqSwapOffers(BUY.name())); + bsqSwapOffers.addAll(client.getBsqSwapOffers(BUY.name())); numFetchAttempts++; if (bsqSwapOffers.size() == 0) { log.warn("No available bsq swap offers found after {} fetch attempts.", numFetchAttempts); @@ -155,12 +158,12 @@ private OfferInfo getAvailableBsqSwapOffer() { return bsqSwapOffer; } - private BsqSwapTradeInfo getBsqSwapTrade(String tradeId) { + private TradeInfo getBsqSwapTrade(GrpcClient client, String tradeId) { int numFetchAttempts = 0; while (true) { try { numFetchAttempts++; - return bobClient.getBsqSwapTrade(tradeId); + return client.getBsqSwapTrade(tradeId); } catch (Exception ex) { log.warn(ex.getMessage()); if (numFetchAttempts > 9) { diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java index fe27e1ea9c7..87a8aa01229 100644 --- a/cli/src/main/java/bisq/cli/GrpcClient.java +++ b/cli/src/main/java/bisq/cli/GrpcClient.java @@ -20,7 +20,6 @@ import bisq.proto.grpc.AddressBalanceInfo; import bisq.proto.grpc.BalancesInfo; import bisq.proto.grpc.BsqBalanceInfo; -import bisq.proto.grpc.BsqSwapTradeInfo; import bisq.proto.grpc.BtcBalanceInfo; import bisq.proto.grpc.GetMethodHelpRequest; import bisq.proto.grpc.GetOfferCategoryReply; @@ -362,7 +361,7 @@ public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, return tradesServiceRequest.getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode); } - public BsqSwapTradeInfo takeBsqSwapOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { + public TradeInfo takeBsqSwapOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { var reply = getTakeBsqSwapOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode); if (reply.hasBsqSwapTrade()) return reply.getBsqSwapTrade(); @@ -374,7 +373,7 @@ public TradeInfo takeOffer(String offerId, String paymentAccountId, String taker return tradesServiceRequest.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode); } - public BsqSwapTradeInfo getBsqSwapTrade(String tradeId) { + public TradeInfo getBsqSwapTrade(String tradeId) { return tradesServiceRequest.getBsqSwapTrade(tradeId); } diff --git a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java index 9c18685ca7f..3395c0e220f 100644 --- a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java +++ b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java @@ -17,7 +17,6 @@ package bisq.cli.request; -import bisq.proto.grpc.BsqSwapTradeInfo; import bisq.proto.grpc.ConfirmPaymentReceivedRequest; import bisq.proto.grpc.ConfirmPaymentStartedRequest; import bisq.proto.grpc.GetTradeRequest; @@ -69,7 +68,7 @@ public TradeInfo takeOffer(String offerId, String paymentAccountId, String taker throw new IllegalStateException(reply.getFailureReason().getDescription()); } - public BsqSwapTradeInfo getBsqSwapTrade(String tradeId) { + public TradeInfo getBsqSwapTrade(String tradeId) { var request = GetTradeRequest.newBuilder() .setTradeId(tradeId) .build(); 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 ce1fa745ebb..1dbd3b10abf 100644 --- a/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java @@ -17,94 +17,62 @@ package bisq.core.api.model; +import bisq.core.api.model.builder.BsqSwapTradeInfoBuilder; import bisq.core.trade.model.bsq_swap.BsqSwapTrade; import bisq.common.Payload; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.ToString; - -import static bisq.core.api.model.OfferInfo.toMyOfferInfo; -import static bisq.core.api.model.OfferInfo.toOfferInfo; @EqualsAndHashCode -@ToString @Getter public class BsqSwapTradeInfo implements Payload { - private final OfferInfo bsqSwapOffer; - private final String tradeId; - private final String tempTradingPeerNodeAddress; - private final String peerNodeAddress; private final String txId; private final long bsqTradeAmount; private final long btcTradeAmount; - private final long tradePrice; private final long bsqMakerTradeFee; private final long bsqTakerTradeFee; private final long txFeePerVbyte; - private final long txFee; private final String makerBsqAddress; private final String makerBtcAddress; private final String takerBsqAddress; private final String takerBtcAddress; - private final long takeOfferDate; - private final String role; - private final String state; private final String errorMessage; - public BsqSwapTradeInfo(Builder builder) { - this.bsqSwapOffer = builder.bsqSwapOffer; - this.tradeId = builder.tradeId; - this.tempTradingPeerNodeAddress = builder.tempTradingPeerNodeAddress; - this.peerNodeAddress = builder.peerNodeAddress; - this.txId = builder.txId; - this.bsqTradeAmount = builder.bsqTradeAmount; - this.btcTradeAmount = builder.btcTradeAmount; - this.tradePrice = builder.tradePrice; - this.bsqMakerTradeFee = builder.bsqMakerTradeFee; - this.bsqTakerTradeFee = builder.bsqTakerTradeFee; - this.txFeePerVbyte = builder.txFeePerVbyte; - this.txFee = builder.txFee; - this.makerBsqAddress = builder.makerBsqAddress; - this.makerBtcAddress = builder.makerBtcAddress; - this.takerBsqAddress = builder.takerBsqAddress; - this.takerBtcAddress = builder.takerBtcAddress; - this.takeOfferDate = builder.takeOfferDate; - this.role = builder.role; - this.state = builder.state; - this.errorMessage = builder.errorMessage; + public BsqSwapTradeInfo(BsqSwapTradeInfoBuilder builder) { + this.txId = builder.getTxId(); + this.bsqTradeAmount = builder.getBsqTradeAmount(); + this.btcTradeAmount = builder.getBtcTradeAmount(); + this.bsqMakerTradeFee = builder.getBsqMakerTradeFee(); + this.bsqTakerTradeFee = builder.getBsqTakerTradeFee(); + this.txFeePerVbyte = builder.getTxFeePerVbyte(); + this.makerBsqAddress = builder.getMakerBsqAddress(); + this.makerBtcAddress = builder.getMakerBtcAddress(); + this.takerBsqAddress = builder.getTakerBsqAddress(); + this.takerBtcAddress = builder.getTakerBtcAddress(); + this.errorMessage = builder.getErrorMessage(); } - public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, String role, boolean wasMyOffer) { + public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, boolean wasMyOffer) { var protocolModel = trade.getBsqSwapProtocolModel(); var swapPeer = protocolModel.getTradePeer(); var makerBsqAddress = wasMyOffer ? protocolModel.getBsqAddress() : swapPeer.getBsqAddress(); var makerBtcAddress = wasMyOffer ? protocolModel.getBtcAddress() : swapPeer.getBtcAddress(); var takerBsqAddress = wasMyOffer ? swapPeer.getBsqAddress() : protocolModel.getBsqAddress(); var takerBtcAddress = wasMyOffer ? swapPeer.getBtcAddress() : protocolModel.getBtcAddress(); - var offerInfo = wasMyOffer ? toMyOfferInfo(trade.getOffer()) : toOfferInfo(trade.getOffer()); - return new Builder() - .withBsqSwapOffer(offerInfo) - .withTradeId(trade.getId()) - .withTempTradingPeerNodeAddress(trade.getBsqSwapProtocolModel().getTempTradingPeerNodeAddress().getFullAddress()) - .withPeerNodeAddress(trade.getTradingPeerNodeAddress().getFullAddress()) + return new BsqSwapTradeInfoBuilder() .withTxId(trade.getTxId()) .withBsqTradeAmount(trade.getBsqTradeAmount()) .withBtcTradeAmount(trade.getAmountAsLong()) - .withTradePrice(trade.getPrice().getValue()) .withBsqMakerTradeFee(trade.getMakerFeeAsLong()) .withBsqTakerTradeFee(trade.getTakerFeeAsLong()) .withTxFeePerVbyte(trade.getTxFeePerVbyte()) - .withTxFee(trade.getTxFee().value) .withMakerBsqAddress(makerBsqAddress) .withMakerBtcAddress(makerBtcAddress) .withTakerBsqAddress(takerBsqAddress) .withTakerBtcAddress(takerBtcAddress) - .withTakeOfferDate(trade.getTakeOfferDate()) - .withRole(role == null ? "" : role) - .withState(trade.getTradeState().name()) .withErrorMessage(trade.getErrorMessage()) .build(); } @@ -116,178 +84,50 @@ public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, String rol @Override public bisq.proto.grpc.BsqSwapTradeInfo toProtoMessage() { return bisq.proto.grpc.BsqSwapTradeInfo.newBuilder() - .setOffer(bsqSwapOffer.toProtoMessage()) - .setTradeId(tradeId) - .setTempTradingPeerNodeAddress(tempTradingPeerNodeAddress != null ? tempTradingPeerNodeAddress : "") - .setPeerNodeAddress(peerNodeAddress != null ? peerNodeAddress : "") .setTxId(txId != null ? txId : "") .setBsqTradeAmount(bsqTradeAmount) .setBtcTradeAmount(btcTradeAmount) - .setTradePrice(tradePrice) .setBsqMakerTradeFee(bsqMakerTradeFee) .setBsqTakerTradeFee(bsqTakerTradeFee) .setTxFeePerVbyte(txFeePerVbyte) - .setTxFee(txFee) .setMakerBsqAddress(makerBsqAddress != null ? makerBsqAddress : "") .setTakerBsqAddress(takerBsqAddress != null ? takerBsqAddress : "") .setMakerBtcAddress(makerBtcAddress != null ? makerBtcAddress : "") .setTakerBtcAddress(takerBtcAddress != null ? takerBtcAddress : "") - .setTakeOfferDate(takeOfferDate) - .setRole(role) - .setState(state) .setErrorMessage(errorMessage != null ? errorMessage : "") .build(); } public static BsqSwapTradeInfo fromProto(bisq.proto.grpc.BsqSwapTradeInfo proto) { - return new Builder() - .withBsqSwapOffer(OfferInfo.fromProto(proto.getOffer())) - .withTradeId(proto.getTradeId()) - .withTempTradingPeerNodeAddress(proto.getTempTradingPeerNodeAddress()) - .withPeerNodeAddress(proto.getPeerNodeAddress()) + return new BsqSwapTradeInfoBuilder() .withTxId(proto.getTxId()) .withBsqTradeAmount(proto.getBsqTradeAmount()) .withBtcTradeAmount(proto.getBtcTradeAmount()) - .withTradePrice(proto.getTradePrice()) .withBsqMakerTradeFee(proto.getBsqMakerTradeFee()) .withBsqTakerTradeFee(proto.getBsqTakerTradeFee()) .withTxFeePerVbyte(proto.getTxFeePerVbyte()) - .withTxFee(proto.getTxFee()) .withMakerBsqAddress(proto.getMakerBsqAddress()) .withMakerBtcAddress(proto.getMakerBtcAddress()) .withTakerBsqAddress(proto.getTakerBsqAddress()) .withTakerBtcAddress(proto.getTakerBtcAddress()) - .withTakeOfferDate(proto.getTakeOfferDate()) - .withRole(proto.getRole()) - .withState(proto.getState()) .withErrorMessage(proto.getErrorMessage()) .build(); } - private static class Builder { - private OfferInfo bsqSwapOffer; - private String tradeId; - private String tempTradingPeerNodeAddress; - private String peerNodeAddress; - private String txId; - private long bsqTradeAmount; - private long btcTradeAmount; - private long tradePrice; - private long bsqMakerTradeFee; - private long bsqTakerTradeFee; - private long txFeePerVbyte; - private long txFee; - private String makerBsqAddress; - private String makerBtcAddress; - private String takerBsqAddress; - private String takerBtcAddress; - private long takeOfferDate; - private String role; - private String state; - private String errorMessage; - - public Builder withBsqSwapOffer(OfferInfo bsqSwapOffer) { - this.bsqSwapOffer = bsqSwapOffer; - return this; - } - - public Builder withTradeId(String tradeId) { - this.tradeId = tradeId; - return this; - } - - public Builder withTempTradingPeerNodeAddress(String tempTradingPeerNodeAddress) { - this.tempTradingPeerNodeAddress = tempTradingPeerNodeAddress; - return this; - } - - public Builder withPeerNodeAddress(String peerNodeAddress) { - this.peerNodeAddress = peerNodeAddress; - return this; - } - - public Builder withTxId(String txId) { - this.txId = txId; - return this; - } - - public Builder withBsqTradeAmount(long bsqTradeAmount) { - this.bsqTradeAmount = bsqTradeAmount; - return this; - } - - public Builder withBtcTradeAmount(long btcTradeAmount) { - this.btcTradeAmount = btcTradeAmount; - return this; - } - - public Builder withTradePrice(long tradePrice) { - this.tradePrice = tradePrice; - return this; - } - - public Builder withBsqMakerTradeFee(long bsqMakerTradeFee) { - this.bsqMakerTradeFee = bsqMakerTradeFee; - return this; - } - - public Builder withBsqTakerTradeFee(long bsqTakerTradeFee) { - this.bsqTakerTradeFee = bsqTakerTradeFee; - return this; - } - - public Builder withTxFeePerVbyte(long txFeePerVbyte) { - this.txFeePerVbyte = txFeePerVbyte; - return this; - } - - public Builder withTxFee(long txFee) { - this.txFee = txFee; - return this; - } - - public Builder withMakerBsqAddress(String makerBsqAddress) { - this.makerBsqAddress = makerBsqAddress; - return this; - } - - public Builder withMakerBtcAddress(String makerBtcAddress) { - this.makerBtcAddress = makerBtcAddress; - return this; - } - - public Builder withTakerBsqAddress(String takerBsqAddress) { - this.takerBsqAddress = takerBsqAddress; - return this; - } - - public Builder withTakerBtcAddress(String takerBtcAddress) { - this.takerBtcAddress = takerBtcAddress; - return this; - } - - public Builder withTakeOfferDate(long takeOfferDate) { - this.takeOfferDate = takeOfferDate; - return this; - } - - public Builder withRole(String role) { - this.role = role; - return this; - } - - public Builder withState(String state) { - this.state = state; - return this; - } - - public Builder withErrorMessage(String errorMessage) { - this.errorMessage = errorMessage; - return this; - } - - public BsqSwapTradeInfo build() { - return new BsqSwapTradeInfo(this); - } + @Override + public String toString() { + return "BsqSwapTradeInfo{" + + ", txId='" + txId + '\'' + + ", bsqTradeAmount=" + bsqTradeAmount + + ", btcTradeAmount=" + btcTradeAmount + + ", bsqMakerTradeFee=" + bsqMakerTradeFee + + ", bsqTakerTradeFee=" + bsqTakerTradeFee + + ", txFeePerVbyte=" + txFeePerVbyte + + ", makerBsqAddress='" + makerBsqAddress + '\'' + + ", makerBtcAddress='" + makerBtcAddress + '\'' + + ", takerBsqAddress='" + takerBsqAddress + '\'' + + ", takerBtcAddress='" + takerBtcAddress + '\'' + + ", errorMessage='" + errorMessage + '\'' + + '}'; } } diff --git a/core/src/main/java/bisq/core/api/model/ContractInfo.java b/core/src/main/java/bisq/core/api/model/ContractInfo.java index 404335c9c7f..d4ce1e8ccb4 100644 --- a/core/src/main/java/bisq/core/api/model/ContractInfo.java +++ b/core/src/main/java/bisq/core/api/model/ContractInfo.java @@ -73,6 +73,7 @@ public ContractInfo(String buyerNodeAddress, // For transmitting TradeInfo messages when no contract is available. + // TODO Is this necessary as protobuf will send a DEFAULT_INSTANCE. public static Supplier emptyContract = () -> new ContractInfo("", "", diff --git a/core/src/main/java/bisq/core/api/model/OfferInfo.java b/core/src/main/java/bisq/core/api/model/OfferInfo.java index f84e69ad001..69d0455de08 100644 --- a/core/src/main/java/bisq/core/api/model/OfferInfo.java +++ b/core/src/main/java/bisq/core/api/model/OfferInfo.java @@ -17,6 +17,7 @@ package bisq.core.api.model; +import bisq.core.api.model.builder.OfferInfoBuilder; import bisq.core.offer.Offer; import bisq.core.offer.OpenOffer; import bisq.core.util.coin.CoinUtil; @@ -74,38 +75,38 @@ public class OfferInfo implements Payload { private final String versionNumber; private final int protocolVersion; - public OfferInfo(Builder builder) { - this.id = builder.id; - this.direction = builder.direction; - this.price = builder.price; - this.useMarketBasedPrice = builder.useMarketBasedPrice; - this.marketPriceMargin = builder.marketPriceMargin; - this.amount = builder.amount; - this.minAmount = builder.minAmount; - this.volume = builder.volume; - this.minVolume = builder.minVolume; - this.txFee = builder.txFee; - this.makerFee = builder.makerFee; - this.offerFeePaymentTxId = builder.offerFeePaymentTxId; - this.buyerSecurityDeposit = builder.buyerSecurityDeposit; - this.sellerSecurityDeposit = builder.sellerSecurityDeposit; - this.triggerPrice = builder.triggerPrice; - this.isCurrencyForMakerFeeBtc = builder.isCurrencyForMakerFeeBtc; - this.paymentAccountId = builder.paymentAccountId; - this.paymentMethodId = builder.paymentMethodId; - this.paymentMethodShortName = builder.paymentMethodShortName; - this.baseCurrencyCode = builder.baseCurrencyCode; - this.counterCurrencyCode = builder.counterCurrencyCode; - this.date = builder.date; - this.state = builder.state; - this.isActivated = builder.isActivated; - this.isMyOffer = builder.isMyOffer; - this.isMyPendingOffer = builder.isMyPendingOffer; - this.isBsqSwapOffer = builder.isBsqSwapOffer; - this.ownerNodeAddress = builder.ownerNodeAddress; - this.pubKeyRing = builder.pubKeyRing; - this.versionNumber = builder.versionNumber; - this.protocolVersion = builder.protocolVersion; + public OfferInfo(OfferInfoBuilder builder) { + this.id = builder.getId(); + this.direction = builder.getDirection(); + this.price = builder.getPrice(); + this.useMarketBasedPrice = builder.isUseMarketBasedPrice(); + this.marketPriceMargin = builder.getMarketPriceMargin(); + this.amount = builder.getAmount(); + this.minAmount = builder.getMinAmount(); + this.volume = builder.getVolume(); + this.minVolume = builder.getMinVolume(); + this.txFee = builder.getTxFee(); + this.makerFee = builder.getMakerFee(); + this.offerFeePaymentTxId = builder.getOfferFeePaymentTxId(); + this.buyerSecurityDeposit = builder.getBuyerSecurityDeposit(); + this.sellerSecurityDeposit = builder.getSellerSecurityDeposit(); + this.triggerPrice = builder.getTriggerPrice(); + this.isCurrencyForMakerFeeBtc = builder.isCurrencyForMakerFeeBtc(); + this.paymentAccountId = builder.getPaymentAccountId(); + this.paymentMethodId = builder.getPaymentMethodId(); + this.paymentMethodShortName = builder.getPaymentMethodShortName(); + this.baseCurrencyCode = builder.getBaseCurrencyCode(); + this.counterCurrencyCode = builder.getCounterCurrencyCode(); + this.date = builder.getDate(); + this.state = builder.getState(); + this.isActivated = builder.isActivated(); + this.isMyOffer = builder.isMyOffer(); + this.isMyPendingOffer = builder.isMyPendingOffer(); + this.isBsqSwapOffer = builder.isBsqSwapOffer(); + this.ownerNodeAddress = builder.getOwnerNodeAddress(); + this.pubKeyRing = builder.getPubKeyRing(); + this.versionNumber = builder.getVersionNumber(); + this.protocolVersion = builder.getProtocolVersion(); } public static OfferInfo toMyOfferInfo(Offer offer) { @@ -136,8 +137,8 @@ public static OfferInfo toMyOfferInfo(OpenOffer openOffer) { .build(); } - private static Builder getBuilder(Offer offer, boolean isMyOffer) { - return new Builder() + private static OfferInfoBuilder getBuilder(Offer offer, boolean isMyOffer) { + return new OfferInfoBuilder() .withId(offer.getId()) .withDirection(offer.getDirection().name()) .withPrice(Objects.requireNonNull(offer.getPrice()).getValue()) @@ -217,7 +218,7 @@ public bisq.proto.grpc.OfferInfo toProtoMessage() { @SuppressWarnings("unused") public static OfferInfo fromProto(bisq.proto.grpc.OfferInfo proto) { - return new Builder() + return new OfferInfoBuilder() .withId(proto.getId()) .withDirection(proto.getDirection()) .withPrice(proto.getPrice()) @@ -251,203 +252,4 @@ public static OfferInfo fromProto(bisq.proto.grpc.OfferInfo proto) { .withProtocolVersion(proto.getProtocolVersion()) .build(); } - - /* - * Builder helps avoid bungling use of a large OfferInfo constructor - * argument list. If consecutive argument values of the same type are not - * ordered correctly, the compiler won't complain but the resulting bugs could - * be hard to find and fix. - */ - private static class Builder { - private String id; - private String direction; - private long price; - private boolean useMarketBasedPrice; - private double marketPriceMargin; - private long amount; - private long minAmount; - private long volume; - private long minVolume; - private long txFee; - private long makerFee; - private String offerFeePaymentTxId; - private long buyerSecurityDeposit; - private long sellerSecurityDeposit; - private long triggerPrice; - private boolean isCurrencyForMakerFeeBtc; - private String paymentAccountId; - private String paymentMethodId; - private String paymentMethodShortName; - private String baseCurrencyCode; - private String counterCurrencyCode; - private long date; - private String state; - private boolean isActivated; - private boolean isMyOffer; - private boolean isMyPendingOffer; - private boolean isBsqSwapOffer; - private String ownerNodeAddress; - private String pubKeyRing; - private String versionNumber; - private int protocolVersion; - - public Builder withId(String id) { - this.id = id; - return this; - } - - public Builder withDirection(String direction) { - this.direction = direction; - return this; - } - - public Builder withPrice(long price) { - this.price = price; - return this; - } - - public Builder withUseMarketBasedPrice(boolean useMarketBasedPrice) { - this.useMarketBasedPrice = useMarketBasedPrice; - return this; - } - - public Builder withMarketPriceMargin(double useMarketBasedPrice) { - this.marketPriceMargin = useMarketBasedPrice; - return this; - } - - public Builder withAmount(long amount) { - this.amount = amount; - return this; - } - - public Builder withMinAmount(long minAmount) { - this.minAmount = minAmount; - return this; - } - - public Builder withVolume(long volume) { - this.volume = volume; - return this; - } - - public Builder withMinVolume(long minVolume) { - this.minVolume = minVolume; - return this; - } - - public Builder withTxFee(long txFee) { - this.txFee = txFee; - return this; - } - - public Builder withMakerFee(long makerFee) { - this.makerFee = makerFee; - return this; - } - - public Builder withOfferFeePaymentTxId(String offerFeePaymentTxId) { - this.offerFeePaymentTxId = offerFeePaymentTxId; - return this; - } - - public Builder withBuyerSecurityDeposit(long buyerSecurityDeposit) { - this.buyerSecurityDeposit = buyerSecurityDeposit; - return this; - } - - public Builder withSellerSecurityDeposit(long sellerSecurityDeposit) { - this.sellerSecurityDeposit = sellerSecurityDeposit; - return this; - } - - public Builder withTriggerPrice(long triggerPrice) { - this.triggerPrice = triggerPrice; - return this; - } - - public Builder withIsCurrencyForMakerFeeBtc(boolean isCurrencyForMakerFeeBtc) { - this.isCurrencyForMakerFeeBtc = isCurrencyForMakerFeeBtc; - return this; - } - - public Builder withPaymentAccountId(String paymentAccountId) { - this.paymentAccountId = paymentAccountId; - return this; - } - - public Builder withPaymentMethodId(String paymentMethodId) { - this.paymentMethodId = paymentMethodId; - return this; - } - - public Builder withPaymentMethodShortName(String paymentMethodShortName) { - this.paymentMethodShortName = paymentMethodShortName; - return this; - } - - public Builder withBaseCurrencyCode(String baseCurrencyCode) { - this.baseCurrencyCode = baseCurrencyCode; - return this; - } - - public Builder withCounterCurrencyCode(String counterCurrencyCode) { - this.counterCurrencyCode = counterCurrencyCode; - return this; - } - - public Builder withDate(long date) { - this.date = date; - return this; - } - - public Builder withState(String state) { - this.state = state; - return this; - } - - public Builder withIsActivated(boolean isActivated) { - this.isActivated = isActivated; - return this; - } - - public Builder withIsMyOffer(boolean isMyOffer) { - this.isMyOffer = isMyOffer; - return this; - } - - public Builder withIsMyPendingOffer(boolean isMyPendingOffer) { - this.isMyPendingOffer = isMyPendingOffer; - return this; - } - - public Builder withIsBsqSwapOffer(boolean isBsqSwapOffer) { - this.isBsqSwapOffer = isBsqSwapOffer; - return this; - } - - public Builder withOwnerNodeAddress(String ownerNodeAddress) { - this.ownerNodeAddress = ownerNodeAddress; - return this; - } - - public Builder withPubKeyRing(String pubKeyRing) { - this.pubKeyRing = pubKeyRing; - return this; - } - - public Builder withVersionNumber(String versionNumber) { - this.versionNumber = versionNumber; - return this; - } - - public Builder withProtocolVersion(int protocolVersion) { - this.protocolVersion = protocolVersion; - return this; - } - - public OfferInfo build() { - return new OfferInfo(this); - } - } } 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 3e2bc8b56ea..70b929839a4 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -17,19 +17,22 @@ package bisq.core.api.model; +import bisq.core.api.model.builder.TradeInfoV1Builder; +import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Contract; import bisq.core.trade.model.bisq_v1.Trade; +import bisq.core.trade.model.bsq_swap.BsqSwapTrade; import bisq.common.Payload; -import java.util.Objects; - import lombok.EqualsAndHashCode; import lombok.Getter; +import static bisq.core.api.model.BsqSwapTradeInfo.toBsqSwapTradeInfo; 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 java.util.Objects.requireNonNull; @EqualsAndHashCode @Getter @@ -39,6 +42,7 @@ public class TradeInfo implements Payload { // lighter weight TradeInfo proto wrapper instead, containing just enough fields to // view and interact with trades. + // Bisq v1 trade protocol fields (some are in common with the BSQ Swap protocol). private final OfferInfo offer; private final String tradeId; private final String shortId; @@ -65,41 +69,85 @@ public class TradeInfo implements Payload { private final boolean isWithdrawn; private final String contractAsJson; private final ContractInfo contract; + // Optional BSQ swap trade protocol details (post v1). + private BsqSwapTradeInfo bsqSwapTradeInfo; + + public TradeInfo(TradeInfoV1Builder builder) { + this.offer = builder.getOffer(); + this.tradeId = builder.getTradeId(); + this.shortId = builder.getShortId(); + this.date = builder.getDate(); + this.role = builder.getRole(); + this.isCurrencyForTakerFeeBtc = builder.isCurrencyForTakerFeeBtc(); + this.txFeeAsLong = builder.getTxFeeAsLong(); + this.takerFeeAsLong = builder.getTakerFeeAsLong(); + this.takerFeeTxId = builder.getTakerFeeTxId(); + this.depositTxId = builder.getDepositTxId(); + this.payoutTxId = builder.getPayoutTxId(); + this.tradeAmountAsLong = builder.getTradeAmountAsLong(); + this.tradePrice = builder.getTradePrice(); + this.tradeVolume = builder.getTradeVolume(); + this.tradingPeerNodeAddress = builder.getTradingPeerNodeAddress(); + this.state = builder.getState(); + this.phase = builder.getPhase(); + this.tradePeriodState = builder.getTradePeriodState(); + this.isDepositPublished = builder.isDepositPublished(); + this.isDepositConfirmed = builder.isDepositConfirmed(); + this.isFiatSent = builder.isFiatSent(); + this.isFiatReceived = builder.isFiatReceived(); + this.isPayoutPublished = builder.isPayoutPublished(); + this.isWithdrawn = builder.isWithdrawn(); + this.contractAsJson = builder.getContractAsJson(); + this.contract = builder.getContract(); + this.bsqSwapTradeInfo = null; + } - public TradeInfo(Builder builder) { - this.offer = builder.offer; - this.tradeId = builder.tradeId; - this.shortId = builder.shortId; - this.date = builder.date; - this.role = builder.role; - this.isCurrencyForTakerFeeBtc = builder.isCurrencyForTakerFeeBtc; - this.txFeeAsLong = builder.txFeeAsLong; - this.takerFeeAsLong = builder.takerFeeAsLong; - this.takerFeeTxId = builder.takerFeeTxId; - this.depositTxId = builder.depositTxId; - this.payoutTxId = builder.payoutTxId; - this.tradeAmountAsLong = builder.tradeAmountAsLong; - this.tradePrice = builder.tradePrice; - this.tradeVolume = builder.tradeVolume; - this.tradingPeerNodeAddress = builder.tradingPeerNodeAddress; - this.state = builder.state; - this.phase = builder.phase; - this.tradePeriodState = builder.tradePeriodState; - this.isDepositPublished = builder.isDepositPublished; - this.isDepositConfirmed = builder.isDepositConfirmed; - this.isFiatSent = builder.isFiatSent; - this.isFiatReceived = builder.isFiatReceived; - this.isPayoutPublished = builder.isPayoutPublished; - this.isWithdrawn = builder.isWithdrawn; - this.contractAsJson = builder.contractAsJson; - this.contract = builder.contract; + public static TradeInfo toNewTradeInfo(BsqSwapTrade trade, String role) { + // Always called by the taker, isMyOffer=false. + return toTradeInfo(trade, role, false); } public static TradeInfo toNewTradeInfo(Trade trade) { + // Always called by the taker, isMyOffer=false. return toTradeInfo(trade, null, false); } - public static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer) { + public static TradeInfo toTradeInfo(TradeModel tradeModel, String role, boolean isMyOffer) { + if (tradeModel instanceof Trade) + return toTradeInfo((Trade) tradeModel, role, isMyOffer); + else if (tradeModel instanceof BsqSwapTrade) + return toTradeInfo((BsqSwapTrade) tradeModel, role, isMyOffer); + else + throw new IllegalStateException("unsupported trade type: " + tradeModel.getClass().getSimpleName()); + } + + public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, String role, boolean isMyOffer) { + OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(bsqSwapTrade.getOffer()) : toOfferInfo(bsqSwapTrade.getOffer()); + TradeInfo tradeInfo = new TradeInfoV1Builder() + .withOffer(offerInfo) + .withTradeId(bsqSwapTrade.getId()) + .withShortId(bsqSwapTrade.getShortId()) + .withDate(bsqSwapTrade.getDate().getTime()) + .withRole(role == null ? "" : role) + .withIsCurrencyForTakerFeeBtc(false) // BSQ Swap fees always paid in BSQ. + .withTxFeeAsLong(bsqSwapTrade.getTxFee().value) + .withTakerFeeAsLong(bsqSwapTrade.getTakerFeeAsLong()) + // N/A: .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) + .build(); + tradeInfo.bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, isMyOffer); + return tradeInfo; + } + + private static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer) { ContractInfo contractInfo; if (trade.getContract() != null) { Contract contract = trade.getContract(); @@ -120,7 +168,7 @@ public static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer) } OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(trade.getOffer()) : toOfferInfo(trade.getOffer()); - return new Builder() + return new TradeInfoV1Builder() .withOffer(offerInfo) .withTradeId(trade.getId()) .withShortId(trade.getShortId()) @@ -129,15 +177,13 @@ public static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer) .withIsCurrencyForTakerFeeBtc(trade.isCurrencyForTakerFeeBtc()) .withTxFeeAsLong(trade.getTradeTxFeeAsLong()) .withTakerFeeAsLong(trade.getTakerFeeAsLong()) - .withTakerFeeAsLong(trade.getTakerFeeAsLong()) .withTakerFeeTxId(trade.getTakerFeeTxId()) .withDepositTxId(trade.getDepositTxId()) .withPayoutTxId(trade.getPayoutTxId()) .withTradeAmountAsLong(trade.getAmountAsLong()) .withTradePrice(trade.getPrice().getValue()) .withTradeVolume(trade.getVolume() == null ? 0 : trade.getVolume().getValue()) - .withTradingPeerNodeAddress(Objects.requireNonNull( - trade.getTradingPeerNodeAddress()).getHostNameWithoutPostFix()) + .withTradingPeerNodeAddress(requireNonNull(trade.getTradingPeerNodeAddress().getFullAddress())) .withState(trade.getTradeState().name()) .withPhase(trade.getTradePhase().name()) .withTradePeriodState(trade.getTradePeriodState().name()) @@ -158,38 +204,45 @@ public static TradeInfo toTradeInfo(Trade trade, String role, boolean isMyOffer) @Override public bisq.proto.grpc.TradeInfo toProtoMessage() { - return bisq.proto.grpc.TradeInfo.newBuilder() - .setOffer(offer.toProtoMessage()) - .setTradeId(tradeId) - .setShortId(shortId) - .setDate(date) - .setRole(role) - .setIsCurrencyForTakerFeeBtc(isCurrencyForTakerFeeBtc) - .setTxFeeAsLong(txFeeAsLong) - .setTakerFeeAsLong(takerFeeAsLong) - .setTakerFeeTxId(takerFeeTxId == null ? "" : takerFeeTxId) - .setDepositTxId(depositTxId == null ? "" : depositTxId) - .setPayoutTxId(payoutTxId == null ? "" : payoutTxId) - .setTradeAmountAsLong(tradeAmountAsLong) - .setTradePrice(tradePrice) - .setTradeVolume(tradeVolume) - .setTradingPeerNodeAddress(tradingPeerNodeAddress) - .setState(state) - .setPhase(phase) - .setTradePeriodState(tradePeriodState) - .setIsDepositPublished(isDepositPublished) - .setIsDepositConfirmed(isDepositConfirmed) - .setIsFiatSent(isFiatSent) - .setIsFiatReceived(isFiatReceived) - .setIsPayoutPublished(isPayoutPublished) - .setIsWithdrawn(isWithdrawn) - .setContractAsJson(contractAsJson == null ? "" : contractAsJson) - .setContract(contract.toProtoMessage()) - .build(); + var protoBuilder = + bisq.proto.grpc.TradeInfo.newBuilder() + .setOffer(offer.toProtoMessage()) + .setTradeId(tradeId) + .setShortId(shortId) + .setDate(date) + .setRole(role) + .setIsCurrencyForTakerFeeBtc(isCurrencyForTakerFeeBtc) + .setTxFeeAsLong(txFeeAsLong) + .setTakerFeeAsLong(takerFeeAsLong) + .setTakerFeeTxId(takerFeeTxId == null ? "" : takerFeeTxId) + .setDepositTxId(depositTxId == null ? "" : depositTxId) + .setPayoutTxId(payoutTxId == null ? "" : payoutTxId) + .setTradeAmountAsLong(tradeAmountAsLong) + .setTradePrice(tradePrice) + .setTradeVolume(tradeVolume) + .setTradingPeerNodeAddress(tradingPeerNodeAddress) + .setState(state == null ? "" : state) + .setPhase(phase == null ? "" : phase) + .setTradePeriodState(tradePeriodState == null ? "" : tradePeriodState) + .setIsDepositPublished(isDepositPublished) + .setIsDepositConfirmed(isDepositConfirmed) + .setIsFiatSent(isFiatSent) + .setIsFiatReceived(isFiatReceived) + .setIsPayoutPublished(isPayoutPublished) + .setIsWithdrawn(isWithdrawn); + + if (offer.isBsqSwapOffer()) { + protoBuilder.setBsqSwapTradeInfo(bsqSwapTradeInfo.toProtoMessage()); + } else { + protoBuilder.setContractAsJson(contractAsJson == null ? "" : contractAsJson); + protoBuilder.setContract(contract.toProtoMessage()); + } + + return protoBuilder.build(); } public static TradeInfo fromProto(bisq.proto.grpc.TradeInfo proto) { - return new Builder() + var tradeInfo = new TradeInfoV1Builder() .withOffer(OfferInfo.fromProto(proto.getOffer())) .withTradeId(proto.getTradeId()) .withShortId(proto.getShortId()) @@ -217,175 +270,11 @@ public static TradeInfo fromProto(bisq.proto.grpc.TradeInfo proto) { .withContractAsJson(proto.getContractAsJson()) .withContract((ContractInfo.fromProto(proto.getContract()))) .build(); - } - - /* - * Builder helps avoid bungling use of a large TradeInfo constructor - * argument list. If consecutive argument values of the same type are not - * ordered correctly, the compiler won't complain but the resulting bugs could - * be hard to find and fix. - */ - private static class Builder { - private OfferInfo offer; - private String tradeId; - private String shortId; - private long date; - private String role; - private boolean isCurrencyForTakerFeeBtc; - private long txFeeAsLong; - private long takerFeeAsLong; - private String takerFeeTxId; - private String depositTxId; - private String payoutTxId; - private long tradeAmountAsLong; - private long tradePrice; - private long tradeVolume; - private String tradingPeerNodeAddress; - private String state; - private String phase; - private String tradePeriodState; - private boolean isDepositPublished; - private boolean isDepositConfirmed; - private boolean isFiatSent; - private boolean isFiatReceived; - private boolean isPayoutPublished; - private boolean isWithdrawn; - private String contractAsJson; - private ContractInfo contract; - - public Builder withOffer(OfferInfo offer) { - this.offer = offer; - return this; - } - - public Builder withTradeId(String tradeId) { - this.tradeId = tradeId; - return this; - } - - public Builder withShortId(String shortId) { - this.shortId = shortId; - return this; - } - - public Builder withDate(long date) { - this.date = date; - return this; - } - - public Builder withRole(String role) { - this.role = role; - return this; - } - - public Builder withIsCurrencyForTakerFeeBtc(boolean isCurrencyForTakerFeeBtc) { - this.isCurrencyForTakerFeeBtc = isCurrencyForTakerFeeBtc; - return this; - } - - public Builder withTxFeeAsLong(long txFeeAsLong) { - this.txFeeAsLong = txFeeAsLong; - return this; - } - - public Builder withTakerFeeAsLong(long takerFeeAsLong) { - this.takerFeeAsLong = takerFeeAsLong; - return this; - } - - public Builder withTakerFeeTxId(String takerFeeTxId) { - this.takerFeeTxId = takerFeeTxId; - return this; - } - public Builder withDepositTxId(String depositTxId) { - this.depositTxId = depositTxId; - return this; - } - - public Builder withPayoutTxId(String payoutTxId) { - this.payoutTxId = payoutTxId; - return this; - } - - public Builder withTradeAmountAsLong(long tradeAmountAsLong) { - this.tradeAmountAsLong = tradeAmountAsLong; - return this; - } - - public Builder withTradePrice(long tradePrice) { - this.tradePrice = tradePrice; - return this; - } - - public Builder withTradeVolume(long tradeVolume) { - this.tradeVolume = tradeVolume; - return this; - } - - public Builder withTradePeriodState(String tradePeriodState) { - this.tradePeriodState = tradePeriodState; - return this; - } - - public Builder withState(String state) { - this.state = state; - return this; - } - - public Builder withPhase(String phase) { - this.phase = phase; - return this; - } - - public Builder withTradingPeerNodeAddress(String tradingPeerNodeAddress) { - this.tradingPeerNodeAddress = tradingPeerNodeAddress; - return this; - } + if (proto.getOffer().getIsBsqSwapOffer()) + tradeInfo.bsqSwapTradeInfo = BsqSwapTradeInfo.fromProto(proto.getBsqSwapTradeInfo()); - public Builder withIsDepositPublished(boolean isDepositPublished) { - this.isDepositPublished = isDepositPublished; - return this; - } - - public Builder withIsDepositConfirmed(boolean isDepositConfirmed) { - this.isDepositConfirmed = isDepositConfirmed; - return this; - } - - public Builder withIsFiatSent(boolean isFiatSent) { - this.isFiatSent = isFiatSent; - return this; - } - - public Builder withIsFiatReceived(boolean isFiatReceived) { - this.isFiatReceived = isFiatReceived; - return this; - } - - public Builder withIsPayoutPublished(boolean isPayoutPublished) { - this.isPayoutPublished = isPayoutPublished; - return this; - } - - public Builder withIsWithdrawn(boolean isWithdrawn) { - this.isWithdrawn = isWithdrawn; - return this; - } - - public Builder withContractAsJson(String contractAsJson) { - this.contractAsJson = contractAsJson; - return this; - } - - public Builder withContract(ContractInfo contract) { - this.contract = contract; - return this; - } - - public TradeInfo build() { - return new TradeInfo(this); - } + return tradeInfo; } @Override @@ -417,6 +306,7 @@ public String toString() { ", offer=" + offer + "\n" + ", contractAsJson=" + contractAsJson + "\n" + ", contract=" + contract + "\n" + + ", bsqSwapTradeInfo=" + bsqSwapTradeInfo + "\n" + '}'; } } 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 new file mode 100644 index 00000000000..dcbaaa4d401 --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/builder/BsqSwapTradeInfoBuilder.java @@ -0,0 +1,107 @@ +/* + * 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.builder; + +import bisq.core.api.model.BsqSwapTradeInfo; + +import lombok.Getter; + +/** + * Proto wrapper for BSQ swap protocol details not common to Bisq v1 + * trade protocol details. + * + * This builder helps avoid bungling use of a large BsqSwapTradeInfo constructor + * argument list. If consecutive argument values of the same type are not + * ordered correctly, the compiler won't complain but the resulting bugs could + * be hard to find and fix. + */ +@Getter +public final class BsqSwapTradeInfoBuilder { + + private String txId; + private long bsqTradeAmount; + private long btcTradeAmount; + private long bsqMakerTradeFee; + private long bsqTakerTradeFee; + private long txFeePerVbyte; + private String makerBsqAddress; + private String makerBtcAddress; + private String takerBsqAddress; + private String takerBtcAddress; + private String errorMessage; + + public BsqSwapTradeInfoBuilder withTxId(String txId) { + this.txId = txId; + return this; + } + + public BsqSwapTradeInfoBuilder withBsqTradeAmount(long bsqTradeAmount) { + this.bsqTradeAmount = bsqTradeAmount; + return this; + } + + public BsqSwapTradeInfoBuilder withBtcTradeAmount(long btcTradeAmount) { + this.btcTradeAmount = btcTradeAmount; + return this; + } + + public BsqSwapTradeInfoBuilder withBsqMakerTradeFee(long bsqMakerTradeFee) { + this.bsqMakerTradeFee = bsqMakerTradeFee; + return this; + } + + public BsqSwapTradeInfoBuilder withBsqTakerTradeFee(long bsqTakerTradeFee) { + this.bsqTakerTradeFee = bsqTakerTradeFee; + return this; + } + + public BsqSwapTradeInfoBuilder withTxFeePerVbyte(long txFeePerVbyte) { + this.txFeePerVbyte = txFeePerVbyte; + return this; + } + + public BsqSwapTradeInfoBuilder withMakerBsqAddress(String makerBsqAddress) { + this.makerBsqAddress = makerBsqAddress; + return this; + } + + public BsqSwapTradeInfoBuilder withMakerBtcAddress(String makerBtcAddress) { + this.makerBtcAddress = makerBtcAddress; + return this; + } + + public BsqSwapTradeInfoBuilder withTakerBsqAddress(String takerBsqAddress) { + this.takerBsqAddress = takerBsqAddress; + return this; + } + + public BsqSwapTradeInfoBuilder withTakerBtcAddress(String takerBtcAddress) { + this.takerBtcAddress = takerBtcAddress; + return this; + } + + public BsqSwapTradeInfoBuilder withErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + return this; + } + + public BsqSwapTradeInfo build() { + return new BsqSwapTradeInfo(this); + } +} + diff --git a/core/src/main/java/bisq/core/api/model/builder/OfferInfoBuilder.java b/core/src/main/java/bisq/core/api/model/builder/OfferInfoBuilder.java new file mode 100644 index 00000000000..3773b465f72 --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/builder/OfferInfoBuilder.java @@ -0,0 +1,223 @@ +/* + * 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.builder; + +import bisq.core.api.model.OfferInfo; + +import lombok.Getter; + +/* + * A builder helps avoid bungling use of a large OfferInfo constructor + * argument list. If consecutive argument values of the same type are not + * ordered correctly, the compiler won't complain but the resulting bugs could + * be hard to find and fix. + */ +@Getter +public final class OfferInfoBuilder { + + private String id; + private String direction; + private long price; + private boolean useMarketBasedPrice; + private double marketPriceMargin; + private long amount; + private long minAmount; + private long volume; + private long minVolume; + private long txFee; + private long makerFee; + private String offerFeePaymentTxId; + private long buyerSecurityDeposit; + private long sellerSecurityDeposit; + private long triggerPrice; + private boolean isCurrencyForMakerFeeBtc; + private String paymentAccountId; + private String paymentMethodId; + private String paymentMethodShortName; + private String baseCurrencyCode; + private String counterCurrencyCode; + private long date; + private String state; + private boolean isActivated; + private boolean isMyOffer; + private boolean isMyPendingOffer; + private boolean isBsqSwapOffer; + private String ownerNodeAddress; + private String pubKeyRing; + private String versionNumber; + private int protocolVersion; + + public OfferInfoBuilder withId(String id) { + this.id = id; + return this; + } + + public OfferInfoBuilder withDirection(String direction) { + this.direction = direction; + return this; + } + + public OfferInfoBuilder withPrice(long price) { + this.price = price; + return this; + } + + public OfferInfoBuilder withUseMarketBasedPrice(boolean useMarketBasedPrice) { + this.useMarketBasedPrice = useMarketBasedPrice; + return this; + } + + public OfferInfoBuilder withMarketPriceMargin(double useMarketBasedPrice) { + this.marketPriceMargin = useMarketBasedPrice; + return this; + } + + public OfferInfoBuilder withAmount(long amount) { + this.amount = amount; + return this; + } + + public OfferInfoBuilder withMinAmount(long minAmount) { + this.minAmount = minAmount; + return this; + } + + public OfferInfoBuilder withVolume(long volume) { + this.volume = volume; + return this; + } + + public OfferInfoBuilder withMinVolume(long minVolume) { + this.minVolume = minVolume; + return this; + } + + public OfferInfoBuilder withTxFee(long txFee) { + this.txFee = txFee; + return this; + } + + public OfferInfoBuilder withMakerFee(long makerFee) { + this.makerFee = makerFee; + return this; + } + + public OfferInfoBuilder withOfferFeePaymentTxId(String offerFeePaymentTxId) { + this.offerFeePaymentTxId = offerFeePaymentTxId; + return this; + } + + public OfferInfoBuilder withBuyerSecurityDeposit(long buyerSecurityDeposit) { + this.buyerSecurityDeposit = buyerSecurityDeposit; + return this; + } + + public OfferInfoBuilder withSellerSecurityDeposit(long sellerSecurityDeposit) { + this.sellerSecurityDeposit = sellerSecurityDeposit; + return this; + } + + public OfferInfoBuilder withTriggerPrice(long triggerPrice) { + this.triggerPrice = triggerPrice; + return this; + } + + public OfferInfoBuilder withIsCurrencyForMakerFeeBtc(boolean isCurrencyForMakerFeeBtc) { + this.isCurrencyForMakerFeeBtc = isCurrencyForMakerFeeBtc; + return this; + } + + public OfferInfoBuilder withPaymentAccountId(String paymentAccountId) { + this.paymentAccountId = paymentAccountId; + return this; + } + + public OfferInfoBuilder withPaymentMethodId(String paymentMethodId) { + this.paymentMethodId = paymentMethodId; + return this; + } + + public OfferInfoBuilder withPaymentMethodShortName(String paymentMethodShortName) { + this.paymentMethodShortName = paymentMethodShortName; + return this; + } + + public OfferInfoBuilder withBaseCurrencyCode(String baseCurrencyCode) { + this.baseCurrencyCode = baseCurrencyCode; + return this; + } + + public OfferInfoBuilder withCounterCurrencyCode(String counterCurrencyCode) { + this.counterCurrencyCode = counterCurrencyCode; + return this; + } + + public OfferInfoBuilder withDate(long date) { + this.date = date; + return this; + } + + public OfferInfoBuilder withState(String state) { + this.state = state; + return this; + } + + public OfferInfoBuilder withIsActivated(boolean isActivated) { + this.isActivated = isActivated; + return this; + } + + public OfferInfoBuilder withIsMyOffer(boolean isMyOffer) { + this.isMyOffer = isMyOffer; + return this; + } + + public OfferInfoBuilder withIsMyPendingOffer(boolean isMyPendingOffer) { + this.isMyPendingOffer = isMyPendingOffer; + return this; + } + + public OfferInfoBuilder withIsBsqSwapOffer(boolean isBsqSwapOffer) { + this.isBsqSwapOffer = isBsqSwapOffer; + return this; + } + + public OfferInfoBuilder withOwnerNodeAddress(String ownerNodeAddress) { + this.ownerNodeAddress = ownerNodeAddress; + return this; + } + + public OfferInfoBuilder withPubKeyRing(String pubKeyRing) { + this.pubKeyRing = pubKeyRing; + return this; + } + + public OfferInfoBuilder withVersionNumber(String versionNumber) { + this.versionNumber = versionNumber; + return this; + } + + public OfferInfoBuilder withProtocolVersion(int protocolVersion) { + this.protocolVersion = protocolVersion; + return this; + } + + public OfferInfo build() { + return new OfferInfo(this); + } +} 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 new file mode 100644 index 00000000000..2bff2254a85 --- /dev/null +++ b/core/src/main/java/bisq/core/api/model/builder/TradeInfoV1Builder.java @@ -0,0 +1,195 @@ +/* + * 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.builder; + +import bisq.core.api.model.ContractInfo; +import bisq.core.api.model.OfferInfo; +import bisq.core.api.model.TradeInfo; + +import lombok.Getter; + +/** + * A builder helps avoid bungling use of a large TradeInfo constructor + * argument list. If consecutive argument values of the same type are not + * ordered correctly, the compiler won't complain but the resulting bugs could + * be hard to find and fix. + */ +@Getter +public final class TradeInfoV1Builder { + + private OfferInfo offer; + private String tradeId; + private String shortId; + private long date; + private String role; + private boolean isCurrencyForTakerFeeBtc; + private long txFeeAsLong; + private long takerFeeAsLong; + private String takerFeeTxId; + private String depositTxId; + private String payoutTxId; + private long tradeAmountAsLong; + private long tradePrice; + private long tradeVolume; + private String tradingPeerNodeAddress; + private String state; + private String phase; + private String tradePeriodState; + private boolean isDepositPublished; + private boolean isDepositConfirmed; + private boolean isFiatSent; + private boolean isFiatReceived; + private boolean isPayoutPublished; + private boolean isWithdrawn; + private String contractAsJson; + private ContractInfo contract; + + public TradeInfoV1Builder withOffer(OfferInfo offer) { + this.offer = offer; + return this; + } + + public TradeInfoV1Builder withTradeId(String tradeId) { + this.tradeId = tradeId; + return this; + } + + public TradeInfoV1Builder withShortId(String shortId) { + this.shortId = shortId; + return this; + } + + public TradeInfoV1Builder withDate(long date) { + this.date = date; + return this; + } + + public TradeInfoV1Builder withRole(String role) { + this.role = role; + return this; + } + + public TradeInfoV1Builder withIsCurrencyForTakerFeeBtc(boolean isCurrencyForTakerFeeBtc) { + this.isCurrencyForTakerFeeBtc = isCurrencyForTakerFeeBtc; + return this; + } + + public TradeInfoV1Builder withTxFeeAsLong(long txFeeAsLong) { + this.txFeeAsLong = txFeeAsLong; + return this; + } + + public TradeInfoV1Builder withTakerFeeAsLong(long takerFeeAsLong) { + this.takerFeeAsLong = takerFeeAsLong; + return this; + } + + public TradeInfoV1Builder withTakerFeeTxId(String takerFeeTxId) { + this.takerFeeTxId = takerFeeTxId; + return this; + } + + public TradeInfoV1Builder withDepositTxId(String depositTxId) { + this.depositTxId = depositTxId; + return this; + } + + public TradeInfoV1Builder withPayoutTxId(String payoutTxId) { + this.payoutTxId = payoutTxId; + return this; + } + + public TradeInfoV1Builder withTradeAmountAsLong(long tradeAmountAsLong) { + this.tradeAmountAsLong = tradeAmountAsLong; + return this; + } + + public TradeInfoV1Builder withTradePrice(long tradePrice) { + this.tradePrice = tradePrice; + return this; + } + + public TradeInfoV1Builder withTradeVolume(long tradeVolume) { + this.tradeVolume = tradeVolume; + return this; + } + + public TradeInfoV1Builder withTradePeriodState(String tradePeriodState) { + this.tradePeriodState = tradePeriodState; + return this; + } + + public TradeInfoV1Builder withState(String state) { + this.state = state; + return this; + } + + public TradeInfoV1Builder withPhase(String phase) { + this.phase = phase; + return this; + } + + public TradeInfoV1Builder withTradingPeerNodeAddress(String tradingPeerNodeAddress) { + this.tradingPeerNodeAddress = tradingPeerNodeAddress; + return this; + } + + public TradeInfoV1Builder withIsDepositPublished(boolean isDepositPublished) { + this.isDepositPublished = isDepositPublished; + return this; + } + + public TradeInfoV1Builder withIsDepositConfirmed(boolean isDepositConfirmed) { + this.isDepositConfirmed = isDepositConfirmed; + return this; + } + + public TradeInfoV1Builder withIsFiatSent(boolean isFiatSent) { + this.isFiatSent = isFiatSent; + return this; + } + + public TradeInfoV1Builder withIsFiatReceived(boolean isFiatReceived) { + this.isFiatReceived = isFiatReceived; + return this; + } + + public TradeInfoV1Builder withIsPayoutPublished(boolean isPayoutPublished) { + this.isPayoutPublished = isPayoutPublished; + return this; + } + + public TradeInfoV1Builder withIsWithdrawn(boolean isWithdrawn) { + this.isWithdrawn = isWithdrawn; + return this; + } + + public TradeInfoV1Builder withContractAsJson(String contractAsJson) { + this.contractAsJson = contractAsJson; + return this; + } + + public TradeInfoV1Builder withContract(ContractInfo contract) { + this.contract = contract; + return this; + } + + public TradeInfo build() { + return new TradeInfo(this); + } +} diff --git a/core/src/main/java/bisq/core/trade/protocol/bsq_swap/model/BsqSwapProtocolModel.java b/core/src/main/java/bisq/core/trade/protocol/bsq_swap/model/BsqSwapProtocolModel.java index bdf5dd88b2c..47b09d10b45 100644 --- a/core/src/main/java/bisq/core/trade/protocol/bsq_swap/model/BsqSwapProtocolModel.java +++ b/core/src/main/java/bisq/core/trade/protocol/bsq_swap/model/BsqSwapProtocolModel.java @@ -69,6 +69,7 @@ public class BsqSwapProtocolModel implements ProtocolModel { transient private Offer offer; @Setter transient private TradeMessage tradeMessage; + // TODO rename tradingPeerNodeAddress ? @Nullable @Setter transient private NodeAddress tempTradingPeerNodeAddress; diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 00ec52305a9..641d9ab2eb0 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -18,7 +18,6 @@ package bisq.daemon.grpc; import bisq.core.api.CoreApi; -import bisq.core.api.model.BsqSwapTradeInfo; import bisq.core.api.model.TradeInfo; import bisq.core.trade.model.bisq_v1.Trade; @@ -48,7 +47,6 @@ import lombok.extern.slf4j.Slf4j; -import static bisq.core.api.model.BsqSwapTradeInfo.toBsqSwapTradeInfo; import static bisq.core.api.model.TradeInfo.toNewTradeInfo; import static bisq.core.api.model.TradeInfo.toTradeInfo; import static bisq.daemon.grpc.interceptor.GrpcServiceRateMeteringConfig.getCustomRateMeteringInterceptor; @@ -80,8 +78,9 @@ public void getBsqSwapTrade(GetTradeRequest req, var bsqSwapTrade = coreApi.getBsqSwapTrade(req.getTradeId()); boolean wasMyOffer = coreApi.isMyOffer(bsqSwapTrade.getOffer().getId()); String role = coreApi.getBsqSwapTradeRole(req.getTradeId()); + var tradeInfo = toTradeInfo(bsqSwapTrade, role, wasMyOffer); var reply = GetBsqSwapTradeReply.newBuilder() - .setBsqSwapTrade(toBsqSwapTradeInfo(bsqSwapTrade, role, wasMyOffer).toProtoMessage()) + .setBsqSwapTrade(tradeInfo.toProtoMessage()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); @@ -107,11 +106,9 @@ public void takeBsqSwapOffer(TakeBsqSwapOfferRequest req, req.getTakerFeeCurrencyCode(), bsqSwapTrade -> { String role = coreApi.getBsqSwapTradeRole(bsqSwapTrade); - BsqSwapTradeInfo bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, - role, - false); + var tradeInfo = toNewTradeInfo(bsqSwapTrade, role); var reply = TakeBsqSwapOfferReply.newBuilder() - .setBsqSwapTrade(bsqSwapTradeInfo.toProtoMessage()) + .setBsqSwapTrade(tradeInfo.toProtoMessage()) .build(); responseObserver.onNext(reply); responseObserver.onCompleted(); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index e050f951051..5a5d81c88fe 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -408,7 +408,7 @@ message TakeBsqSwapOfferRequest { } message TakeBsqSwapOfferReply { - BsqSwapTradeInfo bsqSwapTrade = 1; + TradeInfo bsqSwapTrade = 1; AvailabilityResultWithDescription failureReason = 2; } @@ -438,7 +438,7 @@ message ConfirmPaymentReceivedReply { } message GetBsqSwapTradeReply { - BsqSwapTradeInfo bsqSwapTrade = 1; + TradeInfo bsqSwapTrade = 1; } message GetTradeRequest { @@ -465,34 +465,8 @@ message WithdrawFundsRequest { message WithdrawFundsReply { } -message BsqSwapTradeInfo { - OfferInfo offer = 1; - string tradeId = 2; - string tempTradingPeerNodeAddress = 3; - string peerNodeAddress = 4; - string txId = 5; - uint64 bsqTradeAmount = 6; - uint64 btcTradeAmount = 7; - uint64 tradePrice = 8; - bool isCurrencyForMakerFeeBtc = 9; - bool isCurrencyForTakerFeeBtc = 10; - uint64 bsqMakerTradeFee = 11; - uint64 btcMakerTradeFee = 12; - uint64 bsqTakerTradeFee = 13; - uint64 btcTakerTradeFee = 14; - uint64 txFeePerVbyte = 15; - uint64 txFee = 16; - string makerBsqAddress = 17; - string makerBtcAddress = 18; - string takerBsqAddress = 19; - string takerBtcAddress = 20; - uint64 takeOfferDate = 21; - string role = 22; - string state = 23; - string errorMessage = 24; -} - message TradeInfo { + // Bisq v1 trade protocol fields. OfferInfo offer = 1; string tradeId = 2; string shortId = 3; @@ -519,8 +493,8 @@ message TradeInfo { string contractAsJson = 24; ContractInfo contract = 25; uint64 tradeVolume = 26; - // TODO See if a sub-message BsqSwapTradeInfo field could be added here. - // Use the existing BsqSwapTradeInfo message, without the redundant fields. + // Optional Bisq v2+ trade protocol fields. + BsqSwapTradeInfo bsqSwapTradeInfo = 28; } message ContractInfo { @@ -538,6 +512,21 @@ message ContractInfo { uint64 lockTime = 12; } +message BsqSwapTradeInfo { + // BSQ Swap protocol specific fields not common to Bisq v1 trade protocol fields. + string txId = 1; + uint64 bsqTradeAmount = 2; + uint64 btcTradeAmount = 3; + uint64 bsqMakerTradeFee = 4; + uint64 bsqTakerTradeFee = 5; + uint64 txFeePerVbyte = 6; + string makerBsqAddress = 7; + string makerBtcAddress = 8; + string takerBsqAddress = 9; + string takerBtcAddress = 10; + string errorMessage = 11; +} + message PaymentAccountPayloadInfo { string id = 1; string paymentMethodId = 2; From fc53ca48c1a22998655c37915087c81ab511ee32 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 14 Nov 2021 13:40:59 -0300 Subject: [PATCH 10/39] Add CLI output IntegerColumn --- .../bisq/cli/table/column/IntegerColumn.java | 93 +++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 cli/src/main/java/bisq/cli/table/column/IntegerColumn.java diff --git a/cli/src/main/java/bisq/cli/table/column/IntegerColumn.java b/cli/src/main/java/bisq/cli/table/column/IntegerColumn.java new file mode 100644 index 00000000000..5c4e04af9d0 --- /dev/null +++ b/cli/src/main/java/bisq/cli/table/column/IntegerColumn.java @@ -0,0 +1,93 @@ +/* + * 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.column; + +import java.util.ArrayList; +import java.util.List; +import java.util.function.Predicate; +import java.util.stream.IntStream; + +import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; + +/** + * For displaying Integer values. + */ +public class IntegerColumn extends NumberColumn { + + protected final List rows = new ArrayList<>(); + + protected final Predicate isNewMaxWidth = (s) -> s != null && !s.isEmpty() && s.length() > maxWidth; + + // The default IntegerColumn JUSTIFICATION is RIGHT. + public IntegerColumn(String name) { + this(name, RIGHT); + } + + public IntegerColumn(String name, JUSTIFICATION justification) { + super(name, justification); + this.maxWidth = name.length(); + } + + @Override + public void addRow(Integer value) { + rows.add(value); + + String s = String.valueOf(value); + if (isNewMaxWidth.test(s)) + maxWidth = s.length(); + } + + @Override + public List getRows() { + return rows; + } + + @Override + public int rowCount() { + return rows.size(); + } + + @Override + public boolean isEmpty() { + return rows.isEmpty(); + } + + @Override + public Integer getRow(int rowIndex) { + return rows.get(rowIndex); + } + + @Override + public void updateRow(int rowIndex, Integer newValue) { + rows.set(rowIndex, newValue); + } + + @Override + public String getRowAsFormattedString(int rowIndex) { + String s = String.valueOf(getRow(rowIndex)); + return toJustifiedString(s); + } + + @Override + public StringColumn asStringColumn() { + IntStream.range(0, rows.size()).forEachOrdered(rowIndex -> + stringColumn.addRow(getRowAsFormattedString(rowIndex))); + + return stringColumn; + } +} From 4ca878a8e1cf2a6c400a4d95a4c5a86faeaa4ba6 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 14 Nov 2021 13:48:12 -0300 Subject: [PATCH 11/39] Adjust API 'gettrade' for Bsq swaps - Made several adjustments to CLI's 'gettrade' output related code so it can show single trade details for either Bisq v1 trades, or BSQ swap trades. - Did minor refactoring of API's core to retrieve # tx confirmations for an addresses and transactions. - Show # of tx confirmations in bsq swap trade detail. --- .../method/trade/BsqSwapTradeTest.java | 24 +++- .../builder/AbstractTradeListBuilder.java | 40 +++++-- .../table/builder/TableBuilderConstants.java | 3 +- .../builder/TradeDetailTableBuilder.java | 112 ++++++++++++++---- .../builder/TradeTableColumnSupplier.java | 88 ++++++++++---- core/src/main/java/bisq/core/api/CoreApi.java | 4 + .../bisq/core/api/CoreWalletsService.java | 54 +++++---- .../bisq/core/api/model/BsqSwapTradeInfo.java | 12 +- .../java/bisq/core/api/model/OfferInfo.java | 8 +- .../java/bisq/core/api/model/TradeInfo.java | 11 +- .../builder/BsqSwapTradeInfoBuilder.java | 6 + .../bisq/daemon/grpc/GrpcTradesService.java | 6 +- proto/src/main/proto/grpc.proto | 3 +- 13 files changed, 275 insertions(+), 96 deletions(-) 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 efea480e7bd..674109f569f 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java @@ -111,21 +111,39 @@ public void testBobTakesBsqSwapOffer() { var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId(), bobsBsqSwapAcct.getId(), BISQ_FEE_CURRENCY_CODE); - log.debug("BsqSwap Trade at PREPARATION: {}", swapTrade); + 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, swapTrade.getTradeId()); - log.debug("BsqSwap Trade at COMPLETION: {}", swapTrade); log.debug("BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(swapTrade)); assertEquals(COMPLETED.name(), swapTrade.getState()); } @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()); + } + + @Test + @Order(5) public void testGetBalancesAfterTrade() { - genBtcBlocksThenWait(1, 5_000); var alicesBalances = aliceClient.getBalances(); log.debug("Alice's After Trade Balance:\n{}", formatBalancesTbls(alicesBalances)); var bobsBalances = bobClient.getBalances(); 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 23a265a45ce..5c7b109e574 100644 --- a/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/AbstractTradeListBuilder.java @@ -102,6 +102,15 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { @Nullable protected final Column colAltcoinReceiveAddressColumn; + // BSQ swap trade detail specific columns + + @Nullable + protected final Column status; + @Nullable + protected final Column colTxId; + @Nullable + protected final Column colNumConfirmations; + AbstractTradeListBuilder(TableType tableType, List protos) { super(tableType, protos); validate(); @@ -125,7 +134,9 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { this.colRole = colSupplier.roleColumn.get(); this.colOfferType = colSupplier.offerTypeColumn.get(); this.colStatusDescription = colSupplier.statusDescriptionColumn.get(); - // Trade detail specific columns + + // Trade detail specific columns, some in common with BSQ swap trades detail. + this.colIsDepositPublished = colSupplier.depositPublishedColumn.get(); this.colIsDepositConfirmed = colSupplier.depositConfirmedColumn.get(); this.colIsPayoutPublished = colSupplier.payoutPublishedColumn.get(); @@ -135,6 +146,12 @@ abstract class AbstractTradeListBuilder extends AbstractTableBuilder { this.colIsPaymentSent = colSupplier.paymentSentColumn.get(); this.colIsPaymentReceived = colSupplier.paymentReceivedColumn.get(); this.colAltcoinReceiveAddressColumn = colSupplier.altcoinReceiveAddressColumn.get(); + + // BSQ swap trade detail specific columns + + this.status = colSupplier.bsqSwapStatusColumn.get(); + this.colTxId = colSupplier.bsqSwapTxIdColumn.get(); + this.colNumConfirmations = colSupplier.numConfirmationsColumn.get(); } protected void validate() { @@ -149,9 +166,8 @@ protected void validate() { // Helper Functions 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 isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); // Column Value Functions @@ -185,7 +201,6 @@ protected void validate() { } }; - // TODO Move to TradeUtil ? protected final Function toPriceDeviation = (t) -> t.getOffer().getUseMarketBasedPrice() ? formatToPercent(t.getOffer().getMarketPriceMargin()) @@ -196,8 +211,6 @@ protected void validate() { ? t.getTxFeeAsLong() : t.getOffer().getTxFee(); - - // TODO Move to TradeUtil ? protected final BiFunction toTradeFeeBsq = (t, isMyOffer) -> { if (isMyOffer) { return t.getOffer().getIsCurrencyForMakerFeeBtc() @@ -210,7 +223,6 @@ protected void validate() { } }; - // TODO Move to TradeUtil ? protected final BiFunction toTradeFeeBtc = (t, isMyOffer) -> { if (isMyOffer) { return t.getOffer().getIsCurrencyForMakerFeeBtc() @@ -223,12 +235,18 @@ protected void validate() { } }; - protected final Function toMyMakerOrTakerFee = (t) -> - isTaker.test(t) + protected final Function toMyMakerOrTakerFee = (t) -> { + if (isBsqSwapTrade.test(t)) { + return isTaker.test(t) + ? t.getBsqSwapTradeInfo().getBsqTakerTradeFee() + : t.getBsqSwapTradeInfo().getBsqMakerTradeFee(); + } else { + return isTaker.test(t) ? t.getTakerFeeAsLong() : t.getOffer().getMakerFee(); + } + }; - // TODO Move to TradeUtil ? SEE ClosedTradesViewModel # getDirectionLabel protected final Function toOfferType = (t) -> { if (isFiatTrade.test(t)) { return t.getOffer().getDirection() + " " + t.getOffer().getBaseCurrencyCode(); @@ -267,7 +285,7 @@ protected void validate() { } }; - // TODO Stuff to move into bisq/cli/CurrencyFormat.java ? + // TODO Move to bisq/cli/CurrencyFormat.java ? public static String formatToPercent(double value) { DecimalFormat decimalFormat = new DecimalFormat("#.##"); 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 1ea28b63ecc..b29aa3e3ec9 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TableBuilderConstants.java +++ b/cli/src/main/java/bisq/cli/table/builder/TableBuilderConstants.java @@ -35,6 +35,7 @@ class TableBuilderConstants { static final String COL_HEADER_LOCKUP_BONDS_BALANCE = "Lockup Bonds Balance"; 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_CONFIRMATIONS = "Confirmations"; @@ -65,8 +66,6 @@ class TableBuilderConstants { static final String COL_HEADER_TRADE_ID = "Trade ID"; static final String COL_HEADER_TRADE_ROLE = "My Role"; static final String COL_HEADER_TRADE_SHORT_ID = "ID"; - @Deprecated - static final String COL_HEADER_TRADE_TX_FEE = "Tx Fee(BTC)"; static final String COL_HEADER_TRADE_MAKER_FEE = "Maker Fee(%-3s)"; static final String COL_HEADER_TRADE_TAKER_FEE = "Taker Fee(%-3s)"; static final String COL_HEADER_TRADE_FEE = "Trade Fee"; diff --git a/cli/src/main/java/bisq/cli/table/builder/TradeDetailTableBuilder.java b/cli/src/main/java/bisq/cli/table/builder/TradeDetailTableBuilder.java index 693a0c916a0..9d577ca1f6d 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TradeDetailTableBuilder.java +++ b/cli/src/main/java/bisq/cli/table/builder/TradeDetailTableBuilder.java @@ -17,10 +17,16 @@ package bisq.cli.table.builder; +import bisq.proto.grpc.TradeInfo; + import java.util.ArrayList; import java.util.List; +import java.util.function.Predicate; import static bisq.cli.table.builder.TableType.TRADE_DETAIL_TBL; +import static java.lang.String.format; +import static protobuf.BsqSwapTrade.State.COMPLETED; +import static protobuf.BsqSwapTrade.State.PREPARATION; @@ -30,8 +36,12 @@ /** * Builds a {@code bisq.cli.table.Table} from a {@code bisq.proto.grpc.TradeInfo} object. */ +@SuppressWarnings("ConstantConditions") class TradeDetailTableBuilder extends AbstractTradeListBuilder { + private final Predicate isPendingBsqSwap = (t) -> t.getState().equals(PREPARATION.name()); + private final Predicate isCompletedBsqSwap = (t) -> t.getState().equals(COMPLETED.name()); + TradeDetailTableBuilder(List protos) { super(TRADE_DETAIL_TBL, protos); } @@ -41,33 +51,70 @@ class TradeDetailTableBuilder extends AbstractTradeListBuilder { * @return Table containing one row */ public Table build() { - populateColumns(); - List> columns = defineColumnList(); + // A trade detail table only has one row. + var trade = trades.get(0); + populateColumns(trade); + List> columns = defineColumnList(trade); return new Table(columns.toArray(new Column[0])); } - private void populateColumns() { - trades.stream().forEachOrdered(t -> { - colTradeId.addRow(t.getShortId()); - colRole.addRow(t.getRole()); - colPrice.addRow(t.getTradePrice()); - colAmountInBtc.addRow(toAmount.apply(t)); - colMinerTxFee.addRow(toMyMinerTxFee.apply(t)); - colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(t)); - colIsDepositPublished.addRow(t.getIsDepositPublished()); - colIsDepositConfirmed.addRow(t.getIsDepositConfirmed()); - colTradeCost.addRow(toTradeVolume.apply(t)); - colIsPaymentSent.addRow(t.getIsFiatSent()); - colIsPaymentReceived.addRow(t.getIsFiatReceived()); - colIsPayoutPublished.addRow(t.getIsPayoutPublished()); - colIsFundsWithdrawn.addRow(t.getIsWithdrawn()); - - if (colAltcoinReceiveAddressColumn != null) - colAltcoinReceiveAddressColumn.addRow(toAltcoinReceiveAddress.apply(t)); - }); + private void populateColumns(TradeInfo trade) { + if (isBsqSwapTrade.test(trade)) { + var isPending = isPendingBsqSwap.test(trade); + var isCompleted = isCompletedBsqSwap.test(trade); + if (isPending == isCompleted) + throw new IllegalStateException( + format("programmer error: trade must be either pending or completed, is pending=%s and completed=%s", + isPending, + isCompleted)); + populateBsqSwapTradeColumns(trade); + } else { + populateBisqV1TradeColumns(trade); + } } - private List> defineColumnList() { + private void populateBisqV1TradeColumns(TradeInfo trade) { + colTradeId.addRow(trade.getShortId()); + colRole.addRow(trade.getRole()); + colPrice.addRow(trade.getTradePrice()); + colAmountInBtc.addRow(toAmount.apply(trade)); + colMinerTxFee.addRow(toMyMinerTxFee.apply(trade)); + colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(trade)); + colIsDepositPublished.addRow(trade.getIsDepositPublished()); + colIsDepositConfirmed.addRow(trade.getIsDepositConfirmed()); + colTradeCost.addRow(toTradeVolume.apply(trade)); + colIsPaymentSent.addRow(trade.getIsFiatSent()); + colIsPaymentReceived.addRow(trade.getIsFiatReceived()); + colIsPayoutPublished.addRow(trade.getIsPayoutPublished()); + colIsFundsWithdrawn.addRow(trade.getIsWithdrawn()); + if (colAltcoinReceiveAddressColumn != null) + colAltcoinReceiveAddressColumn.addRow(toAltcoinReceiveAddress.apply(trade)); + } + + private void populateBsqSwapTradeColumns(TradeInfo trade) { + colTradeId.addRow(trade.getShortId()); + colRole.addRow(trade.getRole()); + colPrice.addRow(trade.getTradePrice()); + colAmountInBtc.addRow(toAmount.apply(trade)); + colMinerTxFee.addRow(toMyMinerTxFee.apply(trade)); + colBisqTradeFee.addRow(toMyMakerOrTakerFee.apply(trade)); + colTradeCost.addRow(toTradeVolume.apply(trade)); + + var isCompleted = isCompletedBsqSwap.test(trade); + status.addRow(isCompleted ? "COMPLETED" : "PENDING"); + if (isCompleted) { + colTxId.addRow(trade.getBsqSwapTradeInfo().getTxId()); + colNumConfirmations.addRow(trade.getBsqSwapTradeInfo().getNumConfirmations()); + } + } + + private List> defineColumnList(TradeInfo trade) { + return isBsqSwapTrade.test(trade) + ? getBsqSwapTradeColumnList(isCompletedBsqSwap.test(trade)) + : getBisqV1TradeColumnList(); + } + + private List> getBisqV1TradeColumnList() { List> columns = new ArrayList<>() {{ add(colTradeId); add(colRole); @@ -89,4 +136,25 @@ private List> defineColumnList() { return columns; } + + private List> getBsqSwapTradeColumnList(boolean isCompleted) { + List> columns = new ArrayList<>() {{ + add(colTradeId); + add(colRole); + add(colPrice.asStringColumn()); + add(colAmountInBtc.asStringColumn()); + add(colMinerTxFee.asStringColumn()); + add(colBisqTradeFee.asStringColumn()); + add(colTradeCost.asStringColumn()); + add(status); + }}; + + if (isCompleted) + columns.add(colTxId); + + if (!colNumConfirmations.isEmpty()) + columns.add(colNumConfirmations.asStringColumn()); + + return columns; + } } 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 967ee1779f6..ae03cb14e87 100644 --- a/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java +++ b/cli/src/main/java/bisq/cli/table/builder/TradeTableColumnSupplier.java @@ -40,6 +40,7 @@ import static bisq.cli.table.column.Column.JUSTIFICATION.LEFT; import static bisq.cli.table.column.Column.JUSTIFICATION.RIGHT; import static bisq.cli.table.column.FiatColumn.DISPLAY_MODE.VOLUME; +import static java.lang.String.format; @@ -49,6 +50,7 @@ import bisq.cli.table.column.Column; import bisq.cli.table.column.FiatColumn; import bisq.cli.table.column.Iso8601DateTimeColumn; +import bisq.cli.table.column.LongColumn; import bisq.cli.table.column.MixedPriceColumn; import bisq.cli.table.column.MixedTradeFeeColumn; import bisq.cli.table.column.MixedVolumeColumn; @@ -79,7 +81,10 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { 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()); + private final Predicate isBsqSwapTrade = (t) -> t.getOffer().getIsBsqSwapOffer(); private final Predicate isTaker = (t) -> t.getRole().toLowerCase().contains("taker"); + private final Supplier isSwapTradeDetail = () -> + isTradeDetailTblBuilder.get() && isBsqSwapTrade.test(firstRow.get()); final Supplier tradeIdColumn = () -> isTradeDetailTblBuilder.get() ? new StringColumn(COL_HEADER_TRADE_SHORT_ID) @@ -95,8 +100,8 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { private final Function> toDetailedPriceColumn = (t) -> { String colHeader = isFiatTrade.test(t) - ? String.format(COL_HEADER_DETAILED_PRICE, t.getOffer().getCounterCurrencyCode()) - : String.format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, t.getOffer().getBaseCurrencyCode()); + ? format(COL_HEADER_DETAILED_PRICE, t.getOffer().getCounterCurrencyCode()) + : format(COL_HEADER_DETAILED_PRICE_OF_ALTCOIN, t.getOffer().getBaseCurrencyCode()); return isFiatTrade.test(t) ? new FiatColumn(colHeader) : new AltcoinColumn(colHeader); @@ -116,7 +121,7 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { private final Function> toDetailedAmountColumn = (t) -> { String headerCurrencyCode = t.getOffer().getBaseCurrencyCode(); - String colHeader = String.format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode); + String colHeader = format(COL_HEADER_DETAILED_AMOUNT, headerCurrencyCode); return isFiatTrade.test(t) ? new SatoshiColumn(colHeader) : new AltcoinColumn(colHeader, ALTCOIN_OFFER_VOLUME); @@ -142,10 +147,14 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { ? null : new StringColumn(COL_HEADER_PAYMENT_METHOD, LEFT); - final Supplier roleColumn = () -> - isTradeDetailTblBuilder.get() || isOpenTradeTblBuilder.get() || isFailedTradeTblBuilder.get() + final Supplier roleColumn = () -> { + if (isSwapTradeDetail.get()) + return new StringColumn(COL_HEADER_BSQ_SWAP_TRADE_ROLE); + else + return isTradeDetailTblBuilder.get() || isOpenTradeTblBuilder.get() || isFailedTradeTblBuilder.get() ? new StringColumn(COL_HEADER_TRADE_ROLE) : null; + }; final Function> toSecurityDepositColumn = (name) -> isClosedTradeTblBuilder.get() ? new SatoshiColumn(name) @@ -161,21 +170,42 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { private final Function> toBooleanColumn = BooleanColumn::new; - final Supplier> depositPublishedColumn = () -> isTradeDetailTblBuilder.get() - ? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_PUBLISHED) - : null; + final Supplier> depositPublishedColumn = () -> { + if (isSwapTradeDetail.get()) + return null; + else + return isTradeDetailTblBuilder.get() + ? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_PUBLISHED) + : null; + }; - final Supplier> depositConfirmedColumn = () -> isTradeDetailTblBuilder.get() - ? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_CONFIRMED) - : null; + final Supplier> depositConfirmedColumn = () -> { + if (isSwapTradeDetail.get()) + return null; + else + return isTradeDetailTblBuilder.get() + ? toBooleanColumn.apply(COL_HEADER_TRADE_DEPOSIT_CONFIRMED) + : null; - final Supplier> payoutPublishedColumn = () -> isTradeDetailTblBuilder.get() - ? toBooleanColumn.apply(COL_HEADER_TRADE_PAYOUT_PUBLISHED) - : null; + }; - final Supplier> fundsWithdrawnColumn = () -> isTradeDetailTblBuilder.get() - ? toBooleanColumn.apply(COL_HEADER_TRADE_WITHDRAWN) - : null; + final Supplier> payoutPublishedColumn = () -> { + if (isSwapTradeDetail.get()) + return null; + else + return isTradeDetailTblBuilder.get() + ? toBooleanColumn.apply(COL_HEADER_TRADE_PAYOUT_PUBLISHED) + : null; + }; + + final Supplier> fundsWithdrawnColumn = () -> { + if (isSwapTradeDetail.get()) + return null; + else + return isTradeDetailTblBuilder.get() + ? toBooleanColumn.apply(COL_HEADER_TRADE_WITHDRAWN) + : null; + }; final Supplier> bisqTradeDetailFeeColumn = () -> { if (isTradeDetailTblBuilder.get()) { @@ -184,8 +214,8 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { ? t.getIsCurrencyForTakerFeeBtc() ? "BTC" : "BSQ" : t.getOffer().getIsCurrencyForMakerFeeBtc() ? "BTC" : "BSQ"; String colHeader = isTaker.test(t) - ? String.format(COL_HEADER_TRADE_TAKER_FEE, headerCurrencyCode) - : String.format(COL_HEADER_TRADE_MAKER_FEE, headerCurrencyCode); + ? format(COL_HEADER_TRADE_TAKER_FEE, headerCurrencyCode) + : format(COL_HEADER_TRADE_MAKER_FEE, headerCurrencyCode); boolean isBsqSatoshis = headerCurrencyCode.equals("BSQ"); return new SatoshiColumn(colHeader, isBsqSatoshis); } else { @@ -201,7 +231,7 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { final Supplier> paymentSentColumn = () -> { if (isTradeDetailTblBuilder.get()) { String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get()); - String colHeader = String.format(COL_HEADER_TRADE_PAYMENT_SENT, headerCurrencyCode); + String colHeader = format(COL_HEADER_TRADE_PAYMENT_SENT, headerCurrencyCode); return new BooleanColumn(colHeader); } else { return null; @@ -211,7 +241,7 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { final Supplier> paymentReceivedColumn = () -> { if (isTradeDetailTblBuilder.get()) { String headerCurrencyCode = toPaymentCurrencyCode.apply(firstRow.get()); - String colHeader = String.format(COL_HEADER_TRADE_PAYMENT_RECEIVED, headerCurrencyCode); + String colHeader = format(COL_HEADER_TRADE_PAYMENT_RECEIVED, headerCurrencyCode); return new BooleanColumn(colHeader); } else { return null; @@ -222,7 +252,7 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { if (isTradeDetailTblBuilder.get()) { TradeInfo t = firstRow.get(); String headerCurrencyCode = t.getOffer().getCounterCurrencyCode(); - String colHeader = String.format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode); + String colHeader = format(COL_HEADER_TRADE_BUYER_COST, headerCurrencyCode); return isFiatTrade.test(t) ? new FiatColumn(colHeader, VOLUME) : new SatoshiColumn(colHeader); @@ -231,6 +261,18 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { } }; + final Supplier> bsqSwapTxIdColumn = () -> isSwapTradeDetail.get() + ? new StringColumn(COL_HEADER_TX_ID) + : null; + + final Supplier> bsqSwapStatusColumn = () -> isSwapTradeDetail.get() + ? new StringColumn(COL_HEADER_STATUS) + : null; + + final Supplier> numConfirmationsColumn = () -> isSwapTradeDetail.get() + ? new LongColumn(COL_HEADER_CONFIRMATIONS) + : null; + final Predicate showAltCoinBuyerAddress = (t) -> { if (isFiatTrade.test(t)) { return false; @@ -251,7 +293,7 @@ public TradeTableColumnSupplier(TableType tableType, List trades) { TradeInfo t = firstRow.get(); if (showAltCoinBuyerAddress.test(t)) { String headerCurrencyCode = toPaymentCurrencyCode.apply(t); - String colHeader = String.format(COL_HEADER_TRADE_ALTCOIN_BUYER_ADDRESS, headerCurrencyCode); + String colHeader = format(COL_HEADER_TRADE_ALTCOIN_BUYER_ADDRESS, headerCurrencyCode); return new StringColumn(colHeader); } else { return null; diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 409299c3330..75e082d18c3 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -401,6 +401,10 @@ public Transaction getTransaction(String txId) { return walletsService.getTransaction(txId); } + public int getTransactionConfirmations(String txId) { + return walletsService.getTransactionConfirmations(txId); + } + public void setWalletPassword(String password, String newPassword) { walletsService.setWalletPassword(password, newPassword); } diff --git a/core/src/main/java/bisq/core/api/CoreWalletsService.java b/core/src/main/java/bisq/core/api/CoreWalletsService.java index f2dec09a530..6579786e147 100644 --- a/core/src/main/java/bisq/core/api/CoreWalletsService.java +++ b/core/src/main/java/bisq/core/api/CoreWalletsService.java @@ -85,6 +85,7 @@ import static bisq.core.btc.wallet.Restrictions.getMinNonDustOutput; import static bisq.core.util.ParsingUtils.parseToCoin; import static java.lang.String.format; +import static java.util.Objects.requireNonNull; import static java.util.concurrent.TimeUnit.SECONDS; @Singleton @@ -209,10 +210,10 @@ List getFundingAddresses() { } return addressStrings.stream().map(address -> - new AddressBalanceInfo(address, - balances.getUnchecked(address), - getNumConfirmationsForMostRecentTransaction(address), - btcWalletService.isAddressUnused(getAddressEntry(address).getAddress()))) + new AddressBalanceInfo(address, + balances.getUnchecked(address), + getNumConfirmationsForMostRecentTransaction(address), + btcWalletService.isAddressUnused(getAddressEntry(address).getAddress()))) .collect(Collectors.toList()); } @@ -324,7 +325,7 @@ boolean verifyBsqSentToAddress(String address, String amount) { for (TransactionOutput txOut : spendableBsqTxOutputs) { if (isTxOutputAddressMatch.test(txOut) && isTxOutputValueMatch.test(txOut)) { log.info("\t\tTx {} output has matching address {} and value {}.", - txOut.getParentTransaction().getTxId(), + requireNonNull(txOut.getParentTransaction()).getTxId(), address, txOut.getValue().toPlainString()); numMatches++; @@ -346,6 +347,7 @@ void getTxFeeRate(ResultHandler resultHandler) { @SuppressWarnings({"unchecked", "Convert2MethodRef"}) ListenableFuture future = (ListenableFuture) executor.submit(() -> feeService.requestFees()); + //noinspection NullableProblems Futures.addCallback(future, new FutureCallback<>() { @Override public void onSuccess(@Nullable Void ignored) { @@ -393,23 +395,11 @@ TxFeeRateInfo getMostRecentTxFeeRateInfo() { } Transaction getTransaction(String txId) { - if (txId.length() != 64) - throw new IllegalArgumentException(format("%s is not a transaction id", txId)); - - try { - Transaction tx = btcWalletService.getTransaction(txId); - if (tx == null) - throw new IllegalArgumentException(format("tx with id %s not found", txId)); - else - return tx; + return getTransactionWithId(txId); + } - } catch (IllegalArgumentException ex) { - log.error("", ex); - throw new IllegalArgumentException( - format("could not get transaction with id %s%ncause: %s", - txId, - ex.getMessage().toLowerCase())); - } + int getTransactionConfirmations(String txId) { + return getTransactionWithId(txId).getConfidence().getDepthInBlocks(); } int getNumConfirmationsForMostRecentTransaction(String addressString) { @@ -654,12 +644,32 @@ private AddressEntry getAddressEntry(String addressString) { return addressEntry.get(); } + private Transaction getTransactionWithId(String txId) { + if (txId.length() != 64) + throw new IllegalArgumentException(format("%s is not a transaction id", txId)); + + try { + Transaction tx = btcWalletService.getTransaction(txId); + if (tx == null) + throw new IllegalArgumentException(format("tx with id %s not found", txId)); + else + return tx; + + } catch (IllegalArgumentException ex) { + log.error("", ex); + throw new IllegalArgumentException( + format("could not get transaction with id %s%ncause: %s", + txId, + ex.getMessage().toLowerCase())); + } + } + /** * Memoization stores the results of expensive function calls and returns * the cached result when the same input occurs again. * * Resulting LoadingCache is used by calling `.get(input I)` or - * `.getUnchecked(input I)`, depending on whether or not `f` can return null. + * `.getUnchecked(input I)`, depending on whether `f` can return null. * That's because CacheLoader throws an exception on null output from `f`. */ private static LoadingCache memoize(Function f) { 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 1dbd3b10abf..d46cf877a02 100644 --- a/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/BsqSwapTradeInfo.java @@ -39,6 +39,7 @@ public class BsqSwapTradeInfo implements Payload { private final String makerBtcAddress; private final String takerBsqAddress; private final String takerBtcAddress; + private final long numConfirmations; private final String errorMessage; public BsqSwapTradeInfo(BsqSwapTradeInfoBuilder builder) { @@ -52,10 +53,13 @@ public BsqSwapTradeInfo(BsqSwapTradeInfoBuilder builder) { this.makerBtcAddress = builder.getMakerBtcAddress(); this.takerBsqAddress = builder.getTakerBsqAddress(); this.takerBtcAddress = builder.getTakerBtcAddress(); + this.numConfirmations = builder.getNumConfirmations(); this.errorMessage = builder.getErrorMessage(); } - public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, boolean wasMyOffer) { + public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, + boolean wasMyOffer, + int numConfirmations) { var protocolModel = trade.getBsqSwapProtocolModel(); var swapPeer = protocolModel.getTradePeer(); var makerBsqAddress = wasMyOffer ? protocolModel.getBsqAddress() : swapPeer.getBsqAddress(); @@ -73,6 +77,7 @@ public static BsqSwapTradeInfo toBsqSwapTradeInfo(BsqSwapTrade trade, boolean wa .withMakerBtcAddress(makerBtcAddress) .withTakerBsqAddress(takerBsqAddress) .withTakerBtcAddress(takerBtcAddress) + .withNumConfirmations(numConfirmations) .withErrorMessage(trade.getErrorMessage()) .build(); } @@ -94,7 +99,8 @@ public bisq.proto.grpc.BsqSwapTradeInfo toProtoMessage() { .setTakerBsqAddress(takerBsqAddress != null ? takerBsqAddress : "") .setMakerBtcAddress(makerBtcAddress != null ? makerBtcAddress : "") .setTakerBtcAddress(takerBtcAddress != null ? takerBtcAddress : "") - .setErrorMessage(errorMessage != null ? errorMessage : "") + .setTakerBtcAddress(takerBtcAddress != null ? takerBtcAddress : "") + .setNumConfirmations(numConfirmations) .build(); } @@ -110,6 +116,7 @@ public static BsqSwapTradeInfo fromProto(bisq.proto.grpc.BsqSwapTradeInfo proto) .withMakerBtcAddress(proto.getMakerBtcAddress()) .withTakerBsqAddress(proto.getTakerBsqAddress()) .withTakerBtcAddress(proto.getTakerBtcAddress()) + .withNumConfirmations(proto.getNumConfirmations()) .withErrorMessage(proto.getErrorMessage()) .build(); } @@ -127,6 +134,7 @@ public String toString() { ", makerBtcAddress='" + makerBtcAddress + '\'' + ", takerBsqAddress='" + takerBsqAddress + '\'' + ", takerBtcAddress='" + takerBtcAddress + '\'' + + ", numConfirmations='" + numConfirmations + '\'' + ", errorMessage='" + errorMessage + '\'' + '}'; } diff --git a/core/src/main/java/bisq/core/api/model/OfferInfo.java b/core/src/main/java/bisq/core/api/model/OfferInfo.java index 69d0455de08..6cf635cb3b2 100644 --- a/core/src/main/java/bisq/core/api/model/OfferInfo.java +++ b/core/src/main/java/bisq/core/api/model/OfferInfo.java @@ -24,8 +24,6 @@ import bisq.common.Payload; -import java.util.Objects; - import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; @@ -141,13 +139,13 @@ private static OfferInfoBuilder getBuilder(Offer offer, boolean isMyOffer) { return new OfferInfoBuilder() .withId(offer.getId()) .withDirection(offer.getDirection().name()) - .withPrice(Objects.requireNonNull(offer.getPrice()).getValue()) + .withPrice(requireNonNull(offer.getPrice()).getValue()) .withUseMarketBasedPrice(offer.isUseMarketBasedPrice()) .withMarketPriceMargin(offer.getMarketPriceMargin()) .withAmount(offer.getAmount().value) .withMinAmount(offer.getMinAmount().value) - .withVolume(Objects.requireNonNull(offer.getVolume()).getValue()) - .withMinVolume(Objects.requireNonNull(offer.getMinVolume()).getValue()) + .withVolume(requireNonNull(offer.getVolume()).getValue()) + .withMinVolume(requireNonNull(offer.getMinVolume()).getValue()) .withMakerFee(getMakerFee(offer, isMyOffer)) .withTxFee(offer.getTxFee().value) .withOfferFeePaymentTxId(offer.getOfferFeePaymentTxId()) 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 70b929839a4..b8eb474e6ed 100644 --- a/core/src/main/java/bisq/core/api/model/TradeInfo.java +++ b/core/src/main/java/bisq/core/api/model/TradeInfo.java @@ -104,7 +104,7 @@ public TradeInfo(TradeInfoV1Builder builder) { public static TradeInfo toNewTradeInfo(BsqSwapTrade trade, String role) { // Always called by the taker, isMyOffer=false. - return toTradeInfo(trade, role, false); + return toTradeInfo(trade, role, false, 0); } public static TradeInfo toNewTradeInfo(Trade trade) { @@ -116,12 +116,15 @@ public static TradeInfo toTradeInfo(TradeModel tradeModel, String role, boolean if (tradeModel instanceof Trade) return toTradeInfo((Trade) tradeModel, role, isMyOffer); else if (tradeModel instanceof BsqSwapTrade) - return toTradeInfo((BsqSwapTrade) tradeModel, role, isMyOffer); + return toTradeInfo(tradeModel, role, isMyOffer); else throw new IllegalStateException("unsupported trade type: " + tradeModel.getClass().getSimpleName()); } - public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, String role, boolean isMyOffer) { + public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, + String role, + boolean isMyOffer, + int numConfirmations) { OfferInfo offerInfo = isMyOffer ? toMyOfferInfo(bsqSwapTrade.getOffer()) : toOfferInfo(bsqSwapTrade.getOffer()); TradeInfo tradeInfo = new TradeInfoV1Builder() .withOffer(offerInfo) @@ -143,7 +146,7 @@ public static TradeInfo toTradeInfo(BsqSwapTrade bsqSwapTrade, String role, bool // N/A: .withIsFiatSent(false), .withIsFiatReceived(false), .withIsPayoutPublished(false) // N/A: .withIsWithdrawn(false), .withContractAsJson(""), .withContract(null) .build(); - tradeInfo.bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, isMyOffer); + tradeInfo.bsqSwapTradeInfo = toBsqSwapTradeInfo(bsqSwapTrade, isMyOffer, numConfirmations); return tradeInfo; } 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 dcbaaa4d401..3ae01d4ea8c 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 @@ -43,6 +43,7 @@ public final class BsqSwapTradeInfoBuilder { private String makerBtcAddress; private String takerBsqAddress; private String takerBtcAddress; + private long numConfirmations; private String errorMessage; public BsqSwapTradeInfoBuilder withTxId(String txId) { @@ -95,6 +96,11 @@ public BsqSwapTradeInfoBuilder withTakerBtcAddress(String takerBtcAddress) { return this; } + public BsqSwapTradeInfoBuilder withNumConfirmations(long numConfirmations) { + this.numConfirmations = numConfirmations; + return this; + } + public BsqSwapTradeInfoBuilder withErrorMessage(String errorMessage) { this.errorMessage = errorMessage; return this; diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 641d9ab2eb0..d9619dc51c6 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -78,7 +78,11 @@ public void getBsqSwapTrade(GetTradeRequest req, var bsqSwapTrade = coreApi.getBsqSwapTrade(req.getTradeId()); boolean wasMyOffer = coreApi.isMyOffer(bsqSwapTrade.getOffer().getId()); String role = coreApi.getBsqSwapTradeRole(req.getTradeId()); - var tradeInfo = toTradeInfo(bsqSwapTrade, role, wasMyOffer); + var numConfirmations = coreApi.getTransactionConfirmations(bsqSwapTrade.getTxId()); + var tradeInfo = toTradeInfo(bsqSwapTrade, + role, + wasMyOffer, + numConfirmations); var reply = GetBsqSwapTradeReply.newBuilder() .setBsqSwapTrade(tradeInfo.toProtoMessage()) .build(); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index 5a5d81c88fe..bf26b3428d2 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -524,7 +524,8 @@ message BsqSwapTradeInfo { string makerBtcAddress = 8; string takerBsqAddress = 9; string takerBtcAddress = 10; - string errorMessage = 11; + uint64 numConfirmations = 11; + string errorMessage = 12; } message PaymentAccountPayloadInfo { From d8f1e446eac1cb183ad81503d27d46808698331f Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 14 Nov 2021 14:30:41 -0300 Subject: [PATCH 12/39] Try to hide from pesky codacy --- cli/src/test/java/bisq/cli/GetOffersSmokeTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java b/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java index aa871c3e863..6bbec525125 100644 --- a/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java +++ b/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java @@ -11,6 +11,7 @@ This can be run on mainnet. */ +@SuppressWarnings({"CommentedOutCode", "unused"}) public class GetOffersSmokeTest { public static void main(String[] args) { From 17277c4c67fa89de517c0fb840518d73f7b75d60 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Wed, 24 Nov 2021 12:52:58 -0300 Subject: [PATCH 13/39] Remove Get/Take BSQSwap Offer/Trade rpc service defs The old GetTrade and TakeOffer rpc service defs will be used for getting BSQ swap trades, and taking BSQ swap offers. --- proto/src/main/proto/grpc.proto | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index bf26b3428d2..d8c739bd369 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -383,12 +383,8 @@ message StopReply { /////////////////////////////////////////////////////////////////////////////////////////// service Trades { - rpc GetBsqSwapTrade (GetTradeRequest) returns (GetBsqSwapTradeReply) { - } rpc GetTrade (GetTradeRequest) returns (GetTradeReply) { } - rpc TakeBsqSwapOffer (TakeBsqSwapOfferRequest) returns (TakeBsqSwapOfferReply) { - } rpc TakeOffer (TakeOfferRequest) returns (TakeOfferReply) { } rpc ConfirmPaymentStarted (ConfirmPaymentStartedRequest) returns (ConfirmPaymentStartedReply) { @@ -401,17 +397,6 @@ service Trades { } } -message TakeBsqSwapOfferRequest { - string offerId = 1; - string paymentAccountId = 2; - string takerFeeCurrencyCode = 3; -} - -message TakeBsqSwapOfferReply { - TradeInfo bsqSwapTrade = 1; - AvailabilityResultWithDescription failureReason = 2; -} - message TakeOfferRequest { string offerId = 1; string paymentAccountId = 2; @@ -437,10 +422,6 @@ message ConfirmPaymentReceivedRequest { message ConfirmPaymentReceivedReply { } -message GetBsqSwapTradeReply { - TradeInfo bsqSwapTrade = 1; -} - message GetTradeRequest { string tradeId = 1; } From f88c1513ec3e2a7a1bba7a9aaa93f5dfafec3e3b Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Wed, 24 Nov 2021 13:00:30 -0300 Subject: [PATCH 14/39] Refactor GrpcTradesService: use GetTrade & TakeOffer services for BSQ swaps The rpc GetBsqSwapTrade and TakeBsqSwapOffer services were a quick hack to help test BSQ swap feature development more quickly, using apitest cases. Their gRPC service method implementations are removed here and the GetTrade & TakeOffer service methods are refactored to support BSQ swaps. --- .../bisq/daemon/grpc/GrpcTradesService.java | 162 +++++++++--------- 1 file changed, 81 insertions(+), 81 deletions(-) diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index d9619dc51c6..22961043b12 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -19,19 +19,18 @@ import bisq.core.api.CoreApi; import bisq.core.api.model.TradeInfo; +import bisq.core.trade.model.TradeModel; import bisq.core.trade.model.bisq_v1.Trade; +import bisq.core.trade.model.bsq_swap.BsqSwapTrade; import bisq.proto.grpc.ConfirmPaymentReceivedReply; import bisq.proto.grpc.ConfirmPaymentReceivedRequest; import bisq.proto.grpc.ConfirmPaymentStartedReply; import bisq.proto.grpc.ConfirmPaymentStartedRequest; -import bisq.proto.grpc.GetBsqSwapTradeReply; import bisq.proto.grpc.GetTradeReply; import bisq.proto.grpc.GetTradeRequest; import bisq.proto.grpc.KeepFundsReply; import bisq.proto.grpc.KeepFundsRequest; -import bisq.proto.grpc.TakeBsqSwapOfferReply; -import bisq.proto.grpc.TakeBsqSwapOfferRequest; import bisq.proto.grpc.TakeOfferReply; import bisq.proto.grpc.TakeOfferRequest; import bisq.proto.grpc.WithdrawFundsReply; @@ -72,67 +71,49 @@ public GrpcTradesService(CoreApi coreApi, GrpcExceptionHandler exceptionHandler) } @Override - public void getBsqSwapTrade(GetTradeRequest req, - StreamObserver responseObserver) { - try { - var bsqSwapTrade = coreApi.getBsqSwapTrade(req.getTradeId()); - boolean wasMyOffer = coreApi.isMyOffer(bsqSwapTrade.getOffer().getId()); - String role = coreApi.getBsqSwapTradeRole(req.getTradeId()); - var numConfirmations = coreApi.getTransactionConfirmations(bsqSwapTrade.getTxId()); - var tradeInfo = toTradeInfo(bsqSwapTrade, - role, - wasMyOffer, - numConfirmations); - var reply = GetBsqSwapTradeReply.newBuilder() - .setBsqSwapTrade(tradeInfo.toProtoMessage()) - .build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - } catch (IllegalArgumentException cause) { - // Offer makers may call 'gettrade' many times before a trade exists. - // Log a 'trade not found' warning instead of a full stack trace. - exceptionHandler.handleExceptionAsWarning(log, "getBsqSwapTrade", cause, responseObserver); - } catch (Throwable cause) { - exceptionHandler.handleException(log, cause, responseObserver); - } - } - - @Override - public void takeBsqSwapOffer(TakeBsqSwapOfferRequest req, - StreamObserver responseObserver) { + public void takeOffer(TakeOfferRequest req, + StreamObserver responseObserver) { GrpcErrorMessageHandler errorMessageHandler = - new GrpcErrorMessageHandler(getTakeBsqSwapOfferMethod().getFullMethodName(), + new GrpcErrorMessageHandler(getTakeOfferMethod().getFullMethodName(), responseObserver, exceptionHandler, log); - coreApi.takeBsqSwapOffer(req.getOfferId(), - req.getPaymentAccountId(), - req.getTakerFeeCurrencyCode(), - bsqSwapTrade -> { - String role = coreApi.getBsqSwapTradeRole(bsqSwapTrade); - var tradeInfo = toNewTradeInfo(bsqSwapTrade, role); - var reply = TakeBsqSwapOfferReply.newBuilder() - .setBsqSwapTrade(tradeInfo.toProtoMessage()) - .build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - }, - errorMessage -> { - if (!errorMessageHandler.isErrorHandled()) - errorMessageHandler.handleErrorMessage(errorMessage); - }); + + if (coreApi.isAvailableBsqSwapOffer(req.getOfferId())) { + coreApi.takeBsqSwapOffer(req.getOfferId(), + bsqSwapTrade -> { + var reply = buildTakeOfferReply(bsqSwapTrade); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + }, + errorMessage -> { + if (!errorMessageHandler.isErrorHandled()) + errorMessageHandler.handleErrorMessage(errorMessage); + }); + } else { + coreApi.takeOffer(req.getOfferId(), + req.getPaymentAccountId(), + req.getTakerFeeCurrencyCode(), + trade -> { + var reply = buildTakeOfferReply(trade); + responseObserver.onNext(reply); + responseObserver.onCompleted(); + }, + errorMessage -> { + if (!errorMessageHandler.isErrorHandled()) + errorMessageHandler.handleErrorMessage(errorMessage); + }); + } } @Override public void getTrade(GetTradeRequest req, StreamObserver responseObserver) { try { - Trade trade = coreApi.getTrade(req.getTradeId()); - boolean wasMyOffer = coreApi.isMyOffer(trade.getOffer().getId()); - String role = coreApi.getTradeRole(req.getTradeId()); - var reply = GetTradeReply.newBuilder() - .setTrade(toTradeInfo(trade, role, wasMyOffer).toProtoMessage()) - .build(); + var tradeModel = coreApi.getTradeModel(req.getTradeId()); + var reply = tradeModel.getOffer().isBsqSwapOffer() + ? buildGetTradeReply((BsqSwapTrade) tradeModel) + : buildGetTradeReply((Trade) tradeModel); responseObserver.onNext(reply); responseObserver.onCompleted(); } catch (IllegalArgumentException cause) { @@ -144,31 +125,6 @@ public void getTrade(GetTradeRequest req, } } - @Override - public void takeOffer(TakeOfferRequest req, - StreamObserver responseObserver) { - GrpcErrorMessageHandler errorMessageHandler = - new GrpcErrorMessageHandler(getTakeOfferMethod().getFullMethodName(), - responseObserver, - exceptionHandler, - log); - coreApi.takeOffer(req.getOfferId(), - req.getPaymentAccountId(), - req.getTakerFeeCurrencyCode(), - trade -> { - TradeInfo tradeInfo = toNewTradeInfo(trade); - var reply = TakeOfferReply.newBuilder() - .setTrade(tradeInfo.toProtoMessage()) - .build(); - responseObserver.onNext(reply); - responseObserver.onCompleted(); - }, - errorMessage -> { - if (!errorMessageHandler.isErrorHandled()) - errorMessageHandler.handleErrorMessage(errorMessage); - }); - } - @Override public void confirmPaymentStarted(ConfirmPaymentStartedRequest req, StreamObserver responseObserver) { @@ -232,9 +188,8 @@ final Optional rateMeteringInterceptor() { .or(() -> Optional.of(CallRateMeteringInterceptor.valueOf( new HashMap<>() {{ put(getGetTradeMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); - put(getGetBsqSwapTradeMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getTakeOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); - put(getTakeBsqSwapOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); + // put(getTakeBsqSwapOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getConfirmPaymentStartedMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getConfirmPaymentReceivedMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); put(getKeepFundsMethod().getFullMethodName(), new GrpcCallRateMeter(1, MINUTES)); @@ -242,4 +197,49 @@ final Optional rateMeteringInterceptor() { }} ))); } + + private TakeOfferReply buildTakeOfferReply(TradeModel tradeModel) { + TradeInfo tradeInfo; + if (tradeModel.getOffer().isBsqSwapOffer()) { + BsqSwapTrade bsqSwapTrade = (BsqSwapTrade) tradeModel; + String role = getMyRole(bsqSwapTrade); + tradeInfo = toNewTradeInfo(bsqSwapTrade, role); + } else { + tradeInfo = toNewTradeInfo((Trade) tradeModel); + } + return TakeOfferReply.newBuilder() + .setTrade(tradeInfo.toProtoMessage()) + .build(); + } + + private GetTradeReply buildGetTradeReply(BsqSwapTrade bsqSwapTrade) { + boolean wasMyOffer = wasMyOffer(bsqSwapTrade); + String role = getMyRole(bsqSwapTrade); + var numConfirmations = coreApi.getTransactionConfirmations(bsqSwapTrade.getTxId()); + var tradeInfo = toTradeInfo(bsqSwapTrade, + role, + wasMyOffer, + numConfirmations); + return GetTradeReply.newBuilder() + .setTrade(tradeInfo.toProtoMessage()) + .build(); + } + + private GetTradeReply buildGetTradeReply(Trade trade) { + boolean wasMyOffer = wasMyOffer(trade); + String role = getMyRole(trade); + return GetTradeReply.newBuilder() + .setTrade(toTradeInfo(trade, role, wasMyOffer).toProtoMessage()) + .build(); + } + + private boolean wasMyOffer(TradeModel tradeModel) { + return coreApi.isMyOffer(tradeModel.getOffer().getId()); + } + + private String getMyRole(TradeModel tradeModel) { + return tradeModel.getOffer().isBsqSwapOffer() + ? coreApi.getBsqSwapTradeRole((BsqSwapTrade) tradeModel) + : coreApi.getTradeRole(tradeModel.getId()); + } } From 3aae05281306c7f1c4f588e50ac2ec49ef344482 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Wed, 24 Nov 2021 13:05:09 -0300 Subject: [PATCH 15/39] Refactor core.api trade service implemenentations for BSQ swaps A minor refactoring to support serving TradeModel instances to CLI. For example, the CLI 'gettrade' command must return a v1 Trade or BSQ swap trade. --- core/src/main/java/bisq/core/api/CoreApi.java | 17 +++----------- .../java/bisq/core/api/CoreTradesService.java | 23 +++++++++++-------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 75e082d18c3..261993f08de 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.TradeModel; import bisq.core.trade.model.bisq_v1.Trade; import bisq.core.trade.model.bsq_swap.BsqSwapTrade; import bisq.core.trade.statistics.TradeStatistics3; @@ -276,14 +277,10 @@ public void getMarketPrice(String currencyCode, Consumer resultHandler) /////////////////////////////////////////////////////////////////////////////////////////// public void takeBsqSwapOffer(String offerId, - String paymentAccountId, - String takerFeeCurrencyCode, TradeResultHandler tradeResultHandler, ErrorMessageHandler errorMessageHandler) { Offer bsqSwapOffer = coreOffersService.getBsqSwapOffer(offerId); coreTradesService.takeBsqSwapOffer(bsqSwapOffer, - paymentAccountId, - takerFeeCurrencyCode, tradeResultHandler, errorMessageHandler); } @@ -317,22 +314,14 @@ public void withdrawFunds(String tradeId, String address, String memo) { coreTradesService.withdrawFunds(tradeId, address, memo); } - public BsqSwapTrade getBsqSwapTrade(String tradeId) { - return coreTradesService.getBsqSwapTrade(tradeId); - } - - public Trade getTrade(String tradeId) { - return coreTradesService.getTrade(tradeId); + public TradeModel getTradeModel(String tradeId) { + return coreTradesService.getTradeModel(tradeId); } public String getTradeRole(String tradeId) { return coreTradesService.getTradeRole(tradeId); } - public String getBsqSwapTradeRole(String tradeId) { - return coreTradesService.getBsqSwapTradeRole(tradeId); - } - public String getBsqSwapTradeRole(BsqSwapTrade bsqSwapTrade) { return coreTradesService.getBsqSwapTradeRole(bsqSwapTrade); } diff --git a/core/src/main/java/bisq/core/api/CoreTradesService.java b/core/src/main/java/bisq/core/api/CoreTradesService.java index 6fdb23434db..3184ff31477 100644 --- a/core/src/main/java/bisq/core/api/CoreTradesService.java +++ b/core/src/main/java/bisq/core/api/CoreTradesService.java @@ -28,6 +28,7 @@ import bisq.core.trade.bisq_v1.TradeResultHandler; import bisq.core.trade.bisq_v1.TradeUtil; 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; import bisq.core.trade.protocol.bisq_v1.BuyerProtocol; @@ -91,18 +92,14 @@ public CoreTradesService(CoreContext coreContext, this.user = user; } - // todo we need to pass the intended trade amount + // TODO We need to pass the intended trade amount, not default to the maximum. void takeBsqSwapOffer(Offer offer, - String paymentAccountId, - String takerFeeCurrencyCode, TradeResultHandler tradeResultHandler, ErrorMessageHandler errorMessageHandler) { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); bsqSwapTakeOfferModel.initWithData(offer); - - //todo use the intended trade amount bsqSwapTakeOfferModel.applyAmount(offer.getAmount()); log.info("Initiating take {} offer, {}", @@ -114,6 +111,7 @@ void takeBsqSwapOffer(Offer offer, coreContext.isApiUser()); } + // TODO We need to pass the intended trade amount, not default to the maximum. void takeOffer(Offer offer, String paymentAccountId, String takerFeeCurrencyCode, @@ -234,17 +232,22 @@ void withdrawFunds(String tradeId, String toAddress, String memo) { }); } - BsqSwapTrade getBsqSwapTrade(String tradeId) { + TradeModel getTradeModel(String tradeId) { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); + + Optional openTrade = getOpenTrade(tradeId); + if (openTrade.isPresent()) + return openTrade.get(); + + Optional closedTrade = getClosedTrade(tradeId); + if (closedTrade.isPresent()) + return closedTrade.get(); + return tradeManager.findBsqSwapTradeById(tradeId).orElseThrow(() -> new IllegalArgumentException(format("trade with id '%s' not found", tradeId))); } - String getBsqSwapTradeRole(String tradeId) { - return getBsqSwapTradeRole(getBsqSwapTrade(tradeId)); - } - String getBsqSwapTradeRole(BsqSwapTrade bsqSwapTrade) { coreWalletsService.verifyWalletsAreAvailable(); coreWalletsService.verifyEncryptedWalletIsUnlocked(); From 0c5c343fe09f532c22d7e06f2e689574f47f8bee Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Wed, 24 Nov 2021 13:10:34 -0300 Subject: [PATCH 16/39] Adjust CLI to support getting/taking BSQ swaps Also refactored some of the opt parsers. --- cli/src/main/java/bisq/cli/CliMain.java | 32 +++++++++++++------ cli/src/main/java/bisq/cli/GrpcClient.java | 21 ++---------- .../cli/opts/CancelOfferOptionParser.java | 16 ++-------- .../cli/opts/CreateOfferOptionParser.java | 2 +- .../opts/CreatePaymentAcctOptionParser.java | 2 +- .../bisq/cli/opts/EditOfferOptionParser.java | 27 ++++++---------- .../opts/GetPaymentAcctFormOptionParser.java | 2 +- ...onParser.java => OfferIdOptionParser.java} | 12 ++++--- .../cli/opts/SetTxFeeRateOptionParser.java | 2 +- .../bisq/cli/opts/TakeOfferOptionParser.java | 17 ++-------- .../cli/request/TradesServiceRequest.java | 28 +++++----------- 11 files changed, 57 insertions(+), 104 deletions(-) rename cli/src/main/java/bisq/cli/opts/{GetOfferOptionParser.java => OfferIdOptionParser.java} (80%) diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index bc84aed69f4..eef8943723a 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -18,6 +18,7 @@ package bisq.cli; import bisq.proto.grpc.OfferInfo; +import bisq.proto.grpc.TradeInfo; import io.grpc.StatusRuntimeException; @@ -45,6 +46,7 @@ import static bisq.cli.Method.*; import static bisq.cli.opts.OptLabel.*; import static bisq.cli.table.builder.TableType.*; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory.BSQ_SWAP; import static java.lang.String.format; import static java.lang.System.err; import static java.lang.System.exit; @@ -62,11 +64,11 @@ import bisq.cli.opts.GetAddressBalanceOptionParser; import bisq.cli.opts.GetBTCMarketPriceOptionParser; import bisq.cli.opts.GetBalanceOptionParser; -import bisq.cli.opts.GetOfferOptionParser; import bisq.cli.opts.GetOffersOptionParser; import bisq.cli.opts.GetPaymentAcctFormOptionParser; import bisq.cli.opts.GetTradeOptionParser; import bisq.cli.opts.GetTransactionOptionParser; +import bisq.cli.opts.OfferIdOptionParser; import bisq.cli.opts.RegisterDisputeAgentOptionParser; import bisq.cli.opts.RemoveWalletPasswordOptionParser; import bisq.cli.opts.SendBsqOptionParser; @@ -392,7 +394,7 @@ public static void run(String[] args) { return; } case getoffer: { - var opts = new GetOfferOptionParser(args).parse(); + var opts = new OfferIdOptionParser(args).parse(); if (opts.isForHelp()) { out.println(client.getMethodHelp(method)); return; @@ -403,7 +405,7 @@ public static void run(String[] args) { return; } case getmyoffer: { - var opts = new GetOfferOptionParser(args).parse(); + var opts = new OfferIdOptionParser(args).parse(); if (opts.isForHelp()) { out.println(client.getMethodHelp(method)); return; @@ -446,15 +448,25 @@ public static void run(String[] args) { return; } case takeoffer: { - var opts = new TakeOfferOptionParser(args).parse(); - if (opts.isForHelp()) { + var offerIdOpt = new OfferIdOptionParser(args).parse(); + if (offerIdOpt.isForHelp()) { out.println(client.getMethodHelp(method)); return; } - var offerId = opts.getOfferId(); - var paymentAccountId = opts.getPaymentAccountId(); - var takerFeeCurrencyCode = opts.getTakerFeeCurrencyCode(); - var trade = client.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode); + var offerId = offerIdOpt.getOfferId(); + TradeInfo trade; + // We only send an 'offer-id' param when taking a BsqSwapOffer. + // Find out what kind of offer is being taken before sending a + // 'takeoffer' request. + var offerCategory = client.getAvailableOfferCategory(offerId); + if (offerCategory.equals(BSQ_SWAP)) { + trade = client.takeBsqSwapOffer(offerId); + } else { + var opts = new TakeOfferOptionParser(args).parse(); + var paymentAccountId = opts.getPaymentAccountId(); + var takerFeeCurrencyCode = opts.getTakerFeeCurrencyCode(); + trade = client.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode); + } out.printf("trade %s successfully taken%n", trade.getTradeId()); return; } @@ -825,7 +837,7 @@ private static void printHelp(OptionParser parser, @SuppressWarnings("SameParame stream.format(rowFormat, "", "--currency-code=", ""); stream.println(); stream.format(rowFormat, takeoffer.name(), "--offer-id= \\", "Take offer with id"); - stream.format(rowFormat, "", "--payment-account=", ""); + stream.format(rowFormat, "", "[--payment-account=]", ""); stream.format(rowFormat, "", "[--fee-currency=]", ""); stream.println(); stream.format(rowFormat, gettrade.name(), "--trade-id= \\", "Get trade summary or full contract"); diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java index 87a8aa01229..a1e88898f5e 100644 --- a/cli/src/main/java/bisq/cli/GrpcClient.java +++ b/cli/src/main/java/bisq/cli/GrpcClient.java @@ -27,7 +27,6 @@ import bisq.proto.grpc.OfferInfo; import bisq.proto.grpc.RegisterDisputeAgentRequest; import bisq.proto.grpc.StopRequest; -import bisq.proto.grpc.TakeBsqSwapOfferReply; import bisq.proto.grpc.TakeOfferReply; import bisq.proto.grpc.TradeInfo; import bisq.proto.grpc.TxFeeRateInfo; @@ -349,34 +348,18 @@ public List sortOffersByDate(List offers) { return offersServiceRequest.sortOffersByDate(offers); } - public TakeBsqSwapOfferReply getTakeBsqSwapOfferReply(String offerId, - String paymentAccountId, - String takerFeeCurrencyCode) { - return tradesServiceRequest.getTakeBsqSwapOfferReply(offerId, - paymentAccountId, - takerFeeCurrencyCode); - } - public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { return tradesServiceRequest.getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode); } - public TradeInfo takeBsqSwapOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { - var reply = getTakeBsqSwapOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode); - if (reply.hasBsqSwapTrade()) - return reply.getBsqSwapTrade(); - else - throw new IllegalStateException(reply.getFailureReason().getDescription()); + public TradeInfo takeBsqSwapOffer(String offerId) { + return tradesServiceRequest.takeBsqSwapOffer(offerId); } public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { return tradesServiceRequest.takeOffer(offerId, paymentAccountId, takerFeeCurrencyCode); } - public TradeInfo getBsqSwapTrade(String tradeId) { - return tradesServiceRequest.getBsqSwapTrade(tradeId); - } - public TradeInfo getTrade(String tradeId) { return tradesServiceRequest.getTrade(tradeId); } diff --git a/cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java index c35ffc7bfb4..a18edb84c60 100644 --- a/cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CancelOfferOptionParser.java @@ -18,14 +18,7 @@ package bisq.cli.opts; -import joptsimple.OptionSpec; - -import static bisq.cli.opts.OptLabel.OPT_OFFER_ID; - -public class CancelOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to cancel") - .withRequiredArg(); +public class CancelOfferOptionParser extends OfferIdOptionParser implements MethodOpts { public CancelOfferOptionParser(String[] args) { super(args); @@ -34,12 +27,7 @@ public CancelOfferOptionParser(String[] args) { public CancelOfferOptionParser parse() { super.parse(); - // Short circuit opt validation if user just wants help. - if (options.has(helpOpt)) - return this; - - if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty()) - throw new IllegalArgumentException("no offer id specified"); + // Super class will short-circuit parsing if help option is present. return this; } diff --git a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java index 42cf8ad1550..df85c69812a 100644 --- a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java @@ -28,7 +28,7 @@ public class CreateOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT, - "id of payment account used for offer") + "id of payment account used for offer") .withRequiredArg() .defaultsTo(EMPTY); diff --git a/cli/src/main/java/bisq/cli/opts/CreatePaymentAcctOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreatePaymentAcctOptionParser.java index fe91924bb41..c4b52229664 100644 --- a/cli/src/main/java/bisq/cli/opts/CreatePaymentAcctOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CreatePaymentAcctOptionParser.java @@ -29,7 +29,7 @@ public class CreatePaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts { final OptionSpec paymentAcctFormPathOpt = parser.accepts(OPT_PAYMENT_ACCOUNT_FORM, - "path to json payment account form") + "path to json payment account form") .withRequiredArg(); public CreatePaymentAcctOptionParser(String[] args) { diff --git a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java index 288f1a9f40d..35001553006 100644 --- a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java @@ -24,7 +24,10 @@ import java.math.BigDecimal; -import static bisq.cli.opts.OptLabel.*; +import static bisq.cli.opts.OptLabel.OPT_ENABLE; +import static bisq.cli.opts.OptLabel.OPT_FIXED_PRICE; +import static bisq.cli.opts.OptLabel.OPT_MKT_PRICE_MARGIN; +import static bisq.cli.opts.OptLabel.OPT_TRIGGER_PRICE; import static bisq.proto.grpc.EditOfferRequest.EditType.*; import static java.lang.String.format; @@ -32,26 +35,23 @@ import org.checkerframework.checker.nullness.qual.Nullable; -public class EditOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { +public class EditOfferOptionParser extends OfferIdOptionParser implements MethodOpts { static int OPT_ENABLE_ON = 1; static int OPT_ENABLE_OFF = 0; static int OPT_ENABLE_IGNORED = -1; - final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to cancel") - .withRequiredArg(); - final OptionSpec fixedPriceOpt = parser.accepts(OPT_FIXED_PRICE, "fixed btc price") .withOptionalArg() .defaultsTo("0"); final OptionSpec mktPriceMarginOpt = parser.accepts(OPT_MKT_PRICE_MARGIN, - "market btc price margin (%)") + "market btc price margin (%)") .withOptionalArg() .defaultsTo("0.00"); final OptionSpec triggerPriceOpt = parser.accepts(OPT_TRIGGER_PRICE, - "trigger price (applies to mkt price margin based offers)") + "trigger price (applies to mkt price margin based offers)") .withOptionalArg() .defaultsTo("0"); @@ -59,7 +59,7 @@ public class EditOfferOptionParser extends AbstractMethodOptionParser implements // activation state). For this reason, a boolean type is not used (can only be // true or false). final OptionSpec enableOpt = parser.accepts(OPT_ENABLE, - "enable or disable offer") + "enable or disable offer") .withOptionalArg() .ofType(String.class); @@ -72,12 +72,7 @@ public EditOfferOptionParser(String[] args) { public EditOfferOptionParser parse() { super.parse(); - // Short circuit opt validation if user just wants help. - if (options.has(helpOpt)) - return this; - - if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty()) - throw new IllegalArgumentException("no offer id specified"); + // Super class will short-circuit parsing if help option is present. boolean hasNoEditDetails = !options.has(fixedPriceOpt) && !options.has(mktPriceMarginOpt) @@ -201,10 +196,6 @@ public EditOfferOptionParser parse() { return this; } - public String getOfferId() { - return options.valueOf(offerIdOpt); - } - public String getFixedPrice() { if (offerEditType.equals(FIXED_PRICE_ONLY) || offerEditType.equals(FIXED_PRICE_AND_ACTIVATION_STATE)) { return options.has(fixedPriceOpt) ? options.valueOf(fixedPriceOpt) : "0"; diff --git a/cli/src/main/java/bisq/cli/opts/GetPaymentAcctFormOptionParser.java b/cli/src/main/java/bisq/cli/opts/GetPaymentAcctFormOptionParser.java index 508069c2f30..bec9044f264 100644 --- a/cli/src/main/java/bisq/cli/opts/GetPaymentAcctFormOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/GetPaymentAcctFormOptionParser.java @@ -25,7 +25,7 @@ public class GetPaymentAcctFormOptionParser extends AbstractMethodOptionParser implements MethodOpts { final OptionSpec paymentMethodIdOpt = parser.accepts(OPT_PAYMENT_METHOD_ID, - "id of payment method type used by a payment account") + "id of payment method type used by a payment account") .withRequiredArg(); public GetPaymentAcctFormOptionParser(String[] args) { diff --git a/cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/OfferIdOptionParser.java similarity index 80% rename from cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java rename to cli/src/main/java/bisq/cli/opts/OfferIdOptionParser.java index 1a849654cf7..60bee4960e1 100644 --- a/cli/src/main/java/bisq/cli/opts/GetOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/OfferIdOptionParser.java @@ -22,16 +22,20 @@ import static bisq.cli.opts.OptLabel.OPT_OFFER_ID; -public class GetOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { +/** + * Superclass for option parsers requiring an offer-id. Avoids a small amount of + * duplicated boilerplate. + */ +public class OfferIdOptionParser extends AbstractMethodOptionParser implements MethodOpts { - final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to get") + final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer") .withRequiredArg(); - public GetOfferOptionParser(String[] args) { + public OfferIdOptionParser(String[] args) { super(args); } - public GetOfferOptionParser parse() { + public OfferIdOptionParser parse() { super.parse(); // Short circuit opt validation if user just wants help. diff --git a/cli/src/main/java/bisq/cli/opts/SetTxFeeRateOptionParser.java b/cli/src/main/java/bisq/cli/opts/SetTxFeeRateOptionParser.java index f7ed113cb3c..be0132b5fee 100644 --- a/cli/src/main/java/bisq/cli/opts/SetTxFeeRateOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/SetTxFeeRateOptionParser.java @@ -25,7 +25,7 @@ public class SetTxFeeRateOptionParser extends AbstractMethodOptionParser implements MethodOpts { final OptionSpec feeRateOpt = parser.accepts(OPT_TX_FEE_RATE, - "tx fee rate preference (sats/byte)") + "tx fee rate preference (sats/byte)") .withRequiredArg(); public SetTxFeeRateOptionParser(String[] args) { diff --git a/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java index 67fbdd8c86d..9d424c23cec 100644 --- a/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java @@ -21,13 +21,9 @@ import joptsimple.OptionSpec; import static bisq.cli.opts.OptLabel.OPT_FEE_CURRENCY; -import static bisq.cli.opts.OptLabel.OPT_OFFER_ID; import static bisq.cli.opts.OptLabel.OPT_PAYMENT_ACCOUNT; -public class TakeOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { - - final OptionSpec offerIdOpt = parser.accepts(OPT_OFFER_ID, "id of offer to take") - .withRequiredArg(); +public class TakeOfferOptionParser extends OfferIdOptionParser implements MethodOpts { final OptionSpec paymentAccountIdOpt = parser.accepts(OPT_PAYMENT_ACCOUNT, "id of payment account used for trade") .withRequiredArg(); @@ -43,12 +39,7 @@ public TakeOfferOptionParser(String[] args) { public TakeOfferOptionParser parse() { super.parse(); - // Short circuit opt validation if user just wants help. - if (options.has(helpOpt)) - return this; - - if (!options.has(offerIdOpt) || options.valueOf(offerIdOpt).isEmpty()) - throw new IllegalArgumentException("no offer id specified"); + // Super class will short-circuit parsing if help option is present. if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) throw new IllegalArgumentException("no payment account id specified"); @@ -56,10 +47,6 @@ public TakeOfferOptionParser parse() { return this; } - public String getOfferId() { - return options.valueOf(offerIdOpt); - } - public String getPaymentAccountId() { return options.valueOf(paymentAccountIdOpt); } diff --git a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java index 3395c0e220f..cf95506615e 100644 --- a/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java +++ b/cli/src/main/java/bisq/cli/request/TradesServiceRequest.java @@ -21,8 +21,6 @@ import bisq.proto.grpc.ConfirmPaymentStartedRequest; import bisq.proto.grpc.GetTradeRequest; import bisq.proto.grpc.KeepFundsRequest; -import bisq.proto.grpc.TakeBsqSwapOfferReply; -import bisq.proto.grpc.TakeBsqSwapOfferRequest; import bisq.proto.grpc.TakeOfferReply; import bisq.proto.grpc.TakeOfferRequest; import bisq.proto.grpc.TradeInfo; @@ -40,17 +38,6 @@ public TradesServiceRequest(GrpcStubs grpcStubs) { this.grpcStubs = grpcStubs; } - public TakeBsqSwapOfferReply getTakeBsqSwapOfferReply(String offerId, - String paymentAccountId, - String takerFeeCurrencyCode) { - var request = TakeBsqSwapOfferRequest.newBuilder() - .setOfferId(offerId) - .setPaymentAccountId(paymentAccountId) - .setTakerFeeCurrencyCode(takerFeeCurrencyCode) - .build(); - return grpcStubs.tradesService.takeBsqSwapOffer(request); - } - public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { var request = TakeOfferRequest.newBuilder() .setOfferId(offerId) @@ -60,19 +47,20 @@ public TakeOfferReply getTakeOfferReply(String offerId, String paymentAccountId, return grpcStubs.tradesService.takeOffer(request); } - public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { - var reply = getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode); + public TradeInfo takeBsqSwapOffer(String offerId) { + var reply = getTakeOfferReply(offerId, "", ""); if (reply.hasTrade()) return reply.getTrade(); else throw new IllegalStateException(reply.getFailureReason().getDescription()); } - public TradeInfo getBsqSwapTrade(String tradeId) { - var request = GetTradeRequest.newBuilder() - .setTradeId(tradeId) - .build(); - return grpcStubs.tradesService.getBsqSwapTrade(request).getBsqSwapTrade(); + public TradeInfo takeOffer(String offerId, String paymentAccountId, String takerFeeCurrencyCode) { + var reply = getTakeOfferReply(offerId, paymentAccountId, takerFeeCurrencyCode); + if (reply.hasTrade()) + return reply.getTrade(); + else + throw new IllegalStateException(reply.getFailureReason().getDescription()); } public TradeInfo getTrade(String tradeId) { From 6403fc1f8133bee655013d19e4ba95085e83f267 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Wed, 24 Nov 2021 13:12:48 -0300 Subject: [PATCH 17/39] Adjust apitest cases to rpc BSQ wwap related changes --- .../method/offer/AbstractOfferTest.java | 9 +++++ .../method/trade/AbstractTradeTest.java | 9 +++++ .../method/trade/BsqSwapTradeTest.java | 36 ++++++++++++------- 3 files changed, 42 insertions(+), 12 deletions(-) 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 fb8c2d76330..218af0e15e1 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/AbstractOfferTest.java @@ -41,10 +41,12 @@ 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.System.out; import bisq.apitest.method.MethodTest; +import bisq.cli.CliMain; import bisq.cli.table.builder.TableBuilder; @Slf4j @@ -134,4 +136,11 @@ public static void createLegacyBsqPaymentAccounts() { public static void tearDown() { tearDownScaffold(); } + + protected static void runCliGetOffer(String offerId) { + out.println("Alice's CLI 'getmyoffer' response:"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "getmyoffer", "--offer-id=" + offerId}); + out.println("Bob's CLI 'getoffer' response:"); + CliMain.main(new String[]{"--password=xyz", "--port=9999", "getoffer", "--offer-id=" + offerId}); + } } 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 53b9f176f84..84f4f9bac21 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/AbstractTradeTest.java @@ -21,11 +21,13 @@ import static bisq.core.trade.model.bisq_v1.Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN; import static bisq.core.trade.model.bisq_v1.Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG; import static java.lang.String.format; +import static java.lang.System.out; import static org.junit.jupiter.api.Assertions.*; import bisq.apitest.method.offer.AbstractOfferTest; +import bisq.cli.CliMain; import bisq.cli.GrpcClient; import bisq.cli.table.builder.TableBuilder; @@ -243,4 +245,11 @@ protected final void logTrade(Logger log, new TableBuilder(TRADE_DETAIL_TBL, trade).build())); } } + + protected static void runCliGetTrade(String tradeId) { + out.println("Alice's CLI 'gettrade' response:"); + CliMain.main(new String[]{"--password=xyz", "--port=9998", "gettrade", "--trade-id=" + tradeId}); + out.println("Bob's CLI 'gettrade' response:"); + CliMain.main(new String[]{"--password=xyz", "--port=9999", "gettrade", "--trade-id=" + tradeId}); + } } 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 674109f569f..fc8cb607ea3 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; @@ -36,9 +35,11 @@ import static bisq.apitest.config.ApiTestConfig.BSQ; import static bisq.apitest.config.ApiTestConfig.BTC; +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; @@ -49,13 +50,11 @@ import bisq.apitest.method.offer.AbstractOfferTest; import bisq.cli.GrpcClient; -@Disabled +// @Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class BsqSwapTradeTest extends AbstractTradeTest { - private static final String BISQ_FEE_CURRENCY_CODE = BSQ; - // Long-running swap trade tests might want to check node logs for exceptions. @Setter private boolean checkForLoggedExceptions; @@ -67,7 +66,7 @@ public static void setUp() { @BeforeEach public void generateBtcBlock() { - genBtcBlocksThenWait(1, 2000); + genBtcBlocksThenWait(1, 2_000); } @Test @@ -102,23 +101,36 @@ public void testAliceCreateBsqSwapBuyOffer() { mySwapOffer = aliceClient.getMyOffer(newOfferId); log.debug("My fetched BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); assertNotEquals(0, mySwapOffer.getMakerFee()); + + runCliGetOffer(newOfferId); } @Test @Order(3) public void testBobTakesBsqSwapOffer() { var availableSwapOffer = getAvailableBsqSwapOffer(bobClient); - var swapTrade = bobClient.takeBsqSwapOffer(availableSwapOffer.getId(), - bobsBsqSwapAcct.getId(), - BISQ_FEE_CURRENCY_CODE); + + // 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(30_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, swapTrade.getTradeId()); + swapTrade = getBsqSwapTrade(bobClient, tradeId); log.debug("BsqSwap Trade at COMPLETION:\n{}", toTradeDetailTable.apply(swapTrade)); assertEquals(COMPLETED.name(), swapTrade.getState()); + + runCliGetTrade(tradeId); } @Test @@ -164,7 +176,7 @@ private OfferInfo getAvailableBsqSwapOffer(GrpcClient client) { } fail(format("Bob gave up on fetching available bsq swap offers after %d attempts.", numFetchAttempts)); } - sleep(1000); + sleep(1_000); } else { assertEquals(1, bsqSwapOffers.size()); log.debug("Bob found new available bsq swap offer on attempt # {}.", numFetchAttempts); @@ -181,7 +193,7 @@ private TradeInfo getBsqSwapTrade(GrpcClient client, String tradeId) { while (true) { try { numFetchAttempts++; - return client.getBsqSwapTrade(tradeId); + return client.getTrade(tradeId); } catch (Exception ex) { log.warn(ex.getMessage()); if (numFetchAttempts > 9) { @@ -190,7 +202,7 @@ private TradeInfo getBsqSwapTrade(GrpcClient client, String tradeId) { } fail(format("Could not find new bsq swap trade after %d attempts.", numFetchAttempts)); } else { - sleep(1000); + sleep(1_000); } } } From 3077be54f03ade0445a96923b7847aee701b126c Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 25 Nov 2021 16:12:05 -0300 Subject: [PATCH 18/39] Remove API createoffer's paymentAcctId request param (BSQ swap) --- .../java/bisq/apitest/method/offer/BsqSwapOfferTest.java | 6 ++---- .../java/bisq/apitest/method/trade/BsqSwapTradeTest.java | 3 +-- cli/src/main/java/bisq/cli/GrpcClient.java | 6 ++---- .../main/java/bisq/cli/request/OffersServiceRequest.java | 4 +--- proto/src/main/proto/grpc.proto | 1 - 5 files changed, 6 insertions(+), 14 deletions(-) diff --git a/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java index 2d65c55c35f..ce3e9e8f115 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java @@ -23,7 +23,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; @@ -37,7 +36,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static protobuf.OfferDirection.BUY; -@Disabled +// @Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class BsqSwapOfferTest extends AbstractOfferTest { @@ -112,8 +111,7 @@ private void createBsqSwapOffer() { var bsqSwapOffer = aliceClient.createBsqSwapOffer(BUY.name(), 1_000_000L, 1_000_000L, - "0.00005", - alicesBsqSwapAcct.getId()); + "0.00005"); log.debug("BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", bsqSwapOffer); var newOfferId = bsqSwapOffer.getId(); assertNotEquals("", newOfferId); 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 fc8cb607ea3..85f6f6d280d 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java @@ -84,8 +84,7 @@ public void testAliceCreateBsqSwapBuyOffer() { var mySwapOffer = aliceClient.createBsqSwapOffer(BUY.name(), 1_000_000L, // 0.01 BTC 1_000_000L, - "0.00005", - alicesBsqSwapAcct.getId()); + "0.00005"); log.debug("Pending BsqSwap Sell BSQ (Buy BTC) OFFER:\n{}", toOfferTable.apply(mySwapOffer)); var newOfferId = mySwapOffer.getId(); assertNotEquals("", newOfferId); diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java index a1e88898f5e..8b218d5d23a 100644 --- a/cli/src/main/java/bisq/cli/GrpcClient.java +++ b/cli/src/main/java/bisq/cli/GrpcClient.java @@ -145,13 +145,11 @@ public GetOfferCategoryReply.OfferCategory getAvailableOfferCategory(String offe public OfferInfo createBsqSwapOffer(String direction, long amount, long minAmount, - String fixedPrice, - String paymentAcctId) { + String fixedPrice) { return offersServiceRequest.createBsqSwapOffer(direction, amount, minAmount, - fixedPrice, - paymentAcctId); + fixedPrice); } public OfferInfo createFixedPricedOffer(String direction, diff --git a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java index 273d95073f4..7fd32e1a911 100644 --- a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java +++ b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java @@ -73,14 +73,12 @@ public GetOfferCategoryReply.OfferCategory getAvailableOfferCategory(String offe public OfferInfo createBsqSwapOffer(String direction, long amount, long minAmount, - String fixedPrice, - String paymentAcctId) { + String fixedPrice) { var request = CreateBsqSwapOfferRequest.newBuilder() .setDirection(direction) .setAmount(amount) .setMinAmount(minAmount) .setPrice(fixedPrice) - .setPaymentAccountId(paymentAcctId) .build(); return grpcStubs.offersService.createBsqSwapOffer(request).getBsqSwapOffer(); } diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index d8c739bd369..ad0ca7f8b6c 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -163,7 +163,6 @@ message CreateBsqSwapOfferRequest { uint64 amount = 2; uint64 minAmount = 3; string price = 4; - string paymentAccountId = 5; } message CreateBsqSwapOfferReply { From b1c872c515fbcf95bbc4eed34610981b04413a10 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 25 Nov 2021 17:08:07 -0300 Subject: [PATCH 19/39] Adjust createoffer opt parser/test for bsq swaps --- .../java/bisq/cli/opts/OptionParsersTest.java | 121 +++++++++++++++--- 1 file changed, 106 insertions(+), 15 deletions(-) diff --git a/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java b/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java index 58b8712fe90..0e58159b206 100644 --- a/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java +++ b/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java @@ -9,6 +9,7 @@ import static bisq.cli.opts.OptLabel.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; public class OptionParsersTest { @@ -62,13 +63,16 @@ public void testValidCancelOfferOpts() { new CancelOfferOptionParser(args).parse(); } - // createoffer opt parser tests + // createoffer (v1) opt parser tests @Test - public void testCreateOfferOptParserWithMissingPaymentAccountIdOptShouldThrowException() { + public void testCreateOfferWithMissingPaymentAccountIdOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, - createoffer.name() + createoffer.name(), + "--" + OPT_DIRECTION + "=" + "SELL", + "--" + OPT_CURRENCY_CODE + "=" + "JPY", + "--" + OPT_AMOUNT + "=" + "0.1" }; Throwable exception = assertThrows(RuntimeException.class, () -> new CreateOfferOptionParser(args).parse()); @@ -76,7 +80,7 @@ public void testCreateOfferOptParserWithMissingPaymentAccountIdOptShouldThrowExc } @Test - public void testCreateOfferOptParserWithEmptyPaymentAccountIdOptShouldThrowException() { + public void testCreateOfferWithEmptyPaymentAccountIdOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createoffer.name(), @@ -88,7 +92,7 @@ public void testCreateOfferOptParserWithEmptyPaymentAccountIdOptShouldThrowExcep } @Test - public void testCreateOfferOptParserWithMissingDirectionOptShouldThrowException() { + public void testCreateOfferWithMissingDirectionOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createoffer.name(), @@ -101,7 +105,7 @@ public void testCreateOfferOptParserWithMissingDirectionOptShouldThrowException( @Test - public void testCreateOfferOptParserWithMissingDirectionOptValueShouldThrowException() { + public void testCreateOfferWithMissingDirectionOptValueShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createoffer.name(), @@ -134,10 +138,97 @@ public void testValidCreateOfferOpts() { assertEquals("25.0", parser.getSecurityDeposit()); } + // createoffer (bsq swap) opt parser tests + + @Test + public void testCreateBsqSwapOfferWithPaymentAcctIdOptShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_SWAP + "=" + "true", + "--" + OPT_PAYMENT_ACCOUNT + "=" + "abc", + "--" + OPT_DIRECTION + "=" + "buy", + "--" + OPT_CURRENCY_CODE + "=" + "bsq", + "--" + OPT_AMOUNT + "=" + "0.125" + }; + Throwable exception = assertThrows(RuntimeException.class, () -> + new CreateOfferOptionParser(args).parse()); + assertEquals("cannot use a payment account id in bsq swap offer", exception.getMessage()); + } + + @Test + public void testCreateBsqSwapOfferWithMktMarginPriceOptShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_SWAP + "=" + "true", + "--" + OPT_DIRECTION + "=" + "buy", + "--" + OPT_CURRENCY_CODE + "=" + "bsq", + "--" + OPT_AMOUNT + "=" + "0.125", + "--" + OPT_MKT_PRICE_MARGIN + "=" + "0.0" + }; + Throwable exception = assertThrows(RuntimeException.class, () -> + new CreateOfferOptionParser(args).parse()); + assertEquals("cannot use a market price margin in bsq swap offer", exception.getMessage()); + } + + @Test + public void testCreateBsqSwapOfferWithSecurityDepositOptShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_SWAP + "=" + "true", + "--" + OPT_DIRECTION + "=" + "buy", + "--" + OPT_CURRENCY_CODE + "=" + "bsq", + "--" + OPT_AMOUNT + "=" + "0.125", + "--" + OPT_SECURITY_DEPOSIT + "=" + "25.0" + }; + Throwable exception = assertThrows(RuntimeException.class, () -> + new CreateOfferOptionParser(args).parse()); + assertEquals("cannot use a security deposit in bsq swap offer", exception.getMessage()); + } + + @Test + public void testCreateBsqSwapOfferWithMissingFixedPriceOptShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_SWAP + "=" + "true", + "--" + OPT_DIRECTION + "=" + "sell", + "--" + OPT_CURRENCY_CODE + "=" + "bsq", + "--" + OPT_MIN_AMOUNT + "=" + "0.075", + "--" + OPT_AMOUNT + "=" + "0.125" + }; + Throwable exception = assertThrows(RuntimeException.class, () -> + new CreateOfferOptionParser(args).parse()); + assertEquals("no fixed price specified", exception.getMessage()); + } + + @Test + public void testValidCreateBsqSwapOfferOpts() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_SWAP + "=" + "true", + "--" + OPT_DIRECTION + "=" + "sell", + "--" + OPT_CURRENCY_CODE + "=" + "bsq", + "--" + OPT_MIN_AMOUNT + "=" + "0.075", + "--" + OPT_AMOUNT + "=" + "0.125", + "--" + OPT_FIXED_PRICE + "=" + "0.00005555" + }; + CreateOfferOptionParser parser = new CreateOfferOptionParser(args).parse(); + assertTrue(parser.getIsSwap()); + assertEquals("sell", parser.getDirection()); + assertEquals("bsq", parser.getCurrencyCode()); + assertEquals("0.075", parser.getMinAmount()); + assertEquals("0.125", parser.getAmount()); + assertEquals("0.00005555", parser.getFixedPrice()); + } + // createpaymentacct opt parser tests @Test - public void testCreatePaymentAcctOptParserWithMissingPaymentFormOptShouldThrowException() { + public void testCreatePaymentAcctWithMissingPaymentFormOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createpaymentacct.name() @@ -149,7 +240,7 @@ public void testCreatePaymentAcctOptParserWithMissingPaymentFormOptShouldThrowEx } @Test - public void testCreatePaymentAcctOptParserWithMissingPaymentFormOptValueShouldThrowException() { + public void testCreatePaymentAcctWithMissingPaymentFormOptValueShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createpaymentacct.name(), @@ -161,7 +252,7 @@ public void testCreatePaymentAcctOptParserWithMissingPaymentFormOptValueShouldTh } @Test - public void testCreatePaymentAcctOptParserWithInvalidPaymentFormOptValueShouldThrowException() { + public void testCreatePaymentAcctWithInvalidPaymentFormOptValueShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createpaymentacct.name(), @@ -180,7 +271,7 @@ public void testCreatePaymentAcctOptParserWithInvalidPaymentFormOptValueShouldTh // createcryptopaymentacct parser tests @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingAcctNameOptShouldThrowException() { + public void testCreateCryptoCurrencyPaymentAcctWithMissingAcctNameOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createcryptopaymentacct.name() @@ -191,7 +282,7 @@ public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingAcctNameOp } @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithEmptyAcctNameOptShouldThrowException() { + public void testCreateCryptoCurrencyPaymentAcctWithEmptyAcctNameOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createcryptopaymentacct.name(), @@ -203,7 +294,7 @@ public void testCreateCryptoCurrencyPaymentAcctOptionParserWithEmptyAcctNameOptS } @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingCurrencyCodeOptShouldThrowException() { + public void testCreateCryptoCurrencyPaymentAcctWithMissingCurrencyCodeOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createcryptopaymentacct.name(), @@ -215,7 +306,7 @@ public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingCurrencyCo } @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithInvalidCurrencyCodeOptShouldThrowException() { + public void testCreateCryptoCurrencyPaymentAcctWithInvalidCurrencyCodeOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createcryptopaymentacct.name(), @@ -228,7 +319,7 @@ public void testCreateCryptoCurrencyPaymentAcctOptionParserWithInvalidCurrencyCo } @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingAddressOptShouldThrowException() { + public void testCreateCryptoCurrencyPaymentAcctWithMissingAddressOptShouldThrowException() { String[] args = new String[]{ PASSWORD_OPT, createcryptopaymentacct.name(), @@ -241,7 +332,7 @@ public void testCreateCryptoCurrencyPaymentAcctOptionParserWithMissingAddressOpt } @Test - public void testCreateCryptoCurrencyPaymentAcctOptionParser() { + public void testCreateCryptoCurrencyPaymentAcct() { var acctName = "bsq payment account"; var currencyCode = "bsq"; var address = "B1nXyZ"; // address is validated on server From 35aeb61338c2fe13cc67cf49d506e967207c9d9f Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 25 Nov 2021 17:10:19 -0300 Subject: [PATCH 20/39] Adjust createoffer opt parser for bsq swaps --- .../cli/opts/AbstractMethodOptionParser.java | 2 +- ...CryptoCurrencyPaymentAcctOptionParser.java | 3 +- .../cli/opts/CreateOfferOptionParser.java | 47 ++++++++++++++----- cli/src/main/java/bisq/cli/opts/OptLabel.java | 1 + 4 files changed, 40 insertions(+), 13 deletions(-) diff --git a/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java b/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java index 499efba7bdc..2bccab1d098 100644 --- a/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/AbstractMethodOptionParser.java @@ -39,7 +39,7 @@ abstract class AbstractMethodOptionParser implements MethodOpts { protected final OptionParser parser = new OptionParser(); - // The help option for a specific api method, e.g., takeoffer -help. + // The help option for a specific api method, e.g., takeoffer --help. protected final OptionSpec helpOpt = parser.accepts(OPT_HELP, "Print method help").forHelp(); @Getter diff --git a/cli/src/main/java/bisq/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java index a37a9f109bb..94db971dba0 100644 --- a/cli/src/main/java/bisq/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CreateCryptoCurrencyPaymentAcctOptionParser.java @@ -24,6 +24,7 @@ import static bisq.cli.opts.OptLabel.OPT_ADDRESS; import static bisq.cli.opts.OptLabel.OPT_CURRENCY_CODE; import static bisq.cli.opts.OptLabel.OPT_TRADE_INSTANT; +import static java.lang.Boolean.FALSE; public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodOptionParser implements MethodOpts { @@ -39,7 +40,7 @@ public class CreateCryptoCurrencyPaymentAcctOptionParser extends AbstractMethodO final OptionSpec tradeInstantOpt = parser.accepts(OPT_TRADE_INSTANT, "create trade instant account") .withOptionalArg() .ofType(boolean.class) - .defaultsTo(Boolean.FALSE); + .defaultsTo(FALSE); public CreateCryptoCurrencyPaymentAcctOptionParser(String[] args) { super(args); diff --git a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java index df85c69812a..ba3a6b36cd3 100644 --- a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java @@ -23,6 +23,7 @@ import java.math.BigDecimal; import static bisq.cli.opts.OptLabel.*; +import static java.lang.Boolean.FALSE; import static joptsimple.internal.Strings.EMPTY; public class CreateOfferOptionParser extends AbstractMethodOptionParser implements MethodOpts { @@ -59,6 +60,11 @@ public class CreateOfferOptionParser extends AbstractMethodOptionParser implemen .withOptionalArg() .defaultsTo("btc"); + final OptionSpec isSwapOpt = parser.accepts(OPT_SWAP, "create bsq swap offer") + .withOptionalArg() + .ofType(boolean.class) + .defaultsTo(FALSE); + public CreateOfferOptionParser(String[] args) { super(args); } @@ -70,9 +76,6 @@ public CreateOfferOptionParser parse() { if (options.has(helpOpt)) return this; - if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) - throw new IllegalArgumentException("no payment account id specified"); - if (!options.has(directionOpt) || options.valueOf(directionOpt).isEmpty()) throw new IllegalArgumentException("no direction (buy|sell) specified"); @@ -82,17 +85,35 @@ public CreateOfferOptionParser parse() { if (!options.has(amountOpt) || options.valueOf(amountOpt).isEmpty()) throw new IllegalArgumentException("no btc amount specified"); - if (!options.has(mktPriceMarginOpt) && !options.has(fixedPriceOpt)) - throw new IllegalArgumentException("no market price margin or fixed price specified"); + if (getIsSwap()) { + if (options.has(paymentAccountIdOpt)) + throw new IllegalArgumentException("cannot use a payment account id in bsq swap offer"); + + if (options.has(mktPriceMarginOpt)) + throw new IllegalArgumentException("cannot use a market price margin in bsq swap offer"); + + if (options.has(securityDepositOpt)) + throw new IllegalArgumentException("cannot use a security deposit in bsq swap offer"); - if (options.has(mktPriceMarginOpt) && options.valueOf(mktPriceMarginOpt).isEmpty()) - throw new IllegalArgumentException("no market price margin specified"); + if (!options.has(fixedPriceOpt) || options.valueOf(fixedPriceOpt).isEmpty()) + throw new IllegalArgumentException("no fixed price specified"); + + } else { + if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) + throw new IllegalArgumentException("no payment account id specified"); - if (options.has(fixedPriceOpt) && options.valueOf(fixedPriceOpt).isEmpty()) - throw new IllegalArgumentException("no fixed price specified"); + if (!options.has(mktPriceMarginOpt) && !options.has(fixedPriceOpt)) + throw new IllegalArgumentException("no market price margin or fixed price specified"); - if (!options.has(securityDepositOpt) || options.valueOf(securityDepositOpt).isEmpty()) - throw new IllegalArgumentException("no security deposit specified"); + if (options.has(mktPriceMarginOpt) && options.valueOf(mktPriceMarginOpt).isEmpty()) + throw new IllegalArgumentException("no market price margin specified"); + + if (options.has(fixedPriceOpt) && options.valueOf(fixedPriceOpt).isEmpty()) + throw new IllegalArgumentException("no fixed price specified"); + + if (!options.has(securityDepositOpt) || options.valueOf(securityDepositOpt).isEmpty()) + throw new IllegalArgumentException("no security deposit specified"); + } return this; } @@ -141,4 +162,8 @@ public String getSecurityDeposit() { public String getMakerFeeCurrencyCode() { return options.has(makerFeeCurrencyCodeOpt) ? options.valueOf(makerFeeCurrencyCodeOpt) : "btc"; } + + public boolean getIsSwap() { + return options.valueOf(isSwapOpt); + } } diff --git a/cli/src/main/java/bisq/cli/opts/OptLabel.java b/cli/src/main/java/bisq/cli/opts/OptLabel.java index 70dda3e6fc3..7dc78165d7a 100644 --- a/cli/src/main/java/bisq/cli/opts/OptLabel.java +++ b/cli/src/main/java/bisq/cli/opts/OptLabel.java @@ -44,6 +44,7 @@ public class OptLabel { public final static String OPT_REGISTRATION_KEY = "registration-key"; public final static String OPT_SECURITY_DEPOSIT = "security-deposit"; public final static String OPT_SHOW_CONTRACT = "show-contract"; + public final static String OPT_SWAP = "swap"; public final static String OPT_TRADE_ID = "trade-id"; public final static String OPT_TRADE_INSTANT = "trade-instant"; public final static String OPT_TIMEOUT = "timeout"; From eff10842b936adb4c9ad70a5e5cffe0053e1fb59 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 25 Nov 2021 17:31:54 -0300 Subject: [PATCH 21/39] Tighten createoffer param validation (only bsq swaps supported) --- .../bisq/cli/opts/CreateOfferOptionParser.java | 5 ++++- .../java/bisq/cli/opts/OptionParsersTest.java | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java index ba3a6b36cd3..f75ee37db55 100644 --- a/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/CreateOfferOptionParser.java @@ -86,6 +86,9 @@ public CreateOfferOptionParser parse() { throw new IllegalArgumentException("no btc amount specified"); if (getIsSwap()) { + if (!options.valueOf(currencyCodeOpt).equalsIgnoreCase("bsq")) + throw new IllegalArgumentException("only bsq swaps are currently supported"); + if (options.has(paymentAccountIdOpt)) throw new IllegalArgumentException("cannot use a payment account id in bsq swap offer"); @@ -97,7 +100,7 @@ public CreateOfferOptionParser parse() { if (!options.has(fixedPriceOpt) || options.valueOf(fixedPriceOpt).isEmpty()) throw new IllegalArgumentException("no fixed price specified"); - + } else { if (!options.has(paymentAccountIdOpt) || options.valueOf(paymentAccountIdOpt).isEmpty()) throw new IllegalArgumentException("no payment account id specified"); diff --git a/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java b/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java index 0e58159b206..ba527a88e5a 100644 --- a/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java +++ b/cli/src/test/java/bisq/cli/opts/OptionParsersTest.java @@ -204,6 +204,23 @@ public void testCreateBsqSwapOfferWithMissingFixedPriceOptShouldThrowException() assertEquals("no fixed price specified", exception.getMessage()); } + @Test + public void testCreateBsqSwapOfferWithIncorrectCurrencyCodeOptShouldThrowException() { + String[] args = new String[]{ + PASSWORD_OPT, + createoffer.name(), + "--" + OPT_SWAP + "=" + "true", + "--" + OPT_DIRECTION + "=" + "sell", + "--" + OPT_CURRENCY_CODE + "=" + "xmr", + "--" + OPT_MIN_AMOUNT + "=" + "0.075", + "--" + OPT_AMOUNT + "=" + "0.125", + "--" + OPT_FIXED_PRICE + "=" + "0.00005555" + }; + Throwable exception = assertThrows(RuntimeException.class, () -> + new CreateOfferOptionParser(args).parse()); + assertEquals("only bsq swaps are currently supported", exception.getMessage()); + } + @Test public void testValidCreateBsqSwapOfferOpts() { String[] args = new String[]{ From 15621d42a9db62c1e0eddd95ac8044e631ed38de Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 25 Nov 2021 18:19:12 -0300 Subject: [PATCH 22/39] Re-@Disable method tests (don't run 2x from gradle) --- .../test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java | 3 ++- .../test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java index ce3e9e8f115..2810938f5c3 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/BsqSwapOfferTest.java @@ -23,6 +23,7 @@ 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; @@ -36,7 +37,7 @@ import static org.junit.jupiter.api.Assertions.fail; import static protobuf.OfferDirection.BUY; -// @Disabled +@Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class BsqSwapOfferTest extends AbstractOfferTest { 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 85f6f6d280d..8938231c74a 100644 --- a/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java +++ b/apitest/src/test/java/bisq/apitest/method/trade/BsqSwapTradeTest.java @@ -28,6 +28,7 @@ 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; @@ -50,7 +51,7 @@ import bisq.apitest.method.offer.AbstractOfferTest; import bisq.cli.GrpcClient; -// @Disabled +@Disabled @Slf4j @TestMethodOrder(MethodOrderer.OrderAnnotation.class) public class BsqSwapTradeTest extends AbstractTradeTest { From 132ecc9ac3e152e3adabf6926afe433300ab2bdf Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 25 Nov 2021 18:20:04 -0300 Subject: [PATCH 23/39] Adjust CliMain's createoffer for BSQ swaps --- cli/src/main/java/bisq/cli/CliMain.java | 34 ++++++++++++++++--------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index eef8943723a..bec252fe65b 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -334,6 +334,7 @@ public static void run(String[] args) { out.println(client.getMethodHelp(method)); return; } + var isSwap = opts.getIsSwap(); var paymentAcctId = opts.getPaymentAccountId(); var direction = opts.getDirection(); var currencyCode = opts.getCurrencyCode(); @@ -342,20 +343,28 @@ public static void run(String[] args) { var useMarketBasedPrice = opts.isUsingMktPriceMargin(); var fixedPrice = opts.getFixedPrice(); var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal(); - var securityDeposit = toSecurityDepositAsPct(opts.getSecurityDeposit()); + var securityDeposit = isSwap ? 0.00 : toSecurityDepositAsPct(opts.getSecurityDeposit()); var makerFeeCurrencyCode = opts.getMakerFeeCurrencyCode(); var triggerPrice = 0; // Cannot be defined until offer is in book. - var offer = client.createOffer(direction, - currencyCode, - amount, - minAmount, - useMarketBasedPrice, - fixedPrice, - marketPriceMargin.doubleValue(), - securityDeposit, - paymentAcctId, - makerFeeCurrencyCode, - triggerPrice); + OfferInfo offer; + if (isSwap) { + offer = client.createBsqSwapOffer(direction, + amount, + minAmount, + fixedPrice); + } else { + offer = client.createOffer(direction, + currencyCode, + amount, + minAmount, + useMarketBasedPrice, + fixedPrice, + marketPriceMargin.doubleValue(), + securityDeposit, + paymentAcctId, + makerFeeCurrencyCode, + triggerPrice); + } new TableBuilder(OFFER_TBL, offer).build().print(out); return; } @@ -817,6 +826,7 @@ private static void printHelp(OptionParser parser, @SuppressWarnings("SameParame stream.format(rowFormat, "", "--security-deposit= \\", ""); stream.format(rowFormat, "", "[--fee-currency=]", ""); stream.format(rowFormat, "", "[--trigger-price=]", ""); + stream.format(rowFormat, "", "[--swap=]", ""); stream.println(); stream.format(rowFormat, editoffer.name(), "--offer-id= \\", "Edit offer with id"); stream.format(rowFormat, "", "[--fixed-price=] \\", ""); From 0aa08524071d02ddac8056d8f214e3e315c0ea81 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Thu, 25 Nov 2021 18:21:06 -0300 Subject: [PATCH 24/39] Add CLI createoffer smoketest (for bsq swaps) NEVER RUN ON MAINNET! --- .../java/bisq/cli/CreateOfferSmokeTest.java | 85 +++++++++++++++++++ .../java/bisq/cli/GetOffersSmokeTest.java | 4 +- 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 cli/src/test/java/bisq/cli/CreateOfferSmokeTest.java diff --git a/cli/src/test/java/bisq/cli/CreateOfferSmokeTest.java b/cli/src/test/java/bisq/cli/CreateOfferSmokeTest.java new file mode 100644 index 00000000000..0cb717f935e --- /dev/null +++ b/cli/src/test/java/bisq/cli/CreateOfferSmokeTest.java @@ -0,0 +1,85 @@ +package bisq.cli; + +import static java.lang.System.out; +import static java.util.Arrays.stream; + +/** + Smoke tests for createoffer method. Useful for testing CLI command and examining the + format of its console output. + + Prerequisites: + + - Run `./bisq-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon --shutdownAfterTests=false --enableBisqDebugging=false` + + Note: Test harness will not automatically generate BTC blocks to confirm transactions. + + Never run on mainnet! + */ +@SuppressWarnings({"CommentedOutCode", "unused"}) +public class CreateOfferSmokeTest extends AbstractCliTest { + + public static void main(String[] args) { + createBsqSwapOffer("buy"); + createBsqSwapOffer("sell"); + } + + private static void createBsqSwapOffer(String direction) { + String[] args = createBsqSwapOfferCommand(direction, "0.01", "0.005", "0.00005"); + out.print(">>>>> bisq-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + + args = getMyOffersCommand(direction); + out.print(">>>>> bisq-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + + args = getAvailableOffersCommand(direction); + out.print(">>>>> bisq-cli "); + stream(args).forEach(a -> out.print(a + " ")); + out.println(); + CliMain.main(args); + out.println("<<<<<"); + } + + private static String[] createBsqSwapOfferCommand(String direction, + String amount, + String minAmount, + String fixedPrice) { + return new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "createoffer", + "--swap=true", + "--direction=" + direction, + "--currency-code=bsq", + "--amount=" + amount, + "--min-amount=" + minAmount, + "--fixed-price=" + fixedPrice + }; + } + + private static String[] getMyOffersCommand(String direction) { + return new String[]{ + PASSWORD_OPT, + ALICE_PORT_OPT, + "getmyoffers", + "--direction=" + direction, + "--currency-code=bsq" + }; + } + + private static String[] getAvailableOffersCommand(String direction) { + return new String[]{ + PASSWORD_OPT, + BOB_PORT_OPT, + "getoffers", + "--direction=" + direction, + "--currency-code=bsq" + }; + } +} diff --git a/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java b/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java index 6bbec525125..fc6ee7b4dbd 100644 --- a/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java +++ b/cli/src/test/java/bisq/cli/GetOffersSmokeTest.java @@ -12,7 +12,9 @@ This can be run on mainnet. */ @SuppressWarnings({"CommentedOutCode", "unused"}) -public class GetOffersSmokeTest { +public class GetOffersSmokeTest extends AbstractCliTest { + + // TODO use the static password and port opt definitions in superclass public static void main(String[] args) { getMyBsqOffers(); From 05d1916ae99197b0d8eede961c809e4d7b14b7c3 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Fri, 26 Nov 2021 10:54:28 -0300 Subject: [PATCH 25/39] Fix help text typo --- cli/src/main/java/bisq/cli/CliMain.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index bec252fe65b..0ddc25f4ab4 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -822,7 +822,7 @@ private static void printHelp(OptionParser parser, @SuppressWarnings("SameParame stream.format(rowFormat, "", "--currency-code= \\", ""); stream.format(rowFormat, "", "--amount= \\", ""); stream.format(rowFormat, "", "[--min-amount=] \\", ""); - stream.format(rowFormat, "", "--fixed-price= | --market-price=margin= \\", ""); + stream.format(rowFormat, "", "--fixed-price= | --market-price-margin= \\", ""); stream.format(rowFormat, "", "--security-deposit= \\", ""); stream.format(rowFormat, "", "[--fee-currency=]", ""); stream.format(rowFormat, "", "[--trigger-price=]", ""); From 64f228d872ac9f042f82971b07ad3b55fa51249f Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 27 Nov 2021 14:01:14 -0300 Subject: [PATCH 26/39] Make rpc GetOfferCategory service work for my+avail offers There are some use cases where the CLI needs to know what kind of offer is being acted on before the request is made, For example: There are differences between a BsqSwap 'takeoffer'request, and a v1 'takeoffer' request. A BsqSwap offer cannot be edited by an 'editoffer' request, and an attempt should be blocked by the CLI. - Append isMyOffer GetOfferCategoryRequest rpc msg def. - Adjust daemon.grpc services for new boolean GetOfferCategoryRequest param. - Adjust core.api for new boolean GetOfferCategoryRequest param. - Add validation check in core.api EditOfferValidator to block attempt to edit a BsqSwap offer. - Refactor CoreOffersService get*offer(id) methods to optionally throw excpetions. --- core/src/main/java/bisq/core/api/CoreApi.java | 12 +-- .../java/bisq/core/api/CoreOffersService.java | 99 +++++++++++-------- .../bisq/core/api/EditOfferValidator.java | 10 +- .../bisq/daemon/grpc/GrpcOffersService.java | 11 ++- .../bisq/daemon/grpc/GrpcTradesService.java | 2 +- proto/src/main/proto/grpc.proto | 1 + 6 files changed, 82 insertions(+), 53 deletions(-) diff --git a/core/src/main/java/bisq/core/api/CoreApi.java b/core/src/main/java/bisq/core/api/CoreApi.java index 261993f08de..caa9e2650a6 100644 --- a/core/src/main/java/bisq/core/api/CoreApi.java +++ b/core/src/main/java/bisq/core/api/CoreApi.java @@ -120,16 +120,16 @@ public String getMethodHelp(String methodName) { // Offers /////////////////////////////////////////////////////////////////////////////////////////// - public boolean isAvailableFiatOffer(String id) { - return coreOffersService.isAvailableFiatOffer(id); + public boolean isFiatOffer(String id, boolean isMyOffer) { + return coreOffersService.isFiatOffer(id, isMyOffer); } - public boolean isAvailableAltcoinOffer(String id) { - return coreOffersService.isAvailableAltcoinOffer(id); + public boolean isAltcoinOffer(String id, boolean isMyOffer) { + return coreOffersService.isAltcoinOffer(id, isMyOffer); } - public boolean isAvailableBsqSwapOffer(String id) { - return coreOffersService.isAvailableBsqSwapOffer(id); + public boolean isBsqSwapOffer(String id, boolean isMyOffer) { + return coreOffersService.isBsqSwapOffer(id, isMyOffer); } public Offer getBsqSwapOffer(String id) { diff --git a/core/src/main/java/bisq/core/api/CoreOffersService.java b/core/src/main/java/bisq/core/api/CoreOffersService.java index 74e8af6c304..fde91f273c6 100644 --- a/core/src/main/java/bisq/core/api/CoreOffersService.java +++ b/core/src/main/java/bisq/core/api/CoreOffersService.java @@ -48,6 +48,8 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; +import java.util.Optional; +import java.util.function.BiFunction; import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; @@ -61,8 +63,6 @@ import static bisq.core.offer.Offer.State; import static bisq.core.offer.OfferDirection.BUY; import static bisq.core.offer.OfferUtil.getRandomOfferId; -import static bisq.core.offer.OfferUtil.isAltcoinOffer; -import static bisq.core.offer.OfferUtil.isFiatOffer; import static bisq.core.offer.OpenOffer.State.AVAILABLE; import static bisq.core.offer.OpenOffer.State.DEACTIVATED; import static bisq.core.payment.PaymentAccountUtil.isPaymentAccountValidForOffer; @@ -81,6 +81,9 @@ class CoreOffersService { private final Supplier> openOfferPriceComparator = () -> comparing(openOffer -> openOffer.getOffer().getPrice()); + private final BiFunction toOfferWithId = (id, isMyOffer) -> + isMyOffer ? getMyOffer(id).getOffer() : getOffer(id); + private final CoreContext coreContext; private final KeyRing keyRing; // Dependencies on core api services in this package must be kept to an absolute @@ -121,63 +124,48 @@ public CoreOffersService(CoreContext coreContext, this.user = user; } - boolean isAvailableFiatOffer(String id) { - return isFiatOffer(getOffer(id)); + boolean isFiatOffer(String id, boolean isMyOffer) { + var offer = toOfferWithId.apply(id, isMyOffer); + return OfferUtil.isFiatOffer(offer); } - boolean isAvailableAltcoinOffer(String id) { - return isAltcoinOffer(getOffer(id)); + boolean isAltcoinOffer(String id, boolean isMyOffer) { + var offer = toOfferWithId.apply(id, isMyOffer); + return OfferUtil.isAltcoinOffer(offer); } - boolean isAvailableBsqSwapOffer(String id) { - var offer = getOffer(id); + boolean isBsqSwapOffer(String id, boolean isMyOffer) { + var offer = toOfferWithId.apply(id, isMyOffer); return offer.isBsqSwapOffer(); } - Offer getBsqSwapOffer(String id) { - return offerBookService.getOffers().stream() - .filter(o -> o.getId().equals(id)) - .filter(o -> !o.isMyOffer(keyRing)) - .filter(o -> offerFilterService.canTakeOffer(o, coreContext.isApiUser()).isValid()) - .filter(o -> o.isBsqSwapOffer()) - .findAny().orElseThrow(() -> - new IllegalStateException(format("offer with id '%s' not found", id))); - } - Offer getOffer(String id) { - return offerBookService.getOffers().stream() - .filter(o -> o.getId().equals(id)) - .filter(o -> !o.isMyOffer(keyRing)) - .filter(o -> offerFilterService.canTakeOffer(o, coreContext.isApiUser()).isValid()) - .findAny().orElseThrow(() -> - new IllegalStateException(format("offer with id '%s' not found", id))); + return findAvailableOffer(id).orElseThrow(() -> + new IllegalStateException(format("offer with id '%s' not found", id))); } OpenOffer getMyOffer(String id) { - return openOfferManager.getObservableList().stream() - .filter(o -> o.getId().equals(id)) - .filter(o -> o.getOffer().isMyOffer(keyRing)) - .findAny().orElseThrow(() -> - new IllegalStateException(format("offer with id '%s' not found", id))); + return findMyOpenOffer(id).orElseThrow(() -> + new IllegalStateException(format("offer with id '%s' not found", id))); + } + + Offer getBsqSwapOffer(String id) { + return findAvailableBsqSwapOffer(id).orElseThrow(() -> + new IllegalStateException(format("offer with id '%s' not found", id))); } Offer getMyBsqSwapOffer(String id) { - return offerBookService.getOffers().stream() - .filter(o -> o.getId().equals(id)) - .filter(o -> o.isMyOffer(keyRing)) - .filter(o -> o.isBsqSwapOffer()) - .findAny().orElseThrow(() -> - new IllegalStateException(format("offer with id '%s' not found", id))); + return findMyBsqSwapOffer(id).orElseThrow(() -> + new IllegalStateException(format("offer with id '%s' not found", id))); } List getBsqSwapOffers(String direction) { - var offers = offerBookService.getOffers().stream() + return offerBookService.getOffers().stream() .filter(o -> !o.isMyOffer(keyRing)) .filter(o -> o.getDirection().name().equalsIgnoreCase(direction)) - .filter(o -> o.isBsqSwapOffer()) + .filter(Offer::isBsqSwapOffer) .sorted(priceComparator(direction)) .collect(Collectors.toList()); - return offers; } List getOffers(String direction, String currencyCode) { @@ -198,13 +186,12 @@ List getMyOffers(String direction, String currencyCode) { } List getMyBsqSwapOffers(String direction) { - var offers = offerBookService.getOffers().stream() + return offerBookService.getOffers().stream() .filter(o -> o.isMyOffer(keyRing)) .filter(o -> o.getDirection().name().equalsIgnoreCase(direction)) .filter(Offer::isBsqSwapOffer) .sorted(priceComparator(direction)) .collect(Collectors.toList()); - return offers; } OpenOffer getMyOpenBsqSwapOffer(String id) { @@ -393,6 +380,38 @@ private void placeOffer(Offer offer, throw new IllegalStateException(offer.getErrorMessage()); } + private Optional findAvailableBsqSwapOffer(String id) { + return offerBookService.getOffers().stream() + .filter(o -> o.getId().equals(id)) + .filter(o -> !o.isMyOffer(keyRing)) + .filter(o -> offerFilterService.canTakeOffer(o, coreContext.isApiUser()).isValid()) + .filter(Offer::isBsqSwapOffer) + .findAny(); + } + + private Optional findMyBsqSwapOffer(String id) { + return offerBookService.getOffers().stream() + .filter(o -> o.getId().equals(id)) + .filter(o -> o.isMyOffer(keyRing)) + .filter(Offer::isBsqSwapOffer) + .findAny(); + } + + private Optional findAvailableOffer(String id) { + return offerBookService.getOffers().stream() + .filter(o -> o.getId().equals(id)) + .filter(o -> !o.isMyOffer(keyRing)) + .filter(o -> offerFilterService.canTakeOffer(o, coreContext.isApiUser()).isValid()) + .findAny(); + } + + private Optional findMyOpenOffer(String id) { + return openOfferManager.getObservableList().stream() + .filter(o -> o.getId().equals(id)) + .filter(o -> o.getOffer().isMyOffer(keyRing)) + .findAny(); + } + private OfferPayload getMergedOfferPayload(OpenOffer openOffer, String editedPriceAsString, double editedMarketPriceMargin, diff --git a/core/src/main/java/bisq/core/api/EditOfferValidator.java b/core/src/main/java/bisq/core/api/EditOfferValidator.java index 7a9840c3cdb..89c9366e1eb 100644 --- a/core/src/main/java/bisq/core/api/EditOfferValidator.java +++ b/core/src/main/java/bisq/core/api/EditOfferValidator.java @@ -45,7 +45,8 @@ class EditOfferValidator { } void validate() { - log.info("Verifying 'editoffer' params OK for editType {}", editType); + log.info("Verifying 'editoffer' params for editType {}", editType); + checkNotBsqSwapOffer(); switch (editType) { case ACTIVATION_STATE_ONLY: { validateEditedActivationState(); @@ -138,4 +139,11 @@ private void checkNotAltcoinOffer() { currentlyOpenOffer.getId())); } } + + private void checkNotBsqSwapOffer() { + if (currentlyOpenOffer.getOffer().isBsqSwapOffer()) { + throw new IllegalStateException( + format("cannot edit bsq swap offer with id '%s'", currentlyOpenOffer.getId())); + } + } } diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java index 4f2fef1556b..edfa09a3d6d 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcOffersService.java @@ -92,7 +92,7 @@ public GrpcOffersService(CoreApi coreApi, GrpcExceptionHandler exceptionHandler) public void getOfferCategory(GetOfferCategoryRequest req, StreamObserver responseObserver) { try { - OfferCategory category = getAvailableOfferCategory(req.getId()); + OfferCategory category = getOfferCategory(req.getId(), req.getIsMyOffer()); var reply = newBuilder() .setOfferCategory(category) .build(); @@ -335,6 +335,7 @@ final Optional rateMeteringInterceptor() { return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass()) .or(() -> Optional.of(CallRateMeteringInterceptor.valueOf( new HashMap<>() {{ + put(getGetOfferCategoryMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getGetOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getGetMyOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getGetOffersMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); @@ -346,12 +347,12 @@ final Optional rateMeteringInterceptor() { ))); } - private OfferCategory getAvailableOfferCategory(String offerId) { - if (coreApi.isAvailableAltcoinOffer(offerId)) + private OfferCategory getOfferCategory(String offerId, boolean isMyOffer) { + if (coreApi.isAltcoinOffer(offerId, isMyOffer)) return ALTCOIN; - else if (coreApi.isAvailableFiatOffer(offerId)) + else if (coreApi.isFiatOffer(offerId, isMyOffer)) return FIAT; - else if (coreApi.isAvailableBsqSwapOffer(offerId)) + else if (coreApi.isBsqSwapOffer(offerId, isMyOffer)) return BSQ_SWAP; else return UNKNOWN; diff --git a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java index 22961043b12..6a9ce12a7cb 100644 --- a/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java +++ b/daemon/src/main/java/bisq/daemon/grpc/GrpcTradesService.java @@ -79,7 +79,7 @@ public void takeOffer(TakeOfferRequest req, exceptionHandler, log); - if (coreApi.isAvailableBsqSwapOffer(req.getOfferId())) { + if (coreApi.isBsqSwapOffer(req.getOfferId(), false)) { coreApi.takeBsqSwapOffer(req.getOfferId(), bsqSwapTrade -> { var reply = buildTakeOfferReply(bsqSwapTrade); diff --git a/proto/src/main/proto/grpc.proto b/proto/src/main/proto/grpc.proto index ad0ca7f8b6c..10a90940db9 100644 --- a/proto/src/main/proto/grpc.proto +++ b/proto/src/main/proto/grpc.proto @@ -92,6 +92,7 @@ service Offers { message GetOfferCategoryRequest { string id = 1; + bool isMyOffer = 2; } message GetOfferCategoryReply { From b95e4b37b6dc22a49436f34b137bef37f2b7387e Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 27 Nov 2021 14:01:38 -0300 Subject: [PATCH 27/39] Block attempts to edit BsqSwap offers from CLI --- cli/src/main/java/bisq/cli/CliMain.java | 12 ++++++++--- cli/src/main/java/bisq/cli/GrpcClient.java | 8 +++++-- .../bisq/cli/opts/OfferIdOptionParser.java | 6 ++++++ .../cli/request/OffersServiceRequest.java | 21 +++++++++++++------ 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index 0ddc25f4ab4..4b4387bb4de 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -369,12 +369,18 @@ public static void run(String[] args) { return; } case editoffer: { - var opts = new EditOfferOptionParser(args).parse(); - if (opts.isForHelp()) { + var offerIdOpt = new OfferIdOptionParser(args, true).parse(); + if (offerIdOpt.isForHelp()) { out.println(client.getMethodHelp(method)); return; } - var offerId = opts.getOfferId(); + // What kind of offer is being edited? BSQ swaps cannot be edited. + var offerId = offerIdOpt.getOfferId(); + var offerCategory = client.getMyOfferCategory(offerId); + if (offerCategory.equals(BSQ_SWAP)) + throw new IllegalStateException("cannot edit swap bsq offers"); + + var opts = new EditOfferOptionParser(args).parse(); var fixedPrice = opts.getFixedPrice(); var isUsingMktPriceMargin = opts.isUsingMktPriceMargin(); var marketPriceMargin = opts.getMktPriceMarginAsBigDecimal(); diff --git a/cli/src/main/java/bisq/cli/GrpcClient.java b/cli/src/main/java/bisq/cli/GrpcClient.java index 8b218d5d23a..6d73bc4ab51 100644 --- a/cli/src/main/java/bisq/cli/GrpcClient.java +++ b/cli/src/main/java/bisq/cli/GrpcClient.java @@ -22,7 +22,6 @@ import bisq.proto.grpc.BsqBalanceInfo; import bisq.proto.grpc.BtcBalanceInfo; import bisq.proto.grpc.GetMethodHelpRequest; -import bisq.proto.grpc.GetOfferCategoryReply; import bisq.proto.grpc.GetVersionRequest; import bisq.proto.grpc.OfferInfo; import bisq.proto.grpc.RegisterDisputeAgentRequest; @@ -40,6 +39,7 @@ import lombok.extern.slf4j.Slf4j; import static bisq.proto.grpc.EditOfferRequest.EditType; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory; @@ -138,10 +138,14 @@ public TxInfo getTransaction(String txId) { return walletsServiceRequest.getTransaction(txId); } - public GetOfferCategoryReply.OfferCategory getAvailableOfferCategory(String offerId) { + public OfferCategory getAvailableOfferCategory(String offerId) { return offersServiceRequest.getAvailableOfferCategory(offerId); } + public OfferCategory getMyOfferCategory(String offerId) { + return offersServiceRequest.getMyOfferCategory(offerId); + } + public OfferInfo createBsqSwapOffer(String direction, long amount, long minAmount, diff --git a/cli/src/main/java/bisq/cli/opts/OfferIdOptionParser.java b/cli/src/main/java/bisq/cli/opts/OfferIdOptionParser.java index 60bee4960e1..ccf04026523 100644 --- a/cli/src/main/java/bisq/cli/opts/OfferIdOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/OfferIdOptionParser.java @@ -32,7 +32,13 @@ public class OfferIdOptionParser extends AbstractMethodOptionParser implements M .withRequiredArg(); public OfferIdOptionParser(String[] args) { + this(args, false); + } + + public OfferIdOptionParser(String[] args, boolean allowsUnrecognizedOptions) { super(args); + if (allowsUnrecognizedOptions) + this.parser.allowsUnrecognizedOptions(); } public OfferIdOptionParser parse() { diff --git a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java index 7fd32e1a911..c3cd9cdc799 100644 --- a/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java +++ b/cli/src/main/java/bisq/cli/request/OffersServiceRequest.java @@ -24,7 +24,6 @@ import bisq.proto.grpc.GetBsqSwapOffersRequest; import bisq.proto.grpc.GetMyOfferRequest; import bisq.proto.grpc.GetMyOffersRequest; -import bisq.proto.grpc.GetOfferCategoryReply; import bisq.proto.grpc.GetOfferCategoryRequest; import bisq.proto.grpc.GetOfferRequest; import bisq.proto.grpc.GetOffersRequest; @@ -40,6 +39,7 @@ import static bisq.proto.grpc.EditOfferRequest.EditType.FIXED_PRICE_ONLY; import static bisq.proto.grpc.EditOfferRequest.EditType.MKT_PRICE_MARGIN_ONLY; import static bisq.proto.grpc.EditOfferRequest.EditType.TRIGGER_PRICE_ONLY; +import static bisq.proto.grpc.GetOfferCategoryReply.OfferCategory; import static java.util.Comparator.comparing; import static java.util.stream.Collectors.toList; import static protobuf.OfferDirection.BUY; @@ -63,11 +63,12 @@ public OffersServiceRequest(GrpcStubs grpcStubs) { this.grpcStubs = grpcStubs; } - public GetOfferCategoryReply.OfferCategory getAvailableOfferCategory(String offerId) { - var request = GetOfferCategoryRequest.newBuilder() - .setId(offerId) - .build(); - return grpcStubs.offersService.getOfferCategory(request).getOfferCategory(); + public OfferCategory getAvailableOfferCategory(String offerId) { + return getOfferCategory(offerId, false); + } + + public OfferCategory getMyOfferCategory(String offerId) { + return getOfferCategory(offerId, true); } public OfferInfo createBsqSwapOffer(String direction, @@ -383,6 +384,14 @@ public List sortOffersByDate(List offerInfoList) { .collect(toList()); } + private OfferCategory getOfferCategory(String offerId, boolean isMyOffer) { + var request = GetOfferCategoryRequest.newBuilder() + .setId(offerId) + .setIsMyOffer(isMyOffer) + .build(); + return grpcStubs.offersService.getOfferCategory(request).getOfferCategory(); + } + private static boolean isSupportedCryptoCurrency(String currencyCode) { return getSupportedCryptoCurrencies().contains(currencyCode.toUpperCase()); } From 000fe9b991397dfaa3aca12b09aaaf3a328bdee8 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 27 Nov 2021 14:02:11 -0300 Subject: [PATCH 28/39] Test blocking attempts to edit BsqSwap offers on server --- .../apitest/method/offer/EditOfferTest.java | 181 ++++++++++-------- 1 file changed, 105 insertions(+), 76 deletions(-) diff --git a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java index 59c231d07b7..559f137882a 100644 --- a/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java +++ b/apitest/src/test/java/bisq/apitest/method/offer/EditOfferTest.java @@ -38,15 +38,13 @@ import org.junit.jupiter.api.TestMethodOrder; import static bisq.apitest.config.ApiTestConfig.BSQ; +import static bisq.apitest.config.ApiTestConfig.BTC; import static bisq.apitest.config.ApiTestConfig.EUR; import static bisq.apitest.config.ApiTestConfig.USD; import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAsPercent; import static bisq.proto.grpc.EditOfferRequest.EditType.*; import static java.lang.String.format; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.*; import static protobuf.OfferDirection.BUY; import static protobuf.OfferDirection.SELL; @@ -70,22 +68,22 @@ public void testOfferDisableAndEnable() { paymentAcct.getId(), 0.0, NO_TRIGGER_PRICE); - log.debug("ORIGINAL EUR OFFER:\n{}", toOfferTable.apply(originalOffer)); + log.debug("Original EUR offer:\n{}", toOfferTable.apply(originalOffer)); assertFalse(originalOffer.getIsActivated()); // Not activated until prep is done. - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. originalOffer = aliceClient.getMyOffer(originalOffer.getId()); assertTrue(originalOffer.getIsActivated()); // Disable offer aliceClient.editOfferActivationState(originalOffer.getId(), DEACTIVATE_OFFER); genBtcBlocksThenWait(1, 1500); // Wait for offer book removal. OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED EUR OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited EUR offer:\n{}", toOfferTable.apply(editedOffer)); assertFalse(editedOffer.getIsActivated()); // Re-enable offer aliceClient.editOfferActivationState(editedOffer.getId(), ACTIVATE_OFFER); genBtcBlocksThenWait(1, 1500); // Wait for offer book re-entry. editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED EUR OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited EUR offer:\n{}", toOfferTable.apply(editedOffer)); assertTrue(editedOffer.getIsActivated()); doSanityCheck(originalOffer, editedOffer); @@ -100,8 +98,8 @@ public void testEditTriggerPrice() { paymentAcct.getId(), 0.0, NO_TRIGGER_PRICE); - log.debug("ORIGINAL EUR OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original EUR offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. originalOffer = aliceClient.getMyOffer(originalOffer.getId()); assertEquals(0 /*no trigger price*/, originalOffer.getTriggerPrice()); @@ -111,9 +109,9 @@ public void testEditTriggerPrice() { var newTriggerPriceAsLong = calcPriceAsLong.apply(mktPrice, delta); aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPriceAsLong); - sleep(2500); // Wait for offer book re-entry. + sleep(2_500); // Wait for offer book re-entry. OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED EUR OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited EUR offer:\n{}", toOfferTable.apply(editedOffer)); assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice()); assertTrue(editedOffer.getUseMarketBasedPrice()); @@ -124,13 +122,13 @@ public void testEditTriggerPrice() { @Order(3) public void testSetTriggerPriceToNegativeValueShouldThrowException() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("FI"); - final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), + var originalOffer = createMktPricedOfferForEdit(SELL.name(), EUR, paymentAcct.getId(), 0.0, NO_TRIGGER_PRICE); - log.debug("ORIGINAL EUR OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original EUR offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. // Edit the offer's trigger price, set to -1, check error. Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOfferTriggerPrice(originalOffer.getId(), -1L)); @@ -145,19 +143,19 @@ public void testSetTriggerPriceToNegativeValueShouldThrowException() { public void testEditMktPriceMargin() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("US"); var originalMktPriceMargin = new BigDecimal("0.1").doubleValue(); - OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), + var originalOffer = createMktPricedOfferForEdit(SELL.name(), USD, paymentAcct.getId(), originalMktPriceMargin, NO_TRIGGER_PRICE); - log.debug("ORIGINAL USD OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original USD offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin()); // Edit the offer's price margin, nothing else. var newMktPriceMargin = new BigDecimal("0.5").doubleValue(); aliceClient.editOfferPriceMargin(originalOffer.getId(), newMktPriceMargin); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED USD OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited USD offer:\n{}", toOfferTable.apply(editedOffer)); assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); doSanityCheck(originalOffer, editedOffer); @@ -169,19 +167,19 @@ public void testEditFixedPrice() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU"); double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE); String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000); - OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(), + var originalOffer = createFixedPricedOfferForEdit(BUY.name(), RUBLE, paymentAcct.getId(), fixedPriceAsString); - log.debug("ORIGINAL RUB OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original RUB offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. // Edit the offer's fixed price, nothing else. String editedFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 100_000.0000); aliceClient.editOfferFixedPrice(originalOffer.getId(), editedFixedPriceAsString); // Wait for edited offer to be removed from offer-book, edited, and re-published. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED RUB OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited RUB offer:\n{}", toOfferTable.apply(editedOffer)); var expectedNewFixedPrice = scaledUpFiatOfferPrice.apply(new BigDecimal(editedFixedPriceAsString)); assertEquals(expectedNewFixedPrice, editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); @@ -195,12 +193,12 @@ public void testEditFixedPriceAndDeactivation() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU"); double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE); String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000); - OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(), + var originalOffer = createFixedPricedOfferForEdit(BUY.name(), RUBLE, paymentAcct.getId(), fixedPriceAsString); - log.debug("ORIGINAL RUB OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original RUB offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. // Edit the offer's fixed price and deactivate it. String editedFixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 100_000.0000); aliceClient.editOffer(originalOffer.getId(), @@ -211,9 +209,9 @@ public void testEditFixedPriceAndDeactivation() { DEACTIVATE_OFFER, FIXED_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be removed from offer-book, edited, and re-published. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED RUB OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited RUB offer:\n{}", toOfferTable.apply(editedOffer)); var expectedNewFixedPrice = scaledUpFiatOfferPrice.apply(new BigDecimal(editedFixedPriceAsString)); assertEquals(expectedNewFixedPrice, editedOffer.getPrice()); assertFalse(editedOffer.getIsActivated()); @@ -232,8 +230,8 @@ public void testEditMktPriceMarginAndDeactivation() { paymentAcct.getId(), originalMktPriceMargin, 0); - log.debug("ORIGINAL USD OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original USD offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. originalOffer = aliceClient.getMyOffer(originalOffer.getId()); assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin()); @@ -247,9 +245,9 @@ public void testEditMktPriceMarginAndDeactivation() { DEACTIVATE_OFFER, MKT_PRICE_MARGIN_AND_ACTIVATION_STATE); // Wait for edited offer to be removed from offer-book, edited, and re-published. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED USD OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited USD offer:\n{}", toOfferTable.apply(editedOffer)); assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); assertEquals(0, editedOffer.getTriggerPrice()); assertFalse(editedOffer.getIsActivated()); @@ -261,18 +259,16 @@ public void testEditMktPriceMarginAndDeactivation() { @Order(8) public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("US"); - var originalMktPriceMargin = new BigDecimal("0.0").doubleValue(); var mktPriceAsDouble = aliceClient.getBtcPrice(USD); var originalTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, -5_000.0000); - OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), USD, paymentAcct.getId(), originalMktPriceMargin, originalTriggerPriceAsLong); - log.debug("ORIGINAL USD OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original USD offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. originalOffer = aliceClient.getMyOffer(originalOffer.getId()); assertEquals(scaledDownMktPriceMargin.apply(originalMktPriceMargin), originalOffer.getMarketPriceMargin()); assertEquals(originalTriggerPriceAsLong, originalOffer.getTriggerPrice()); @@ -288,9 +284,9 @@ public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() { DEACTIVATE_OFFER, MKT_PRICE_MARGIN_AND_TRIGGER_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be removed from offer-book, edited, and re-published. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED USD OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited USD offer:\n{}", toOfferTable.apply(editedOffer)); assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice()); assertFalse(editedOffer.getIsActivated()); @@ -303,13 +299,13 @@ public void testEditMktPriceMarginAndTriggerPriceAndDeactivation() { public void testEditingFixedPriceInMktPriceMarginBasedOfferShouldThrowException() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("US"); var originalMktPriceMargin = new BigDecimal("0.0").doubleValue(); - final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), + var originalOffer = createMktPricedOfferForEdit(SELL.name(), USD, paymentAcct.getId(), originalMktPriceMargin, NO_TRIGGER_PRICE); - log.debug("ORIGINAL USD OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original USD offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. // Try to edit both the fixed price and mkt price margin. var newMktPriceMargin = new BigDecimal("0.25").doubleValue(); var newFixedPrice = "50000.0000"; @@ -335,12 +331,12 @@ public void testEditingTriggerPriceInFixedPriceOfferShouldThrowException() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("RU"); double mktPriceAsDouble = aliceClient.getBtcPrice(RUBLE); String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 200_000.0000); - OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(), + var originalOffer = createFixedPricedOfferForEdit(BUY.name(), RUBLE, paymentAcct.getId(), fixedPriceAsString); - log.debug("ORIGINAL RUB OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original RUB offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. long newTriggerPrice = 1000000L; Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOfferTriggerPrice(originalOffer.getId(), newTriggerPrice)); @@ -358,12 +354,12 @@ public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() { PaymentAccount paymentAcct = getOrCreatePaymentAccount("MX"); double mktPriceAsDouble = aliceClient.getBtcPrice("MXN"); String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00); - OfferInfo originalOffer = createFixedPricedOfferForEdit(BUY.name(), + var originalOffer = createFixedPricedOfferForEdit(BUY.name(), "MXN", paymentAcct.getId(), fixedPriceAsString); - log.debug("ORIGINAL MXN OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original MXN offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. // Change the offer to mkt price based and set a trigger price. var newMktPriceMargin = new BigDecimal("0.05").doubleValue(); @@ -377,9 +373,9 @@ public void testChangeFixedPriceOfferToPriceMarginBasedOfferWithTriggerPrice() { ACTIVATE_OFFER, MKT_PRICE_MARGIN_AND_TRIGGER_PRICE); // Wait for edited offer to be removed from offer-book, edited, and re-published. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED MXN OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited MXN offer:\n{}", toOfferTable.apply(editedOffer)); assertTrue(editedOffer.getUseMarketBasedPrice()); assertEquals(scaledDownMktPriceMargin.apply(newMktPriceMargin), editedOffer.getMarketPriceMargin()); assertEquals(newTriggerPriceAsLong, editedOffer.getTriggerPrice()); @@ -396,13 +392,13 @@ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() { var originalMktPriceMargin = new BigDecimal("0.25").doubleValue(); var delta = 1_000.0000; // trigger price on sell offer is 1K below mkt price var originalTriggerPriceAsLong = calcPriceAsLong.apply(mktPriceAsDouble, delta); - final OfferInfo originalOffer = createMktPricedOfferForEdit(SELL.name(), + var originalOffer = createMktPricedOfferForEdit(SELL.name(), "GBP", paymentAcct.getId(), originalMktPriceMargin, originalTriggerPriceAsLong); - log.debug("ORIGINAL GBP OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original GBP offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. String fixedPriceAsString = calcPriceAsString.apply(mktPriceAsDouble, 0.00); aliceClient.editOffer(originalOffer.getId(), @@ -413,9 +409,9 @@ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() { DEACTIVATE_OFFER, FIXED_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be removed from offer-book, edited, and re-published. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED GBP OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited GBP offer:\n{}", toOfferTable.apply(editedOffer)); assertEquals(scaledUpFiatOfferPrice.apply(new BigDecimal(fixedPriceAsString)), editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); assertEquals(0.00, editedOffer.getMarketPriceMargin()); @@ -426,7 +422,7 @@ public void testChangePriceMarginBasedOfferToFixedPriceOfferAndDeactivateIt() { @Test @Order(13) public void testChangeFixedPricedBsqOfferToPriceMarginBasedOfferShouldThrowException() { - OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), + var originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), BSQ, 100_000_000L, 100_000_000L, @@ -434,8 +430,8 @@ public void testChangeFixedPricedBsqOfferToPriceMarginBasedOfferShouldThrowExcep getDefaultBuyerSecurityDepositAsPercent(), alicesLegacyBsqAcct.getId(), BSQ); - log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOffer(originalOffer.getId(), "0.00", @@ -453,7 +449,7 @@ public void testChangeFixedPricedBsqOfferToPriceMarginBasedOfferShouldThrowExcep @Test @Order(14) public void testEditTriggerPriceOnFixedPriceBsqOfferShouldThrowException() { - OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), + var originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), BSQ, 100_000_000L, 100_000_000L, @@ -461,8 +457,8 @@ public void testEditTriggerPriceOnFixedPriceBsqOfferShouldThrowException() { getDefaultBuyerSecurityDepositAsPercent(), alicesLegacyBsqAcct.getId(), BSQ); - log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. var newTriggerPriceAsLong = calcPriceAsLong.apply(0.00005, 0.00); Throwable exception = assertThrows(StatusRuntimeException.class, () -> aliceClient.editOffer(originalOffer.getId(), @@ -482,7 +478,7 @@ public void testEditTriggerPriceOnFixedPriceBsqOfferShouldThrowException() { @Order(15) public void testEditFixedPriceOnBsqOffer() { String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), + var originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), BSQ, 100_000_000L, 100_000_000L, @@ -490,8 +486,8 @@ public void testEditFixedPriceOnBsqOffer() { getDefaultBuyerSecurityDepositAsPercent(), alicesLegacyBsqAcct.getId(), BSQ); - log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. String newFixedPriceAsString = "0.00003111"; aliceClient.editOffer(originalOffer.getId(), newFixedPriceAsString, @@ -501,9 +497,9 @@ public void testEditFixedPriceOnBsqOffer() { ACTIVATE_OFFER, FIXED_PRICE_ONLY); // Wait for edited offer to be edited and removed from offer-book. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED BSQ OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited BSQ offer:\n{}", toOfferTable.apply(editedOffer)); assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice()); assertTrue(editedOffer.getIsActivated()); assertFalse(editedOffer.getUseMarketBasedPrice()); @@ -515,7 +511,7 @@ public void testEditFixedPriceOnBsqOffer() { @Order(16) public void testDisableBsqOffer() { String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), + var originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), BSQ, 100_000_000L, 100_000_000L, @@ -523,8 +519,8 @@ public void testDisableBsqOffer() { getDefaultBuyerSecurityDepositAsPercent(), alicesLegacyBsqAcct.getId(), BSQ); - log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. aliceClient.editOffer(originalOffer.getId(), fixedPriceAsString, false, @@ -533,9 +529,9 @@ public void testDisableBsqOffer() { DEACTIVATE_OFFER, ACTIVATION_STATE_ONLY); // Wait for edited offer to be removed from offer-book. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED BSQ OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited BSQ offer:\n{}", toOfferTable.apply(editedOffer)); assertFalse(editedOffer.getIsActivated()); assertEquals(scaledUpAltcoinOfferPrice.apply(fixedPriceAsString), editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); @@ -547,7 +543,7 @@ public void testDisableBsqOffer() { @Order(17) public void testEditFixedPriceAndDisableBsqOffer() { String fixedPriceAsString = "0.00005"; // FIXED PRICE IN BTC (satoshis) FOR 1 BSQ - final OfferInfo originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), + var originalOffer = aliceClient.createFixedPricedOffer(BUY.name(), BSQ, 100_000_000L, 100_000_000L, @@ -555,8 +551,8 @@ public void testEditFixedPriceAndDisableBsqOffer() { getDefaultBuyerSecurityDepositAsPercent(), alicesLegacyBsqAcct.getId(), BSQ); - log.debug("ORIGINAL BSQ OFFER:\n{}", toOfferTable.apply(originalOffer)); - genBtcBlocksThenWait(1, 2500); // Wait for offer book entry. + log.debug("Original BSQ offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. String newFixedPriceAsString = "0.000045"; aliceClient.editOffer(originalOffer.getId(), newFixedPriceAsString, @@ -566,9 +562,9 @@ public void testEditFixedPriceAndDisableBsqOffer() { DEACTIVATE_OFFER, FIXED_PRICE_AND_ACTIVATION_STATE); // Wait for edited offer to be edited and removed from offer-book. - genBtcBlocksThenWait(1, 2500); + genBtcBlocksThenWait(1, 2_500); OfferInfo editedOffer = aliceClient.getMyOffer(originalOffer.getId()); - log.debug("EDITED BSQ OFFER:\n{}", toOfferTable.apply(editedOffer)); + log.debug("Edited BSQ offer:\n{}", toOfferTable.apply(editedOffer)); assertFalse(editedOffer.getIsActivated()); assertEquals(scaledUpAltcoinOfferPrice.apply(newFixedPriceAsString), editedOffer.getPrice()); assertFalse(editedOffer.getUseMarketBasedPrice()); @@ -576,6 +572,39 @@ public void testEditFixedPriceAndDisableBsqOffer() { assertEquals(0, editedOffer.getTriggerPrice()); } + @Test + @Order(18) + public void testEditBsqSwapOfferShouldThrowException() { + var originalOffer = aliceClient.createBsqSwapOffer(SELL.name(), + 1_250_000L, + 750_000L, + "0.00005"); + log.debug("BsqSwap Buy BSQ (Buy BTC) offer:\n{}", originalOffer); + var newOfferId = originalOffer.getId(); + assertNotEquals("", newOfferId); + assertEquals(SELL.name(), originalOffer.getDirection()); + assertEquals(5_000, originalOffer.getPrice()); + assertEquals(1_250_000L, originalOffer.getAmount()); + assertEquals(750_000L, originalOffer.getMinAmount()); + assertEquals(BSQ, originalOffer.getBaseCurrencyCode()); + assertEquals(BTC, originalOffer.getCounterCurrencyCode()); + + log.debug("Original BsqSwap offer:\n{}", toOfferTable.apply(originalOffer)); + genBtcBlocksThenWait(1, 2_500); // Wait for offer book entry. + var newFixedPrice = "0.000055"; + Throwable exception = assertThrows(StatusRuntimeException.class, () -> + aliceClient.editOffer(originalOffer.getId(), + newFixedPrice, + false, + 0.0, + 0, + ACTIVATE_OFFER, + TRIGGER_PRICE_ONLY)); + String expectedExceptionMessage = format("UNKNOWN: cannot edit bsq swap offer with id '%s'", + originalOffer.getId()); + assertEquals(expectedExceptionMessage, exception.getMessage()); + } + private OfferInfo createMktPricedOfferForEdit(String direction, String currencyCode, String paymentAccountId, From b5981b5b713e562773bceea5ccf43e8306654c65 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 27 Nov 2021 14:17:52 -0300 Subject: [PATCH 29/39] There is no --swap opt in editoffer command BSQ swap offers cannot be edited --- cli/src/main/java/bisq/cli/CliMain.java | 1 - 1 file changed, 1 deletion(-) diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index 4b4387bb4de..fc079bdd93b 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -832,7 +832,6 @@ private static void printHelp(OptionParser parser, @SuppressWarnings("SameParame stream.format(rowFormat, "", "--security-deposit= \\", ""); stream.format(rowFormat, "", "[--fee-currency=]", ""); stream.format(rowFormat, "", "[--trigger-price=]", ""); - stream.format(rowFormat, "", "[--swap=]", ""); stream.println(); stream.format(rowFormat, editoffer.name(), "--offer-id= \\", "Edit offer with id"); stream.format(rowFormat, "", "[--fixed-price=] \\", ""); From 0d8559d0d23671ef5be51a358204cc68111aaf0d Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sat, 27 Nov 2021 20:58:05 -0300 Subject: [PATCH 30/39] Force rebuild after github action ECONNRESET From 66115f3d81ee5fea41429f2852bd603b49da0fa8 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 28 Nov 2021 14:56:57 -0300 Subject: [PATCH 31/39] Revert "There is no --swap opt in editoffer command" This reverts commit b5981b5b713e562773bceea5ccf43e8306654c65. This was a mistake. There is a swap opt in the createoffer command. --- cli/src/main/java/bisq/cli/CliMain.java | 1 + 1 file changed, 1 insertion(+) diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index fc079bdd93b..4b4387bb4de 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -832,6 +832,7 @@ private static void printHelp(OptionParser parser, @SuppressWarnings("SameParame stream.format(rowFormat, "", "--security-deposit= \\", ""); stream.format(rowFormat, "", "[--fee-currency=]", ""); stream.format(rowFormat, "", "[--trigger-price=]", ""); + stream.format(rowFormat, "", "[--swap=]", ""); stream.println(); stream.format(rowFormat, editoffer.name(), "--offer-id= \\", "Edit offer with id"); stream.format(rowFormat, "", "[--fixed-price=] \\", ""); From 1b7e43a874fd632f2b7a618c6545f5da9c28c74f Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 28 Nov 2021 16:12:02 -0300 Subject: [PATCH 32/39] Add apitest/scripts/bsqswap-simulation.sh script Some refactoring and typo corrections in existing scripts too. --- apitest/scripts/bsqswap-simulation-utils.sh | 83 +++++++++++++++++++ apitest/scripts/bsqswap-simulation.sh | 90 +++++++++++++++++++++ apitest/scripts/trade-simulation-env.sh | 41 ++++++++++ apitest/scripts/trade-simulation-utils.sh | 11 ++- apitest/scripts/trade-simulation.sh | 13 ++- 5 files changed, 225 insertions(+), 13 deletions(-) create mode 100755 apitest/scripts/bsqswap-simulation-utils.sh create mode 100755 apitest/scripts/bsqswap-simulation.sh diff --git a/apitest/scripts/bsqswap-simulation-utils.sh b/apitest/scripts/bsqswap-simulation-utils.sh new file mode 100755 index 00000000000..7a6f8774014 --- /dev/null +++ b/apitest/scripts/bsqswap-simulation-utils.sh @@ -0,0 +1,83 @@ +#! /bin/bash + +# This file must be sourced by the driver script. + +source "$APITEST_SCRIPTS_HOME/trade-simulation-env.sh" +source "$APITEST_SCRIPTS_HOME/trade-simulation-utils.sh" + +gencreatebsqswapoffercommand() { + PORT="$1" + CMD="$CLI_BASE --port=$PORT createoffer" + CMD+=" --swap=true" + CMD+=" --direction=$DIRECTION" + CMD+=" --amount=$AMOUNT" + CMD+=" --fixed-price=$FIXED_PRICE" + CMD+=" --currency-code=$CURRENCY_CODE" + echo "$CMD" +} + +createbsqswapoffer() { + CREATE_OFFER_CMD="$1" + OFFER_DESC=$($CREATE_OFFER_CMD) + + # If the CLI command exited with an error, print the CLI error, and + # return from this function now, passing the error status code to the caller. + commandalert $? "Could not create offer." + + OFFER_DETAIL=$(echo -e "$OFFER_DESC" | sed -n '2p') + NEW_OFFER_ID=$(echo -e "$OFFER_DETAIL" | awk '{print $NF}') + echo "$NEW_OFFER_ID" +} + +executebsqswap() { + # Bob list available BSQ offers. (If a v1 BSQ offer is picked this simulation will break.) + printdate "Bob looking at $DIRECTION $CURRENCY_CODE offers." + CMD="$CLI_BASE --port=$BOB_PORT getoffers --direction=$DIRECTION --currency-code=$CURRENCY_CODE" + printdate "BOB CLI: $CMD" + OFFERS=$($CMD) + exitoncommandalert $? + echo "$OFFERS" + printbreak + + OFFER_ID=$(getfirstofferid "$BOB_PORT") + exitoncommandalert $? + printdate "First BSQ offer found: $OFFER_ID" + + # Take Alice's BSQ swap offer. + CMD="$CLI_BASE --port=$BOB_PORT takeoffer --offer-id=$OFFER_ID" + printdate "BOB CLI: $CMD" + TRADE=$($CMD) + commandalert $? "Could not take BSQ swap offer." + # Print the takeoffer command's console output. + printdate "$TRADE" + printbreak + + # Generate 1 btc block + printdate "Generating 1 btc block after BSQ swap execution." + genbtcblocks 1 2 + printbreak + + printdate "BSQ swap trade $OFFER_ID complete." + printbreak + + printdate "Alice looking at her trade with id $OFFER_ID." + CMD="$CLI_BASE --port=$ALICE_PORT gettrade --trade-id=$OFFER_ID" + printdate "ALICE CLI: $CMD" + GETTRADE_CMD_OUTPUT=$(gettrade "$CMD") + exitoncommandalert $? + echo "$GETTRADE_CMD_OUTPUT" + printbreak + TRADE_DETAIL=$(gettradedetail "$GETTRADE_CMD_OUTPUT") + exitoncommandalert $? + + printdate "Bob looking at his trade with id $OFFER_ID." + CMD="$CLI_BASE --port=$BOB_PORT gettrade --trade-id=$OFFER_ID" + printdate "BOB CLI: $CMD" + GETTRADE_CMD_OUTPUT=$(gettrade "$CMD") + exitoncommandalert $? + echo "$GETTRADE_CMD_OUTPUT" + printbreak + TRADE_DETAIL=$(gettradedetail "$GETTRADE_CMD_OUTPUT") + exitoncommandalert $? +} + diff --git a/apitest/scripts/bsqswap-simulation.sh b/apitest/scripts/bsqswap-simulation.sh new file mode 100755 index 00000000000..4d7c5258243 --- /dev/null +++ b/apitest/scripts/bsqswap-simulation.sh @@ -0,0 +1,90 @@ +#! /bin/bash + +# Demonstrates a bsq <-> btc swap trade using the API CLI with a local regtest bitcoin node. +# +# Prerequisites: +# +# - Linux or OSX with bash, Java 10, or Java 11-15 (JDK language compatibility 11), and bitcoin-core (v0.19, v0.20, or v0.21). +# +# - Bisq must be fully built with apitest dao setup files installed. +# Build command: `./gradlew clean build :apitest:installDaoSetup` +# +# - All supporting nodes must be run locally, in dev/dao/regtest mode: +# bitcoind, seednode, arbdaemon, alicedaemon, bobdaemon +# +# These should be run using the apitest harness. From the root project dir, run: +# `$ ./bisq-apitest --apiPassword=xyz --supportingApps=bitcoind,seednode,arbdaemon,alicedaemon,bobdaemon --shutdownAfterTests=false` +# +# Usage: +# +# This script must be run from the root of the project, e.g.: +# +# `$ apitest/scripts/bsqswap-simulation.sh -d buy -f 0.00005 -a 0.125` +# +# Script options: -d -c -f -a +# +# Examples: +# +# Create and take a bsq swap offer to buy 0.05 btc at a fixed-price of 0.00005 bsq (per 1 btc): +# +# `$ apitest/scripts/bsqswap-simulation.sh -d buy -a 0.05 -f 0.00005` +# +# Create and take a bsq swap offer to buy 1 btc at a fixed-price of 0.00005 bsq (per 1 btc): +# +# `$ apitest/scripts/bsqswap-simulation.sh -d buy -a 1 -f 0.0005` + +export APP_BASE_NAME=$(basename "$0") +export APP_HOME=$(pwd -P) +export APITEST_SCRIPTS_HOME="$APP_HOME/apitest/scripts" + +source "$APITEST_SCRIPTS_HOME/trade-simulation-env.sh" +source "$APITEST_SCRIPTS_HOME/trade-simulation-utils.sh" +source "$APITEST_SCRIPTS_HOME/bsqswap-simulation-utils.sh" + +checksetup +parsebsqswaporderopts "$@" + +printdate "Started $APP_BASE_NAME with parameters:" +printbsqswapscriptparams +printbreak + +# Alice creates a bsq swap offer. +printdate "Alice creating BSQ swap offer: $DIRECTION $AMOUNT BTC for BSQ at fixed price of $FIXED_PRICE BTC per 1 BSQ." +CMD=$(gencreatebsqswapoffercommand "$ALICE_PORT" "$ALICE_ACCT_ID") +printdate "ALICE CLI: $CMD" +OFFER_ID=$(createbsqswapoffer "$CMD") +exitoncommandalert $? +printdate "Alice created bsq swap offer with id: $OFFER_ID." +printbreak +sleeptraced 2 + +# Show Alice's new bsq swap offer. +printdate "Alice looking at her new $DIRECTION $CURRENCY_CODE offer." +CMD="$CLI_BASE --port=$ALICE_PORT getmyoffer --offer-id=$OFFER_ID" +printdate "ALICE CLI: $CMD" +OFFER=$($CMD) +exitoncommandalert $? +echo "$OFFER" +printbreak +sleeptraced 2 + +# Generate 1 btc block. +printdate "Generating 1 btc block after publishing Alice's offer." +genbtcblocks 1 1 +printbreak + +# Execute the BSQ swap. +executebsqswap +exitoncommandalert $? +printbreak + +# Get balances after trade completion. +printdate "Bob & Alice's balances after BSQ swap trade." +printdate "ALICE CLI:" +printbalances "$ALICE_PORT" +printbreak +printdate "BOB CLI:" +printbalances "$BOB_PORT" +printbreak + +exit 0 diff --git a/apitest/scripts/trade-simulation-env.sh b/apitest/scripts/trade-simulation-env.sh index 45fc385e072..e3735fe2a99 100755 --- a/apitest/scripts/trade-simulation-env.sh +++ b/apitest/scripts/trade-simulation-env.sh @@ -179,6 +179,41 @@ parselimitorderopts() { fi } +parsebsqswaporderopts() { + usage() { + echo "Usage: $0 [-d buy|sell] [-f ] [-a ]" 1>&2 + exit 1; + } + + local OPTIND o d f a + while getopts "d:f:a:" o; do + case "${o}" in + d) d=$(echo "${OPTARG}" | tr '[:lower:]' '[:upper:]') + ((d == "BUY" || d == "SELL")) || usage + export DIRECTION=${d} + ;; + f) f=${OPTARG} + export FIXED_PRICE=${f} + ;; + a) a=${OPTARG} + export AMOUNT=${a} + ;; + *) usage ;; + esac + done + shift $((OPTIND-1)) + + if [ -z "${d}" ] || [ -z "${a}" ]; then + usage + fi + + if [ -z "${f}" ] ; then + usage + fi + + export CURRENCY_CODE="BSQ" +} + checkbitcoindrunning() { # There may be a '+' char in the path and we have to escape it for pgrep. if [[ $APP_HOME == *"+"* ]]; then @@ -310,3 +345,9 @@ printscriptparams() { echo " WAIT = $WAIT" fi } + +printbsqswapscriptparams() { + echo " DIRECTION = $DIRECTION" + echo " FIXED_PRICE = $FIXED_PRICE" + echo " AMOUNT = $AMOUNT" +} diff --git a/apitest/scripts/trade-simulation-utils.sh b/apitest/scripts/trade-simulation-utils.sh index 6b146222619..4b63c3a7c5a 100755 --- a/apitest/scripts/trade-simulation-utils.sh +++ b/apitest/scripts/trade-simulation-utils.sh @@ -457,11 +457,10 @@ delayconfirmpaymentreceived() { printbreak } -# This is a large function that should be broken up if it ever makes sense to not treat a trade -# execution simulation as an bsq swap operation. But we are not testing api methods here, just -# demonstrating how to use them to get through the trade protocol. It should work for any trade -# between Bob & Alice, as long as Alice is maker, Bob is taker, and the offer to be taken is the -# first displayed in Bob's getoffers command output. +# This is a large function that might be split into smaller functions. But we are not testing +# api methods here, just demonstrating how to use them to get through the V1 trade protocol with +# the CLI. It should work for any trade between Bob & Alice, as long as Alice is maker, Bob is +# taker, and the offer to be taken is the first displayed in Bob's getoffers command output. executetrade() { # Bob list available offers. printdate "BOB $BOB_ROLE: Looking at $DIRECTION $CURRENCY_CODE offers." @@ -477,7 +476,7 @@ executetrade() { printdate "First offer found: $OFFER_ID" # Take Alice's offer. - CMD="$CLI_BASE --port=$BOB_PORT takeoffer --offer-id=$OFFER_ID --payment-account=$BOB_ACCT_ID --fee-currency=bsq" + CMD="$CLI_BASE --port=$BOB_PORT takeoffer --offer-id=$OFFER_ID --payment-account=$BOB_ACCT_ID --fee-currency=BSQ" printdate "BOB CLI: $CMD" TRADE=$($CMD) commandalert $? "Could not take offer." diff --git a/apitest/scripts/trade-simulation.sh b/apitest/scripts/trade-simulation.sh index 5aa540bf71a..004ff577820 100755 --- a/apitest/scripts/trade-simulation.sh +++ b/apitest/scripts/trade-simulation.sh @@ -1,13 +1,13 @@ #! /bin/bash -# Runs fiat <-> btc trading scenarios using the API CLI with a local regtest bitcoin node. +# Demonstrates a fiat <-> btc trade using the API CLI with a local regtest bitcoin node. # # A country code argument is used to create a country based face to face payment account for the simulated # trade, and the maker's face to face payment account's currency code is used when creating the offer. # # Prerequisites: # -# - Linux or OSX with bash, Java 10, or Java 11-12 (JDK language compatibility 10), and bitcoin-core (v0.19, v0.20, or v0.21). +# - Linux or OSX with bash, Java 10, or Java 11-15 (JDK language compatibility 11), and bitcoin-core (v0.19, v0.20, or v0.21). # # - Bisq must be fully built with apitest dao setup files installed. # Build command: `./gradlew clean build :apitest:installDaoSetup` @@ -26,15 +26,16 @@ # # `$ apitest/scripts/trade-simulation.sh -d buy -c fr -m 3.00 -a 0.125` # -# Script options: -d -c -m - f -a +# Script options: -d -c -m -f -a # # Examples: # -# Create a buy/eur offer to buy 0.125 btc at a mkt-price-margin of 0%, using an Italy face to face payment account: +# Create and take a buy/eur offer to buy 0.125 btc at a mkt-price-margin of 0%, using an Italy face to face +# payment account: # # `$ apitest/scripts/trade-simulation.sh -d buy -c it -m 0.00 -a 0.125` # -# Create a sell/eur offer to sell 0.125 btc at a fixed-price of 38,000 euros, using a France face to face +# Create and take a sell/eur offer to sell 0.125 btc at a fixed-price of 38,000 euros, using a France face to face # payment account: # # `$ apitest/scripts/trade-simulation.sh -d sell -c fr -f 38000 -a 0.125` @@ -53,8 +54,6 @@ printdate "Started $APP_BASE_NAME with parameters:" printscriptparams printbreak -registerdisputeagents - # Demonstrate how to create a country based, face to face account. showcreatepaymentacctsteps "Alice" "$ALICE_PORT" From 8b2dec7a55dc1cba49a05ceb29f22e89717bfa72 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Sun, 28 Nov 2021 16:37:41 -0300 Subject: [PATCH 33/39] Remove unused function outputs --- apitest/scripts/bsqswap-simulation-utils.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/apitest/scripts/bsqswap-simulation-utils.sh b/apitest/scripts/bsqswap-simulation-utils.sh index 7a6f8774014..d0f4787b744 100755 --- a/apitest/scripts/bsqswap-simulation-utils.sh +++ b/apitest/scripts/bsqswap-simulation-utils.sh @@ -67,8 +67,6 @@ executebsqswap() { exitoncommandalert $? echo "$GETTRADE_CMD_OUTPUT" printbreak - TRADE_DETAIL=$(gettradedetail "$GETTRADE_CMD_OUTPUT") - exitoncommandalert $? printdate "Bob looking at his trade with id $OFFER_ID." CMD="$CLI_BASE --port=$BOB_PORT gettrade --trade-id=$OFFER_ID" @@ -77,7 +75,5 @@ executebsqswap() { exitoncommandalert $? echo "$GETTRADE_CMD_OUTPUT" printbreak - TRADE_DETAIL=$(gettradedetail "$GETTRADE_CMD_OUTPUT") - exitoncommandalert $? } From 6b8925458818dbd8ffd6e53646e33283655639ce Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Mon, 29 Nov 2021 11:29:42 -0300 Subject: [PATCH 34/39] Adjust core api method help docs for bsq swaps --- .../main/resources/help/createoffer-help.txt | 62 +++++++++++++------ .../main/resources/help/takeoffer-help.txt | 18 +++++- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/core/src/main/resources/help/createoffer-help.txt b/core/src/main/resources/help/createoffer-help.txt index 38ec0fd8daf..3dfd2b1eae2 100644 --- a/core/src/main/resources/help/createoffer-help.txt +++ b/core/src/main/resources/help/createoffer-help.txt @@ -7,54 +7,76 @@ createoffer - create offer to buy or sell BTC SYNOPSIS -------- createoffer - --payment-account= - --direction= - --currency-code= - --market-price-margin= | --fixed-price= --amount= --min-amount= + --currency-code= + --direction= + --fixed-price= | --market-price-margin= + --payment-account= --security-deposit= + --swap= [--fee-currency=] DESCRIPTION ----------- -Create and place an offer to buy or sell BTC using a fiat account. +Create and place an offer to buy or sell BTC. There are two types of offers. + + BSQ swap offers + + The createoffer command requires the swap, amount [, optional min-amount], direction, + and fixed-price parameters, where --swap=true and the user's wallet contains sufficient + BTC and/or BSQ to cover the trade amount and maker fee. + + Version 1 protocol fiat and BSQ offers + + The createoffer command requires the payment-account, amount [,optional min-amount], + currency-code, direction, fixed-price or market-price-margin, and security-deposit parameters. + The fee-currency parameter can be optionally used to pay the taker fee in BSQ. OPTIONS ------- ---payment-account - The ID of the fiat payment account used to send or receive funds during the trade. +--amount + The amount of BTC to buy or sell, e.g., 0.125. ---direction - The direction of the trade (BUY or SELL). +--min-amount + The minimum amount of BTC to buy or sell, e.g., 0.006. + If --min-amount is not present, it defaults to the --amount value. --currency-code - The three letter code for the fiat used to buy or sell BTC, e.g., EUR, USD, BRL, ... + The three-letter code for the currency used to buy or sell BTC, e.g., BSQ, EUR, USD, BRL, ... ---market-price-margin - The % above or below market BTC price, e.g., 1.00 (1%). - If --market-price-margin is not present, --fixed-price must be. +--direction + The direction of the trade (BUY or SELL). + +--fee-currency + The wallet currency used to pay the Bisq trade maker fee (BSQ|BTC). Default is BTC --fixed-price The fixed BTC price in fiat used to buy or sell BTC, e.g., 34000 (USD). If --fixed-price is not present, --market-price-margin must be. ---amount - The amount of BTC to buy or sell, e.g., 0.125. +--market-price-margin + The % above or below market BTC price, e.g., 1.00 (1%). + If --market-price-margin is not present, --fixed-price must be. ---min-amount - The minimum amount of BTC to buy or sell, e.g., 0.006. - If --min-amount is not present, it defaults to the --amount value. +--payment-account + The ID of the fiat payment account used to send or receive funds during the trade. --security-deposit The percentage of the BTC amount being traded for the security deposit, e.g., 60.0 (60%). ---fee-currency - The wallet currency used to pay the Bisq trade maker fee (BSQ|BTC). Default is BTC +--swap + Flag determining whether the offer is a BSQ swap or version 1 protocol offer. Default is false. EXAMPLES -------- +To create a BUY 0.25 BTC with BSQ swap offer at a fixed BSQ price of 0.00005 BSQ per 1 BTC: +$ ./bisq-cli --password=xyz --port=9998 createoffer --swap=true \ + --direction=buy \ + --amount=0.25 \ + --fixed-price=0.00005 + To create a BUY 0.125 BTC with EUR offer at the current market price, using a payment account with ID 7413d263-225a-4f1b-837a-1e3094dc0d77, diff --git a/core/src/main/resources/help/takeoffer-help.txt b/core/src/main/resources/help/takeoffer-help.txt index 1290a008392..f0ecd6f46e6 100644 --- a/core/src/main/resources/help/takeoffer-help.txt +++ b/core/src/main/resources/help/takeoffer-help.txt @@ -9,11 +9,22 @@ SYNOPSIS takeoffer --offer-id= --payment-account= - --fee-currency= + [--fee-currency=] DESCRIPTION ----------- -Take an existing offer using a matching payment method. The Bisq trade fee can be paid in BSQ or BTC. +Take an existing offer. There are currently two types offers and trade protocols. + + BSQ swap offers + + The takeoffer command only requires an offer-id parameter, and sufficient BSQ and BTC + to cover the trade amount and the taker fee. The trade (swap) will be executed immediately + after being successfully taken. + + Version 1 protocol fiat and BSQ offers + + The offer-id and payment-account parameters are required. The fee-currency parameter can + be optionally used to pay the taker fee in BSQ. OPTIONS ------- @@ -29,6 +40,9 @@ OPTIONS EXAMPLES -------- +To take a BSQ swap offer with ID y3a8b2e2-51b6-4f39-b6c1-3ebd52c22aea; +$ ./bisq-cli --password=xyz --port=9998 takeoffer --offer-id=y3a8b2e2-51b6-4f39-b6c1-3ebd52c22aea + To take an offer with ID 83e8b2e2-51b6-4f39-a748-3ebd29c22aea using a payment account with ID fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e, and paying the Bisq trading fee in BSQ: From b1dd20572c7e38bb452d58fc602ba5aba8763661 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Mon, 29 Nov 2021 12:30:13 -0300 Subject: [PATCH 35/39] Allow unrecognized options in OfferIdOptionParser where appropriate The OfferIdOptionParser superclass reduces duplication for parsing offer-id parameters, but it needs to let subclass parsers' other options pass validation. --- cli/src/main/java/bisq/cli/CliMain.java | 2 +- cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java | 2 +- cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index 4b4387bb4de..fc77205bca7 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -463,7 +463,7 @@ public static void run(String[] args) { return; } case takeoffer: { - var offerIdOpt = new OfferIdOptionParser(args).parse(); + var offerIdOpt = new OfferIdOptionParser(args, true).parse(); if (offerIdOpt.isForHelp()) { out.println(client.getMethodHelp(method)); return; diff --git a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java index 35001553006..ee994e77786 100644 --- a/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/EditOfferOptionParser.java @@ -66,7 +66,7 @@ public class EditOfferOptionParser extends OfferIdOptionParser implements Method private EditOfferRequest.EditType offerEditType; public EditOfferOptionParser(String[] args) { - super(args); + super(args, true); } public EditOfferOptionParser parse() { diff --git a/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java b/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java index 9d424c23cec..6f848f8e413 100644 --- a/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java +++ b/cli/src/main/java/bisq/cli/opts/TakeOfferOptionParser.java @@ -33,7 +33,7 @@ public class TakeOfferOptionParser extends OfferIdOptionParser implements Method .defaultsTo("btc"); public TakeOfferOptionParser(String[] args) { - super(args); + super(args, true); } public TakeOfferOptionParser parse() { From a1db0d10b096810f21b9cb42511bf3c95c564f3e Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Mon, 29 Nov 2021 12:43:58 -0300 Subject: [PATCH 36/39] Update API docs for Bitcoin Core version v22.0 Plus some outdated JDK version compat comments. --- apitest/docs/api-beta-test-guide.md | 4 ++-- apitest/docs/build-run.md | 2 +- apitest/scripts/bsqswap-simulation.sh | 2 +- apitest/scripts/limit-order-simulation.sh | 2 +- apitest/scripts/rolling-offer-simulation.sh | 2 +- apitest/scripts/trade-simulation.sh | 2 +- apitest/src/main/java/bisq/apitest/ApiTestMain.java | 2 +- .../main/java/bisq/apitest/linux/AbstractLinuxProcess.java | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/apitest/docs/api-beta-test-guide.md b/apitest/docs/api-beta-test-guide.md index c3ff01b724e..feb0470e8a8 100644 --- a/apitest/docs/api-beta-test-guide.md +++ b/apitest/docs/api-beta-test-guide.md @@ -17,9 +17,9 @@ option adjustments to compensate. **Shell**: Bash -**Java SDK**: Version 10, 11, 12 or 15 +**Java SDK**: Version 11 - 15 (src and class generation version 11) -**Bitcoin-Core**: Version 0.19, 0.20, or 0.21 +**Bitcoin-Core**: Version 0.19 - 22 **Git Client** diff --git a/apitest/docs/build-run.md b/apitest/docs/build-run.md index ba9149e2383..d877b71f429 100644 --- a/apitest/docs/build-run.md +++ b/apitest/docs/build-run.md @@ -4,7 +4,7 @@ The Java based API runs on Linux and OSX. ## Mainnet -To build from the source, clone the github repository found at `https://github.com/bisq-network/bisq`, +To build from the source, clone the GitHub repository found at `https://github.com/bisq-network/bisq`, and build with gradle: $ ./gradlew clean build diff --git a/apitest/scripts/bsqswap-simulation.sh b/apitest/scripts/bsqswap-simulation.sh index 4d7c5258243..482a0fc40c7 100755 --- a/apitest/scripts/bsqswap-simulation.sh +++ b/apitest/scripts/bsqswap-simulation.sh @@ -4,7 +4,7 @@ # # Prerequisites: # -# - Linux or OSX with bash, Java 10, or Java 11-15 (JDK language compatibility 11), and bitcoin-core (v0.19, v0.20, or v0.21). +# - Linux or OSX with bash, Java 11-15 (JDK language compatibility 11), and bitcoin-core (v0.19 - v22). # # - Bisq must be fully built with apitest dao setup files installed. # Build command: `./gradlew clean build :apitest:installDaoSetup` diff --git a/apitest/scripts/limit-order-simulation.sh b/apitest/scripts/limit-order-simulation.sh index db37d8ab949..3ee50b63dda 100755 --- a/apitest/scripts/limit-order-simulation.sh +++ b/apitest/scripts/limit-order-simulation.sh @@ -6,7 +6,7 @@ # # Prerequisites: # -# - Linux or OSX with bash, Java 10, or Java 11-12 (JDK language compatibility 10), and bitcoin-core (v0.19, v0.20, v0.21). +# - Linux or OSX with bash, Java 11-15 (JDK language compatibility 11), and bitcoin-core (v0.19 - v22). # # - Bisq must be fully built with apitest dao setup files installed. # Build command: `./gradlew clean build :apitest:installDaoSetup` diff --git a/apitest/scripts/rolling-offer-simulation.sh b/apitest/scripts/rolling-offer-simulation.sh index c269940e646..3bbfba4eb5f 100755 --- a/apitest/scripts/rolling-offer-simulation.sh +++ b/apitest/scripts/rolling-offer-simulation.sh @@ -10,7 +10,7 @@ # # Prerequisites: # -# - Linux or OSX with bash, Java 10, or Java 11-12 (JDK language compatibility 10), and bitcoin-core (v0.19, v0.20, v0.21). +# - Linux or OSX with bash, Java 11-15 (JDK language compatibility 11), and bitcoin-core (v0.19 - v22). # # - Bisq must be fully built with apitest dao setup files installed. # Build command: `./gradlew clean build :apitest:installDaoSetup` diff --git a/apitest/scripts/trade-simulation.sh b/apitest/scripts/trade-simulation.sh index 004ff577820..0af66535aa5 100755 --- a/apitest/scripts/trade-simulation.sh +++ b/apitest/scripts/trade-simulation.sh @@ -7,7 +7,7 @@ # # Prerequisites: # -# - Linux or OSX with bash, Java 10, or Java 11-15 (JDK language compatibility 11), and bitcoin-core (v0.19, v0.20, or v0.21). +# - Linux or OSX with bash, Java 11-15 (JDK language compatibility 11), and bitcoin-core (v0.19 - v22). # # - Bisq must be fully built with apitest dao setup files installed. # Build command: `./gradlew clean build :apitest:installDaoSetup` diff --git a/apitest/src/main/java/bisq/apitest/ApiTestMain.java b/apitest/src/main/java/bisq/apitest/ApiTestMain.java index 6d50d19edd8..8ea7e0511be 100644 --- a/apitest/src/main/java/bisq/apitest/ApiTestMain.java +++ b/apitest/src/main/java/bisq/apitest/ApiTestMain.java @@ -48,7 +48,7 @@ * * All method, scenario and end-to-end tests are found in the test sources folder. * - * Requires bitcoind v0.19, v0.20, or v0.21. + * Requires bitcoind v0.19 - v22. */ @Slf4j public class ApiTestMain { diff --git a/apitest/src/main/java/bisq/apitest/linux/AbstractLinuxProcess.java b/apitest/src/main/java/bisq/apitest/linux/AbstractLinuxProcess.java index 83ae6718ae6..9af3bfce379 100644 --- a/apitest/src/main/java/bisq/apitest/linux/AbstractLinuxProcess.java +++ b/apitest/src/main/java/bisq/apitest/linux/AbstractLinuxProcess.java @@ -108,7 +108,7 @@ public void verifyBitcoinPathsExist(boolean verbose) { File bitcoindExecutable = Paths.get(config.bitcoinPath, "bitcoind").toFile(); if (!bitcoindExecutable.exists() || !bitcoindExecutable.canExecute()) throw new IllegalStateException(format("'%s' cannot be found or executed.%n" - + "A bitcoin-core v0.19, v0.20, or v0.21 installation is required," + + + "A bitcoin-core v0.19 - v22 installation is required," + " and the 'bitcoinPath' must be configured in 'apitest.properties'", bitcoindExecutable.getAbsolutePath())); From 3015554f9aae30010bd20c507c0980578d4d0ed7 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Mon, 29 Nov 2021 13:14:56 -0300 Subject: [PATCH 37/39] Adjust API beta-test guide for BSQ swaps --- apitest/docs/api-beta-test-guide.md | 31 ++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/apitest/docs/api-beta-test-guide.md b/apitest/docs/api-beta-test-guide.md index feb0470e8a8..6ed00c69901 100644 --- a/apitest/docs/api-beta-test-guide.md +++ b/apitest/docs/api-beta-test-guide.md @@ -372,6 +372,21 @@ The `trade-simulation.sh` script options that would generate the previous `creat $ apitest/scripts/trade-simulation.sh -d sell -c jp -m 0.5 -a 0.125 ``` +The `createoffer` command can also be used to create BSQ swap offers, where trade execution is performed immediately +after a BSQ swap offer is taken. To swap 0.5 BTC for BSQ at a price of 0.00005 BSQ per 1 BTC: +``` +$ ./bisq-cli --password=xyz --port=9998 createoffer \ + --swap=true \ + --direction=BUY \ + --amount=0.5 \ + --fixed-price=0.00005 +``` + +The `bsqswap-simulation.sh` script options that would generate the previous `createoffer` example is: +``` +$ apitest/scripts/bsqswap-simulation.sh -d buy -a 0.5 -f 0.00005 +``` + ### Browsing Your Own Offers There are different commands to browse available offers you can take, and offers you created. @@ -530,7 +545,7 @@ A CLI user browses available offers with the getoffers command. For example, th $ ./bisq-cli --password=xyz --port=9998 getoffers --direction=SELL --currency-code=EUR ``` -And takes one of the available offers with an EUR payment account ( id `fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e`) +Then takes one of the available offers with an EUR payment account ( id `fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e`) with the `takeoffer` command: ``` $ ./bisq-cli --password=xyz --port=9998 takeoffer \ @@ -538,8 +553,10 @@ $ ./bisq-cli --password=xyz --port=9998 takeoffer \ --payment-account=fe20cdbd-22be-4b8a-a4b6-d2608ff09d6e \ --fee-currency=btc ``` -The taken offer will be used to create a trade contract. The next section describes how to use the Api to execute -the trade. +Depending on the offer type, the taken offer will be used to (1) create a trade contract, or (2) execute a BSQ swap. + +The next section describes how to use the Api to execute a trade. The following Completing a BSQ Swap Trade +section explains how to use the `takeoffer` command to complete a BSQ swap. ### Completing Trade Protocol @@ -598,6 +615,14 @@ $ ./bisq-cli --password=xyz --port=9998 keepfunds --trade-id= $ ./bisq-cli --password=xyz --port=9999 withdrawfunds --trade-id= --address= [--memo=<"memo">] ``` +### Completing a BSQ Swap Trade + +The `takeoffer` command will immediately perform the BSQ swap, assuming both sides' wallets have sufficient BTC and +BSQ to cover the trade amount, and maker or taker fee. It takes one argument: `--offer-id=`: +``` +$ ./bisq-cli --password=xyz --port=9998 takeoffer --offer-id=Xge8b2e2-51b6-3TOOB-z748-3ebd29c2kj99 +``` + ## Shutting Down Test Harness The test harness should cleanly shutdown all the background apps in proper order after entering ^C. From bb68605c69127a005679e657a3c1cea76c5b0292 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Fri, 3 Dec 2021 12:13:33 -0300 Subject: [PATCH 38/39] Add missing --currency-code=bsq param to createoffer example Resolves issue mentioned in https://github.com/bisq-network/bisq/pull/5876#pullrequestreview-822692137 --- apitest/docs/api-beta-test-guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/apitest/docs/api-beta-test-guide.md b/apitest/docs/api-beta-test-guide.md index 6ed00c69901..5eb57131f02 100644 --- a/apitest/docs/api-beta-test-guide.md +++ b/apitest/docs/api-beta-test-guide.md @@ -379,6 +379,7 @@ $ ./bisq-cli --password=xyz --port=9998 createoffer \ --swap=true \ --direction=BUY \ --amount=0.5 \ + --currency-code=BSQ \ --fixed-price=0.00005 ``` From f195b76637ec2945d9479491306425a5631441f8 Mon Sep 17 00:00:00 2001 From: ghubstan <36207203+ghubstan@users.noreply.github.com> Date: Fri, 3 Dec 2021 12:47:03 -0300 Subject: [PATCH 39/39] Tell user bsqswap offer cannot be edited, but can be canceled at no charge Resolves issue described in https://github.com/bisq-network/bisq/pull/5876#pullrequestreview-822692137 --- cli/src/main/java/bisq/cli/CliMain.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cli/src/main/java/bisq/cli/CliMain.java b/cli/src/main/java/bisq/cli/CliMain.java index fc77205bca7..5689fbdad0a 100644 --- a/cli/src/main/java/bisq/cli/CliMain.java +++ b/cli/src/main/java/bisq/cli/CliMain.java @@ -378,7 +378,8 @@ public static void run(String[] args) { var offerId = offerIdOpt.getOfferId(); var offerCategory = client.getMyOfferCategory(offerId); if (offerCategory.equals(BSQ_SWAP)) - throw new IllegalStateException("cannot edit swap bsq offers"); + throw new IllegalStateException("bsq swap offers cannot be edited," + + " but you may cancel them without forfeiting any funds"); var opts = new EditOfferOptionParser(args).parse(); var fixedPrice = opts.getFixedPrice();