offer book view checks for invalid signer or signature
merge OfferFilter into OfferFilterService
This commit is contained in:
parent
b95c689190
commit
a51aeeb484
@ -24,8 +24,8 @@ import bisq.core.offer.CreateOfferService;
|
|||||||
import bisq.core.offer.Offer;
|
import bisq.core.offer.Offer;
|
||||||
import bisq.core.offer.OfferBookService;
|
import bisq.core.offer.OfferBookService;
|
||||||
import bisq.core.offer.OfferDirection;
|
import bisq.core.offer.OfferDirection;
|
||||||
import bisq.core.offer.OfferFilter;
|
import bisq.core.offer.OfferFilterService;
|
||||||
import bisq.core.offer.OfferFilter.Result;
|
import bisq.core.offer.OfferFilterService.Result;
|
||||||
import bisq.core.offer.OfferUtil;
|
import bisq.core.offer.OfferUtil;
|
||||||
import bisq.core.offer.OpenOffer;
|
import bisq.core.offer.OpenOffer;
|
||||||
import bisq.core.offer.OpenOfferManager;
|
import bisq.core.offer.OpenOfferManager;
|
||||||
@ -65,7 +65,7 @@ import static java.util.Comparator.comparing;
|
|||||||
|
|
||||||
@Singleton
|
@Singleton
|
||||||
@Slf4j
|
@Slf4j
|
||||||
class CoreOffersService {
|
public class CoreOffersService {
|
||||||
|
|
||||||
private final Supplier<Comparator<Offer>> priceComparator = () -> comparing(Offer::getPrice);
|
private final Supplier<Comparator<Offer>> priceComparator = () -> comparing(Offer::getPrice);
|
||||||
private final Supplier<Comparator<Offer>> reversePriceComparator = () -> comparing(Offer::getPrice).reversed();
|
private final Supplier<Comparator<Offer>> reversePriceComparator = () -> comparing(Offer::getPrice).reversed();
|
||||||
@ -78,7 +78,7 @@ class CoreOffersService {
|
|||||||
private final CoreWalletsService coreWalletsService;
|
private final CoreWalletsService coreWalletsService;
|
||||||
private final CreateOfferService createOfferService;
|
private final CreateOfferService createOfferService;
|
||||||
private final OfferBookService offerBookService;
|
private final OfferBookService offerBookService;
|
||||||
private final OfferFilter offerFilter;
|
private final OfferFilterService offerFilter;
|
||||||
private final OpenOfferManager openOfferManager;
|
private final OpenOfferManager openOfferManager;
|
||||||
private final User user;
|
private final User user;
|
||||||
private final XmrWalletService xmrWalletService;
|
private final XmrWalletService xmrWalletService;
|
||||||
@ -89,7 +89,7 @@ class CoreOffersService {
|
|||||||
CoreWalletsService coreWalletsService,
|
CoreWalletsService coreWalletsService,
|
||||||
CreateOfferService createOfferService,
|
CreateOfferService createOfferService,
|
||||||
OfferBookService offerBookService,
|
OfferBookService offerBookService,
|
||||||
OfferFilter offerFilter,
|
OfferFilterService offerFilter,
|
||||||
OpenOfferManager openOfferManager,
|
OpenOfferManager openOfferManager,
|
||||||
OfferUtil offerUtil,
|
OfferUtil offerUtil,
|
||||||
User user,
|
User user,
|
||||||
|
@ -1,225 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Haveno.
|
|
||||||
*
|
|
||||||
* Haveno 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.
|
|
||||||
*
|
|
||||||
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package bisq.core.offer;
|
|
||||||
|
|
||||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
|
||||||
import bisq.core.filter.FilterManager;
|
|
||||||
import bisq.core.locale.CurrencyUtil;
|
|
||||||
import bisq.core.payment.PaymentAccount;
|
|
||||||
import bisq.core.payment.PaymentAccountUtil;
|
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
|
||||||
import bisq.core.trade.TradeUtils;
|
|
||||||
import bisq.core.user.Preferences;
|
|
||||||
import bisq.core.user.User;
|
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Singleton;
|
|
||||||
|
|
||||||
import javafx.collections.SetChangeListener;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
|
|
||||||
@Slf4j
|
|
||||||
@Singleton
|
|
||||||
public class OfferFilter {
|
|
||||||
private final User user;
|
|
||||||
private final Preferences preferences;
|
|
||||||
private final FilterManager filterManager;
|
|
||||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
|
||||||
private final Map<String, Boolean> insufficientCounterpartyTradeLimitCache = new HashMap<>();
|
|
||||||
private final Map<String, Boolean> myInsufficientTradeLimitCache = new HashMap<>();
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public OfferFilter(User user,
|
|
||||||
Preferences preferences,
|
|
||||||
FilterManager filterManager,
|
|
||||||
AccountAgeWitnessService accountAgeWitnessService) {
|
|
||||||
this.user = user;
|
|
||||||
this.preferences = preferences;
|
|
||||||
this.filterManager = filterManager;
|
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
|
||||||
|
|
||||||
if (user != null && user.getPaymentAccountsAsObservable() != null) {
|
|
||||||
// If our accounts have changed we reset our myInsufficientTradeLimitCache as it depends on account data
|
|
||||||
user.getPaymentAccountsAsObservable().addListener((SetChangeListener<PaymentAccount>) c ->
|
|
||||||
myInsufficientTradeLimitCache.clear());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Result {
|
|
||||||
VALID(true),
|
|
||||||
API_DISABLED,
|
|
||||||
HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER,
|
|
||||||
HAS_NOT_SAME_PROTOCOL_VERSION,
|
|
||||||
IS_IGNORED,
|
|
||||||
IS_OFFER_BANNED,
|
|
||||||
IS_CURRENCY_BANNED,
|
|
||||||
IS_PAYMENT_METHOD_BANNED,
|
|
||||||
IS_NODE_ADDRESS_BANNED,
|
|
||||||
REQUIRE_UPDATE_TO_NEW_VERSION,
|
|
||||||
IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT,
|
|
||||||
IS_MY_INSUFFICIENT_TRADE_LIMIT,
|
|
||||||
SIGNATURE_NOT_VALIDATED;
|
|
||||||
|
|
||||||
@Getter
|
|
||||||
private final boolean isValid;
|
|
||||||
|
|
||||||
Result(boolean isValid) {
|
|
||||||
this.isValid = isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
Result() {
|
|
||||||
this(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Result canTakeOffer(Offer offer, boolean isTakerApiUser) {
|
|
||||||
if (isTakerApiUser && filterManager.getFilter() != null && filterManager.getFilter().isDisableApi()) {
|
|
||||||
return Result.API_DISABLED;
|
|
||||||
}
|
|
||||||
if (!hasSameProtocolVersion(offer)) {
|
|
||||||
return Result.HAS_NOT_SAME_PROTOCOL_VERSION;
|
|
||||||
}
|
|
||||||
if (isIgnored(offer)) {
|
|
||||||
return Result.IS_IGNORED;
|
|
||||||
}
|
|
||||||
if (isOfferBanned(offer)) {
|
|
||||||
return Result.IS_OFFER_BANNED;
|
|
||||||
}
|
|
||||||
if (isCurrencyBanned(offer)) {
|
|
||||||
return Result.IS_CURRENCY_BANNED;
|
|
||||||
}
|
|
||||||
if (isPaymentMethodBanned(offer)) {
|
|
||||||
return Result.IS_PAYMENT_METHOD_BANNED;
|
|
||||||
}
|
|
||||||
if (isNodeAddressBanned(offer)) {
|
|
||||||
return Result.IS_NODE_ADDRESS_BANNED;
|
|
||||||
}
|
|
||||||
if (requireUpdateToNewVersion()) {
|
|
||||||
return Result.REQUIRE_UPDATE_TO_NEW_VERSION;
|
|
||||||
}
|
|
||||||
if (isInsufficientCounterpartyTradeLimit(offer)) {
|
|
||||||
return Result.IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT;
|
|
||||||
}
|
|
||||||
if (isMyInsufficientTradeLimit(offer)) {
|
|
||||||
return Result.IS_MY_INSUFFICIENT_TRADE_LIMIT;
|
|
||||||
}
|
|
||||||
if (!hasValidSignature(offer)) {
|
|
||||||
return Result.SIGNATURE_NOT_VALIDATED; // TODO (woodser): handle this wherever IS_MY_INSUFFICIENT_TRADE_LIMIT is handled
|
|
||||||
}
|
|
||||||
if (!isAnyPaymentAccountValidForOffer(offer)) {
|
|
||||||
return Result.HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result.VALID;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isAnyPaymentAccountValidForOffer(Offer offer) {
|
|
||||||
return user.getPaymentAccounts() != null &&
|
|
||||||
PaymentAccountUtil.isAnyPaymentAccountValidForOffer(offer, user.getPaymentAccounts());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasSameProtocolVersion(Offer offer) {
|
|
||||||
return offer.getProtocolVersion() == Version.TRADE_PROTOCOL_VERSION;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isIgnored(Offer offer) {
|
|
||||||
return preferences.getIgnoreTradersList().stream()
|
|
||||||
.anyMatch(i -> i.equals(offer.getMakerNodeAddress().getFullAddress()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isOfferBanned(Offer offer) {
|
|
||||||
return filterManager.isOfferIdBanned(offer.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isCurrencyBanned(Offer offer) {
|
|
||||||
return filterManager.isCurrencyBanned(offer.getCurrencyCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isPaymentMethodBanned(Offer offer) {
|
|
||||||
return filterManager.isPaymentMethodBanned(offer.getPaymentMethod());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isNodeAddressBanned(Offer offer) {
|
|
||||||
return filterManager.isNodeAddressBanned(offer.getMakerNodeAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean requireUpdateToNewVersion() {
|
|
||||||
return filterManager.requireUpdateToNewVersionForTrading();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This call is a bit expensive so we cache results
|
|
||||||
public boolean isInsufficientCounterpartyTradeLimit(Offer offer) {
|
|
||||||
String offerId = offer.getId();
|
|
||||||
if (insufficientCounterpartyTradeLimitCache.containsKey(offerId)) {
|
|
||||||
return insufficientCounterpartyTradeLimitCache.get(offerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
|
|
||||||
!accountAgeWitnessService.verifyPeersTradeAmount(offer, offer.getAmount(),
|
|
||||||
errorMessage -> {
|
|
||||||
});
|
|
||||||
insufficientCounterpartyTradeLimitCache.put(offerId, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This call is a bit expensive so we cache results
|
|
||||||
public boolean isMyInsufficientTradeLimit(Offer offer) {
|
|
||||||
String offerId = offer.getId();
|
|
||||||
if (myInsufficientTradeLimitCache.containsKey(offerId)) {
|
|
||||||
return myInsufficientTradeLimitCache.get(offerId);
|
|
||||||
}
|
|
||||||
|
|
||||||
Optional<PaymentAccount> accountOptional = PaymentAccountUtil.getMostMaturePaymentAccountForOffer(offer,
|
|
||||||
user.getPaymentAccounts(),
|
|
||||||
accountAgeWitnessService);
|
|
||||||
long myTradeLimit = accountOptional
|
|
||||||
.map(paymentAccount -> accountAgeWitnessService.getMyTradeLimit(paymentAccount,
|
|
||||||
offer.getCurrencyCode(), offer.getMirroredDirection()))
|
|
||||||
.orElse(0L);
|
|
||||||
long offerMinAmount = offer.getMinAmount().value;
|
|
||||||
log.debug("isInsufficientTradeLimit accountOptional={}, myTradeLimit={}, offerMinAmount={}, ",
|
|
||||||
accountOptional.isPresent() ? accountOptional.get().getAccountName() : "null",
|
|
||||||
Coin.valueOf(myTradeLimit).toFriendlyString(),
|
|
||||||
Coin.valueOf(offerMinAmount).toFriendlyString());
|
|
||||||
boolean result = CurrencyUtil.isFiatCurrency(offer.getCurrencyCode()) &&
|
|
||||||
accountOptional.isPresent() &&
|
|
||||||
myTradeLimit < offerMinAmount;
|
|
||||||
myInsufficientTradeLimitCache.put(offerId, result);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasValidSignature(Offer offer) {
|
|
||||||
|
|
||||||
// get arbitrator
|
|
||||||
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner());
|
|
||||||
if (arbitrator == null) return false; // invalid arbitrator
|
|
||||||
|
|
||||||
// validate arbitrator signature
|
|
||||||
return TradeUtils.isArbitratorSignatureValid(offer.getOfferPayload(), arbitrator);
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,10 +21,11 @@ import bisq.core.account.witness.AccountAgeWitnessService;
|
|||||||
import bisq.core.filter.FilterManager;
|
import bisq.core.filter.FilterManager;
|
||||||
import bisq.core.payment.PaymentAccount;
|
import bisq.core.payment.PaymentAccount;
|
||||||
import bisq.core.payment.PaymentAccountUtil;
|
import bisq.core.payment.PaymentAccountUtil;
|
||||||
|
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
|
import bisq.core.trade.TradeUtils;
|
||||||
import bisq.core.user.Preferences;
|
import bisq.core.user.Preferences;
|
||||||
import bisq.core.user.User;
|
import bisq.core.user.User;
|
||||||
|
|
||||||
import bisq.common.app.DevEnv;
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
@ -61,7 +62,7 @@ public class OfferFilterService {
|
|||||||
this.filterManager = filterManager;
|
this.filterManager = filterManager;
|
||||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||||
|
|
||||||
if (user != null) {
|
if (user != null && user.getPaymentAccountsAsObservable() != null) {
|
||||||
// If our accounts have changed we reset our myInsufficientTradeLimitCache as it depends on account data
|
// If our accounts have changed we reset our myInsufficientTradeLimitCache as it depends on account data
|
||||||
user.getPaymentAccountsAsObservable().addListener((SetChangeListener<PaymentAccount>) c ->
|
user.getPaymentAccountsAsObservable().addListener((SetChangeListener<PaymentAccount>) c ->
|
||||||
myInsufficientTradeLimitCache.clear());
|
myInsufficientTradeLimitCache.clear());
|
||||||
@ -81,7 +82,8 @@ public class OfferFilterService {
|
|||||||
REQUIRE_UPDATE_TO_NEW_VERSION,
|
REQUIRE_UPDATE_TO_NEW_VERSION,
|
||||||
IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT,
|
IS_INSUFFICIENT_COUNTERPARTY_TRADE_LIMIT,
|
||||||
IS_MY_INSUFFICIENT_TRADE_LIMIT,
|
IS_MY_INSUFFICIENT_TRADE_LIMIT,
|
||||||
HIDE_BSQ_SWAPS_DUE_DAO_DEACTIVATED;
|
ARBITRATOR_NOT_VALIDATED,
|
||||||
|
SIGNATURE_NOT_VALIDATED;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
private final boolean isValid;
|
private final boolean isValid;
|
||||||
@ -99,9 +101,6 @@ public class OfferFilterService {
|
|||||||
if (isTakerApiUser && filterManager.getFilter() != null && filterManager.getFilter().isDisableApi()) {
|
if (isTakerApiUser && filterManager.getFilter() != null && filterManager.getFilter().isDisableApi()) {
|
||||||
return Result.API_DISABLED;
|
return Result.API_DISABLED;
|
||||||
}
|
}
|
||||||
if (!isAnyPaymentAccountValidForOffer(offer)) {
|
|
||||||
return Result.HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER;
|
|
||||||
}
|
|
||||||
if (!hasSameProtocolVersion(offer)) {
|
if (!hasSameProtocolVersion(offer)) {
|
||||||
return Result.HAS_NOT_SAME_PROTOCOL_VERSION;
|
return Result.HAS_NOT_SAME_PROTOCOL_VERSION;
|
||||||
}
|
}
|
||||||
@ -129,6 +128,15 @@ public class OfferFilterService {
|
|||||||
if (isMyInsufficientTradeLimit(offer)) {
|
if (isMyInsufficientTradeLimit(offer)) {
|
||||||
return Result.IS_MY_INSUFFICIENT_TRADE_LIMIT;
|
return Result.IS_MY_INSUFFICIENT_TRADE_LIMIT;
|
||||||
}
|
}
|
||||||
|
if (!hasValidArbitrator(offer)) {
|
||||||
|
return Result.ARBITRATOR_NOT_VALIDATED;
|
||||||
|
}
|
||||||
|
if (!hasValidSignature(offer)) {
|
||||||
|
return Result.SIGNATURE_NOT_VALIDATED;
|
||||||
|
}
|
||||||
|
if (!isAnyPaymentAccountValidForOffer(offer)) {
|
||||||
|
return Result.HAS_NO_PAYMENT_ACCOUNT_VALID_FOR_OFFER;
|
||||||
|
}
|
||||||
|
|
||||||
return Result.VALID;
|
return Result.VALID;
|
||||||
}
|
}
|
||||||
@ -207,4 +215,15 @@ public class OfferFilterService {
|
|||||||
myInsufficientTradeLimitCache.put(offerId, result);
|
myInsufficientTradeLimitCache.put(offerId, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasValidArbitrator(Offer offer) {
|
||||||
|
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner());
|
||||||
|
return arbitrator != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasValidSignature(Offer offer) {
|
||||||
|
Arbitrator arbitrator = user.getAcceptedArbitratorByAddress(offer.getOfferPayload().getArbitratorSigner());
|
||||||
|
if (arbitrator == null) return false; // invalid arbitrator
|
||||||
|
return TradeUtils.isArbitratorSignatureValid(offer.getOfferPayload(), arbitrator);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -90,9 +90,7 @@ public class TradeUtils {
|
|||||||
// verify arbitrator signature
|
// verify arbitrator signature
|
||||||
boolean isValid = true;
|
boolean isValid = true;
|
||||||
try {
|
try {
|
||||||
isValid = Sig.verify(arbitrator.getPubKeyRing().getSignaturePubKey(), // TODO (woodser): assign isValid
|
isValid = Sig.verify(arbitrator.getPubKeyRing().getSignaturePubKey(), unsignedOfferAsJson, signature);
|
||||||
unsignedOfferAsJson,
|
|
||||||
signature);
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
isValid = false;
|
isValid = false;
|
||||||
}
|
}
|
||||||
|
@ -415,6 +415,9 @@ offerbook.warning.requireUpdateToNewVersion=Your version of Haveno is not compat
|
|||||||
offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. \
|
offerbook.warning.offerWasAlreadyUsedInTrade=You cannot take this offer because you already took it earlier. \
|
||||||
It could be that your previous take-offer attempt resulted in a failed trade.
|
It could be that your previous take-offer attempt resulted in a failed trade.
|
||||||
|
|
||||||
|
offerbook.warning.arbitratorNotValidated=This offer cannot be taken because the arbitrator is invalid
|
||||||
|
offerbook.warning.signatureNotValidated=This offer cannot be taken because the arbitrator's signature is invalid
|
||||||
|
|
||||||
offerbook.info.sellAtMarketPrice=You will sell at market price (updated every minute).
|
offerbook.info.sellAtMarketPrice=You will sell at market price (updated every minute).
|
||||||
offerbook.info.buyAtMarketPrice=You will buy at market price (updated every minute).
|
offerbook.info.buyAtMarketPrice=You will buy at market price (updated every minute).
|
||||||
offerbook.info.sellBelowMarketPrice=You will get {0} less than the current market price (updated every minute).
|
offerbook.info.sellBelowMarketPrice=You will get {0} less than the current market price (updated every minute).
|
||||||
|
@ -685,8 +685,11 @@ abstract public class OfferBookView<R extends GridPane, M extends OfferBookViewM
|
|||||||
"isInsufficientTradeLimit case.");
|
"isInsufficientTradeLimit case.");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HIDE_BSQ_SWAPS_DUE_DAO_DEACTIVATED:
|
case ARBITRATOR_NOT_VALIDATED:
|
||||||
new Popup().warning(Res.get("offerbook.warning.hideBsqSwapsDueDaoDeactivated")).show();
|
new Popup().warning(Res.get("offerbook.warning.arbitratorNotValidated")).show();
|
||||||
|
break;
|
||||||
|
case SIGNATURE_NOT_VALIDATED:
|
||||||
|
new Popup().warning(Res.get("offerbook.warning.signatureNotValidated")).show();
|
||||||
break;
|
break;
|
||||||
case VALID:
|
case VALID:
|
||||||
default:
|
default:
|
||||||
|
Loading…
Reference in New Issue
Block a user