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.
This commit is contained in:
ghubstan 2021-11-27 14:01:14 -03:00
parent 05d1916ae9
commit 64f228d872
No known key found for this signature in database
GPG Key ID: E35592D6800A861E
6 changed files with 82 additions and 53 deletions

View File

@ -120,16 +120,16 @@ public class CoreApi {
// Offers // Offers
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public boolean isAvailableFiatOffer(String id) { public boolean isFiatOffer(String id, boolean isMyOffer) {
return coreOffersService.isAvailableFiatOffer(id); return coreOffersService.isFiatOffer(id, isMyOffer);
} }
public boolean isAvailableAltcoinOffer(String id) { public boolean isAltcoinOffer(String id, boolean isMyOffer) {
return coreOffersService.isAvailableAltcoinOffer(id); return coreOffersService.isAltcoinOffer(id, isMyOffer);
} }
public boolean isAvailableBsqSwapOffer(String id) { public boolean isBsqSwapOffer(String id, boolean isMyOffer) {
return coreOffersService.isAvailableBsqSwapOffer(id); return coreOffersService.isBsqSwapOffer(id, isMyOffer);
} }
public Offer getBsqSwapOffer(String id) { public Offer getBsqSwapOffer(String id) {

View File

@ -48,6 +48,8 @@ import java.math.BigDecimal;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Supplier; import java.util.function.Supplier;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -61,8 +63,6 @@ import static bisq.core.locale.CurrencyUtil.isCryptoCurrency;
import static bisq.core.offer.Offer.State; import static bisq.core.offer.Offer.State;
import static bisq.core.offer.OfferDirection.BUY; import static bisq.core.offer.OfferDirection.BUY;
import static bisq.core.offer.OfferUtil.getRandomOfferId; 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.AVAILABLE;
import static bisq.core.offer.OpenOffer.State.DEACTIVATED; import static bisq.core.offer.OpenOffer.State.DEACTIVATED;
import static bisq.core.payment.PaymentAccountUtil.isPaymentAccountValidForOffer; import static bisq.core.payment.PaymentAccountUtil.isPaymentAccountValidForOffer;
@ -81,6 +81,9 @@ class CoreOffersService {
private final Supplier<Comparator<OpenOffer>> openOfferPriceComparator = () -> private final Supplier<Comparator<OpenOffer>> openOfferPriceComparator = () ->
comparing(openOffer -> openOffer.getOffer().getPrice()); comparing(openOffer -> openOffer.getOffer().getPrice());
private final BiFunction<String, Boolean, Offer> toOfferWithId = (id, isMyOffer) ->
isMyOffer ? getMyOffer(id).getOffer() : getOffer(id);
private final CoreContext coreContext; private final CoreContext coreContext;
private final KeyRing keyRing; private final KeyRing keyRing;
// Dependencies on core api services in this package must be kept to an absolute // Dependencies on core api services in this package must be kept to an absolute
@ -121,63 +124,48 @@ class CoreOffersService {
this.user = user; this.user = user;
} }
boolean isAvailableFiatOffer(String id) { boolean isFiatOffer(String id, boolean isMyOffer) {
return isFiatOffer(getOffer(id)); var offer = toOfferWithId.apply(id, isMyOffer);
return OfferUtil.isFiatOffer(offer);
} }
boolean isAvailableAltcoinOffer(String id) { boolean isAltcoinOffer(String id, boolean isMyOffer) {
return isAltcoinOffer(getOffer(id)); var offer = toOfferWithId.apply(id, isMyOffer);
return OfferUtil.isAltcoinOffer(offer);
} }
boolean isAvailableBsqSwapOffer(String id) { boolean isBsqSwapOffer(String id, boolean isMyOffer) {
var offer = getOffer(id); var offer = toOfferWithId.apply(id, isMyOffer);
return offer.isBsqSwapOffer(); 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) { Offer getOffer(String id) {
return offerBookService.getOffers().stream() return findAvailableOffer(id).orElseThrow(() ->
.filter(o -> o.getId().equals(id)) new IllegalStateException(format("offer with id '%s' not found", 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)));
} }
OpenOffer getMyOffer(String id) { OpenOffer getMyOffer(String id) {
return openOfferManager.getObservableList().stream() return findMyOpenOffer(id).orElseThrow(() ->
.filter(o -> o.getId().equals(id)) new IllegalStateException(format("offer with id '%s' not found", id)));
.filter(o -> o.getOffer().isMyOffer(keyRing)) }
.findAny().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) { Offer getMyBsqSwapOffer(String id) {
return offerBookService.getOffers().stream() return findMyBsqSwapOffer(id).orElseThrow(() ->
.filter(o -> o.getId().equals(id)) new IllegalStateException(format("offer with id '%s' not found", id)));
.filter(o -> o.isMyOffer(keyRing))
.filter(o -> o.isBsqSwapOffer())
.findAny().orElseThrow(() ->
new IllegalStateException(format("offer with id '%s' not found", id)));
} }
List<Offer> getBsqSwapOffers(String direction) { List<Offer> getBsqSwapOffers(String direction) {
var offers = offerBookService.getOffers().stream() return offerBookService.getOffers().stream()
.filter(o -> !o.isMyOffer(keyRing)) .filter(o -> !o.isMyOffer(keyRing))
.filter(o -> o.getDirection().name().equalsIgnoreCase(direction)) .filter(o -> o.getDirection().name().equalsIgnoreCase(direction))
.filter(o -> o.isBsqSwapOffer()) .filter(Offer::isBsqSwapOffer)
.sorted(priceComparator(direction)) .sorted(priceComparator(direction))
.collect(Collectors.toList()); .collect(Collectors.toList());
return offers;
} }
List<Offer> getOffers(String direction, String currencyCode) { List<Offer> getOffers(String direction, String currencyCode) {
@ -198,13 +186,12 @@ class CoreOffersService {
} }
List<Offer> getMyBsqSwapOffers(String direction) { List<Offer> getMyBsqSwapOffers(String direction) {
var offers = offerBookService.getOffers().stream() return offerBookService.getOffers().stream()
.filter(o -> o.isMyOffer(keyRing)) .filter(o -> o.isMyOffer(keyRing))
.filter(o -> o.getDirection().name().equalsIgnoreCase(direction)) .filter(o -> o.getDirection().name().equalsIgnoreCase(direction))
.filter(Offer::isBsqSwapOffer) .filter(Offer::isBsqSwapOffer)
.sorted(priceComparator(direction)) .sorted(priceComparator(direction))
.collect(Collectors.toList()); .collect(Collectors.toList());
return offers;
} }
OpenOffer getMyOpenBsqSwapOffer(String id) { OpenOffer getMyOpenBsqSwapOffer(String id) {
@ -393,6 +380,38 @@ class CoreOffersService {
throw new IllegalStateException(offer.getErrorMessage()); throw new IllegalStateException(offer.getErrorMessage());
} }
private Optional<Offer> 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<Offer> findMyBsqSwapOffer(String id) {
return offerBookService.getOffers().stream()
.filter(o -> o.getId().equals(id))
.filter(o -> o.isMyOffer(keyRing))
.filter(Offer::isBsqSwapOffer)
.findAny();
}
private Optional<Offer> 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<OpenOffer> 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, private OfferPayload getMergedOfferPayload(OpenOffer openOffer,
String editedPriceAsString, String editedPriceAsString,
double editedMarketPriceMargin, double editedMarketPriceMargin,

View File

@ -45,7 +45,8 @@ class EditOfferValidator {
} }
void validate() { void validate() {
log.info("Verifying 'editoffer' params OK for editType {}", editType); log.info("Verifying 'editoffer' params for editType {}", editType);
checkNotBsqSwapOffer();
switch (editType) { switch (editType) {
case ACTIVATION_STATE_ONLY: { case ACTIVATION_STATE_ONLY: {
validateEditedActivationState(); validateEditedActivationState();
@ -138,4 +139,11 @@ class EditOfferValidator {
currentlyOpenOffer.getId())); currentlyOpenOffer.getId()));
} }
} }
private void checkNotBsqSwapOffer() {
if (currentlyOpenOffer.getOffer().isBsqSwapOffer()) {
throw new IllegalStateException(
format("cannot edit bsq swap offer with id '%s'", currentlyOpenOffer.getId()));
}
}
} }

View File

@ -92,7 +92,7 @@ class GrpcOffersService extends OffersImplBase {
public void getOfferCategory(GetOfferCategoryRequest req, public void getOfferCategory(GetOfferCategoryRequest req,
StreamObserver<GetOfferCategoryReply> responseObserver) { StreamObserver<GetOfferCategoryReply> responseObserver) {
try { try {
OfferCategory category = getAvailableOfferCategory(req.getId()); OfferCategory category = getOfferCategory(req.getId(), req.getIsMyOffer());
var reply = newBuilder() var reply = newBuilder()
.setOfferCategory(category) .setOfferCategory(category)
.build(); .build();
@ -335,6 +335,7 @@ class GrpcOffersService extends OffersImplBase {
return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass()) return getCustomRateMeteringInterceptor(coreApi.getConfig().appDataDir, this.getClass())
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf( .or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
new HashMap<>() {{ new HashMap<>() {{
put(getGetOfferCategoryMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getGetOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getGetOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getGetMyOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getGetMyOfferMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
put(getGetOffersMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS)); put(getGetOffersMethod().getFullMethodName(), new GrpcCallRateMeter(1, SECONDS));
@ -346,12 +347,12 @@ class GrpcOffersService extends OffersImplBase {
))); )));
} }
private OfferCategory getAvailableOfferCategory(String offerId) { private OfferCategory getOfferCategory(String offerId, boolean isMyOffer) {
if (coreApi.isAvailableAltcoinOffer(offerId)) if (coreApi.isAltcoinOffer(offerId, isMyOffer))
return ALTCOIN; return ALTCOIN;
else if (coreApi.isAvailableFiatOffer(offerId)) else if (coreApi.isFiatOffer(offerId, isMyOffer))
return FIAT; return FIAT;
else if (coreApi.isAvailableBsqSwapOffer(offerId)) else if (coreApi.isBsqSwapOffer(offerId, isMyOffer))
return BSQ_SWAP; return BSQ_SWAP;
else else
return UNKNOWN; return UNKNOWN;

View File

@ -79,7 +79,7 @@ class GrpcTradesService extends TradesImplBase {
exceptionHandler, exceptionHandler,
log); log);
if (coreApi.isAvailableBsqSwapOffer(req.getOfferId())) { if (coreApi.isBsqSwapOffer(req.getOfferId(), false)) {
coreApi.takeBsqSwapOffer(req.getOfferId(), coreApi.takeBsqSwapOffer(req.getOfferId(),
bsqSwapTrade -> { bsqSwapTrade -> {
var reply = buildTakeOfferReply(bsqSwapTrade); var reply = buildTakeOfferReply(bsqSwapTrade);

View File

@ -92,6 +92,7 @@ service Offers {
message GetOfferCategoryRequest { message GetOfferCategoryRequest {
string id = 1; string id = 1;
bool isMyOffer = 2;
} }
message GetOfferCategoryReply { message GetOfferCategoryReply {