Add abstract AutoConfirmResult class to get better support if we want

to add auto confirm for other currencies in the future. The generic part
is only used where we would have issues with backward compatibility like
in the protobuf objects. Most of the current classes are kept XMR
specific and could be generalized once we add other assets, but that
would be an internal refactoring without breaking any network or
storage data. I think it would be premature to go further here as we
don't know the details of other use cases. I added the methods used from
clients to AutoConfirmResult, not sure if the API is well defined by
that, but as said that could become subject of a future refactoring once
another auto confirm feature gets added. Goal of that refactoring was
to avoid that we need more fields for trade and the the UI would have to
deal with lots of switch cases based on currency.

Sorry that is a larger commit, would have been hard to break up...
This commit is contained in:
chimp1984 2020-08-30 15:45:47 -05:00
parent 2f1566bb06
commit ed5078c0f1
No known key found for this signature in database
GPG key ID: 9801B4EC591F90E3
15 changed files with 263 additions and 181 deletions

View file

@ -17,66 +17,31 @@
package bisq.core.trade;
import bisq.core.locale.Res;
import bisq.common.proto.ProtoUtil;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import javax.annotation.Nullable;
@Slf4j
@Value
public class AutoConfirmResult {
public enum State {
UNDEFINED,
FEATURE_DISABLED,
TX_NOT_FOUND,
TX_NOT_CONFIRMED,
PROOF_OK,
CONNECTION_FAIL,
API_FAILURE,
API_INVALID,
TX_KEY_REUSED,
TX_HASH_INVALID,
TX_KEY_INVALID,
ADDRESS_INVALID,
NO_MATCH_FOUND,
AMOUNT_NOT_MATCHING,
TRADE_LIMIT_EXCEEDED,
TRADE_DATE_NOT_MATCHING
/**
* Base class for AutoConfirm implementations
*/
@EqualsAndHashCode
@Getter
public abstract class AutoConfirmResult {
public static AutoConfirmResult fromCurrencyCode(String currencyCode) {
switch (currencyCode) {
case "XMR":
return new XmrAutoConfirmResult();
default:
return null;
}
}
// Only state gets persisted
private final State state;
private final String stateName;
private final transient int confirmCount;
private final transient int confirmsRequired;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public AutoConfirmResult(State state) {
this(state, 0, 0);
}
// alternate constructor for showing confirmation progress information
public AutoConfirmResult(State state, int confirmCount, int confirmsRequired) {
this.state = state;
this.confirmCount = confirmCount;
this.confirmsRequired = confirmsRequired;
}
// alternate constructor for error scenarios
public AutoConfirmResult(State state, @Nullable String errorMsg) {
this(state, 0, 0);
if (isErrorState()) {
log.error(errorMsg != null ? errorMsg : state.toString());
}
protected AutoConfirmResult(String stateName) {
this.stateName = stateName;
}
@ -84,48 +49,25 @@ public class AutoConfirmResult {
// PROTOBUF
///////////////////////////////////////////////////////////////////////////////////////////
public protobuf.AutoConfirmResult toProtoMessage() {
return protobuf.AutoConfirmResult.newBuilder().setStateName(state.name()).build();
// We use fromProto as kind of factory method to get the specific AutoConfirmResult
@Nullable
static AutoConfirmResult fromProto(protobuf.AutoConfirmResult proto, String currencyCode) {
switch (currencyCode) {
case "XMR":
return XmrAutoConfirmResult.fromProto(proto);
default:
return null;
}
}
public static AutoConfirmResult fromProto(protobuf.AutoConfirmResult proto) {
AutoConfirmResult.State state = ProtoUtil.enumFromProto(AutoConfirmResult.State.class, proto.getStateName());
return state != null ? new AutoConfirmResult(state) : new AutoConfirmResult(State.UNDEFINED);
}
public abstract protobuf.AutoConfirmResult toProtoMessage();
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public String getTextStatus() {
switch (state) {
case TX_NOT_CONFIRMED:
return Res.get("portfolio.pending.autoConfirmPending")
+ " " + confirmCount
+ "/" + confirmsRequired;
case TX_NOT_FOUND:
return Res.get("portfolio.pending.autoConfirmTxNotFound");
case FEATURE_DISABLED:
return Res.get("portfolio.pending.autoConfirmDisabled");
case PROOF_OK:
return Res.get("portfolio.pending.autoConfirmSuccess");
default:
// any other statuses we display the enum name
return this.state.toString();
}
}
abstract public boolean isSuccessState();
public boolean isPendingState() {
return (state == State.TX_NOT_FOUND || state == State.TX_NOT_CONFIRMED);
}
public boolean isSuccessState() {
return (state == State.PROOF_OK);
}
public boolean isErrorState() {
return (!isPendingState() && !isSuccessState());
}
abstract public String getTextStatus();
}

View file

@ -435,7 +435,10 @@ public abstract class Trade implements Tradable, Model {
@Setter
private String counterCurrencyExtraData;
@Getter
public AutoConfirmResult getAutoConfirmResult() {
return autoConfirmResult != null ? autoConfirmResult : AutoConfirmResult.fromCurrencyCode(checkNotNull(offer).getCurrencyCode());
}
@Nullable
private AutoConfirmResult autoConfirmResult;
@ -595,7 +598,7 @@ public abstract class Trade implements Tradable, Model {
trade.setLockTime(proto.getLockTime());
trade.setLastRefreshRequestDate(proto.getLastRefreshRequestDate());
trade.setCounterCurrencyExtraData(ProtoUtil.stringOrNullFromProto(proto.getCounterCurrencyExtraData()));
trade.setAutoConfirmResult(AutoConfirmResult.fromProto(proto.getAutoConfirmResult()));
trade.setAutoConfirmResult(AutoConfirmResult.fromProto(proto.getAutoConfirmResult(), checkNotNull(trade.getOffer()).getCurrencyCode()));
trade.chatMessages.addAll(proto.getChatMessageList().stream()
.map(ChatMessage::fromPayloadProto)
@ -625,7 +628,7 @@ public abstract class Trade implements Tradable, Model {
User user,
FilterManager filterManager,
AccountAgeWitnessService accountAgeWitnessService,
AutoConfirmationManager autoConfirmationManager,
XmrAutoConfirmationManager xmrAutoConfirmationManager,
TradeStatisticsManager tradeStatisticsManager,
ArbitratorManager arbitratorManager,
MediatorManager mediatorManager,
@ -645,7 +648,7 @@ public abstract class Trade implements Tradable, Model {
user,
filterManager,
accountAgeWitnessService,
autoConfirmationManager,
xmrAutoConfirmationManager,
tradeStatisticsManager,
arbitratorManager,
mediatorManager,

View file

@ -126,7 +126,7 @@ public class TradeManager implements PersistedDataHost {
private final TradeStatisticsManager tradeStatisticsManager;
private final ReferralIdService referralIdService;
private final AccountAgeWitnessService accountAgeWitnessService;
private final AutoConfirmationManager autoConfirmationManager;
private final XmrAutoConfirmationManager xmrAutoConfirmationManager;
private final ArbitratorManager arbitratorManager;
private final MediatorManager mediatorManager;
private final RefundAgentManager refundAgentManager;
@ -168,7 +168,7 @@ public class TradeManager implements PersistedDataHost {
TradeStatisticsManager tradeStatisticsManager,
ReferralIdService referralIdService,
AccountAgeWitnessService accountAgeWitnessService,
AutoConfirmationManager autoConfirmationManager,
XmrAutoConfirmationManager xmrAutoConfirmationManager,
ArbitratorManager arbitratorManager,
MediatorManager mediatorManager,
RefundAgentManager refundAgentManager,
@ -191,7 +191,7 @@ public class TradeManager implements PersistedDataHost {
this.tradeStatisticsManager = tradeStatisticsManager;
this.referralIdService = referralIdService;
this.accountAgeWitnessService = accountAgeWitnessService;
this.autoConfirmationManager = autoConfirmationManager;
this.xmrAutoConfirmationManager = xmrAutoConfirmationManager;
this.arbitratorManager = arbitratorManager;
this.mediatorManager = mediatorManager;
this.refundAgentManager = refundAgentManager;
@ -436,7 +436,7 @@ public class TradeManager implements PersistedDataHost {
user,
filterManager,
accountAgeWitnessService,
autoConfirmationManager,
xmrAutoConfirmationManager,
tradeStatisticsManager,
arbitratorManager,
mediatorManager,

View file

@ -0,0 +1,136 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.core.trade;
import bisq.core.locale.Res;
import bisq.common.proto.ProtoUtil;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.Nullable;
@Slf4j
@Getter
@EqualsAndHashCode(callSuper = true)
public class XmrAutoConfirmResult extends AutoConfirmResult {
public enum State {
UNDEFINED,
FEATURE_DISABLED,
TX_NOT_FOUND,
TX_NOT_CONFIRMED,
PROOF_OK,
CONNECTION_FAIL,
API_FAILURE,
API_INVALID,
TX_KEY_REUSED,
TX_HASH_INVALID,
TX_KEY_INVALID,
ADDRESS_INVALID,
NO_MATCH_FOUND,
AMOUNT_NOT_MATCHING,
TRADE_LIMIT_EXCEEDED,
TRADE_DATE_NOT_MATCHING
}
private final State state;
private final transient int confirmCount;
private final transient int confirmsRequired;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
public XmrAutoConfirmResult() {
this(State.UNDEFINED, 0, 0);
}
public XmrAutoConfirmResult(State state) {
this(state, 0, 0);
}
// alternate constructor for showing confirmation progress information
public XmrAutoConfirmResult(State state, int confirmCount, int confirmsRequired) {
super(state.name());
this.state = state;
this.confirmCount = confirmCount;
this.confirmsRequired = confirmsRequired;
}
// alternate constructor for error scenarios
public XmrAutoConfirmResult(State state, @Nullable String errorMsg) {
this(state, 0, 0);
if (isErrorState()) {
log.error(errorMsg != null ? errorMsg : state.toString());
}
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTOBUF
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public protobuf.AutoConfirmResult toProtoMessage() {
return protobuf.AutoConfirmResult.newBuilder().setStateName(state.name()).build();
}
public static XmrAutoConfirmResult fromProto(protobuf.AutoConfirmResult proto) {
XmrAutoConfirmResult.State state = ProtoUtil.enumFromProto(XmrAutoConfirmResult.State.class, proto.getStateName());
return state != null ? new XmrAutoConfirmResult(state) : new XmrAutoConfirmResult(State.UNDEFINED);
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public String getTextStatus() {
switch (state) {
case TX_NOT_CONFIRMED:
return Res.get("portfolio.pending.autoConfirmPending")
+ " " + confirmCount
+ "/" + confirmsRequired;
case TX_NOT_FOUND:
return Res.get("portfolio.pending.autoConfirmTxNotFound");
case FEATURE_DISABLED:
return Res.get("portfolio.pending.autoConfirmDisabled");
case PROOF_OK:
return Res.get("portfolio.pending.autoConfirmSuccess");
default:
// any other statuses we display the enum name
return this.state.toString();
}
}
public boolean isPendingState() {
return (state == State.TX_NOT_FOUND || state == State.TX_NOT_CONFIRMED);
}
public boolean isSuccessState() {
return (state == State.PROOF_OK);
}
public boolean isErrorState() {
return (!isPendingState() && !isSuccessState());
}
}

View file

@ -20,6 +20,7 @@ package bisq.core.trade;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.setup.WalletsSetup;
import bisq.core.filter.FilterManager;
import bisq.core.monetary.Volume;
import bisq.core.offer.Offer;
import bisq.core.payment.payload.AssetsAccountPayload;
import bisq.core.payment.payload.PaymentAccountPayload;
@ -49,7 +50,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j
@Singleton
public class AutoConfirmationManager {
public class XmrAutoConfirmationManager {
private final FilterManager filterManager;
private final Preferences preferences;
@ -66,7 +67,7 @@ public class AutoConfirmationManager {
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
AutoConfirmationManager(FilterManager filterManager,
XmrAutoConfirmationManager(FilterManager filterManager,
Preferences preferences,
XmrTransferProofService xmrTransferProofService,
ClosedTradableManager closedTradableManager,
@ -153,7 +154,7 @@ public class AutoConfirmationManager {
if (alreadyUsed) {
String message = "Peer used the XMR tx key already at another trade with trade ID " +
t.getId() + ". This might be a scam attempt.";
trade.setAutoConfirmResult(new AutoConfirmResult(AutoConfirmResult.State.TX_KEY_REUSED, message));
trade.setAutoConfirmResult(new XmrAutoConfirmResult(XmrAutoConfirmResult.State.TX_KEY_REUSED, message));
}
return alreadyUsed;
});
@ -163,23 +164,24 @@ public class AutoConfirmationManager {
}
if (!preferences.getAutoConfirmSettings().enabled || this.isAutoConfDisabledByFilter()) {
trade.setAutoConfirmResult(new AutoConfirmResult(AutoConfirmResult.State.FEATURE_DISABLED, null));
trade.setAutoConfirmResult(new XmrAutoConfirmResult(XmrAutoConfirmResult.State.FEATURE_DISABLED, null));
return;
}
Coin tradeAmount = trade.getTradeAmount();
Coin tradeLimit = Coin.valueOf(preferences.getAutoConfirmSettings().tradeLimit);
if (tradeAmount.isGreaterThan(tradeLimit)) {
if (tradeAmount != null && tradeAmount.isGreaterThan(tradeLimit)) {
log.warn("Trade amount {} is higher than settings limit {}, will not attempt auto-confirm",
tradeAmount.toFriendlyString(), tradeLimit.toFriendlyString());
trade.setAutoConfirmResult(new AutoConfirmResult(AutoConfirmResult.State.TRADE_LIMIT_EXCEEDED, null));
trade.setAutoConfirmResult(new XmrAutoConfirmResult(XmrAutoConfirmResult.State.TRADE_LIMIT_EXCEEDED, null));
return;
}
String address = sellersAssetsAccountPayload.getAddress();
// XMR satoshis have 12 decimal places vs. bitcoin's 8
long amountXmr = offer.getVolumeByAmount(tradeAmount).getValue() * 10000L;
Volume volume = offer.getVolumeByAmount(tradeAmount);
long amountXmr = volume != null ? volume.getValue() * 10000L : 0L;
int confirmsRequired = preferences.getAutoConfirmSettings().requiredConfirmations;
trade.setAutoConfirmResult(new AutoConfirmResult(AutoConfirmResult.State.TX_NOT_FOUND));
trade.setAutoConfirmResult(new XmrAutoConfirmResult(XmrAutoConfirmResult.State.TX_NOT_FOUND));
List<String> serviceAddresses = preferences.getAutoConfirmSettings().serviceAddresses;
txProofResultsPending.put(trade.getId(), serviceAddresses.size()); // need result from each service address
for (String serviceAddress : serviceAddresses) {
@ -204,7 +206,7 @@ public class AutoConfirmationManager {
}
}
private boolean handleProofResult(AutoConfirmResult result, Trade trade) {
private boolean handleProofResult(XmrAutoConfirmResult result, Trade trade) {
boolean success = true;
boolean failure = false;
@ -250,8 +252,10 @@ public class AutoConfirmationManager {
}
accountAgeWitnessService.maybeSignWitness(trade);
// transition the trade to step 4:
((SellerTrade) trade).onFiatPaymentReceived(() -> { },
errorMessage -> { });
((SellerTrade) trade).onFiatPaymentReceived(() -> {
},
errorMessage -> {
});
return success;
}

View file

@ -17,7 +17,7 @@
package bisq.core.trade.asset.xmr;
import bisq.core.trade.AutoConfirmResult;
import bisq.core.trade.XmrAutoConfirmResult;
import bisq.asset.CryptoNoteAddressValidator;
@ -68,57 +68,57 @@ public class XmrProofInfo {
return txHash + "|" + serviceAddress;
}
public AutoConfirmResult checkApiResponse(String jsonTxt) {
public XmrAutoConfirmResult checkApiResponse(String jsonTxt) {
try {
JsonObject json = new Gson().fromJson(jsonTxt, JsonObject.class);
if (json == null) {
return new AutoConfirmResult(AutoConfirmResult.State.API_INVALID, "Empty json");
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Empty json");
}
// there should always be "data" and "status" at the top level
if (json.get("data") == null || !json.get("data").isJsonObject() || json.get("status") == null) {
return new AutoConfirmResult(AutoConfirmResult.State.API_INVALID, "Missing data / status fields");
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Missing data / status fields");
}
JsonObject jsonData = json.get("data").getAsJsonObject();
String jsonStatus = json.get("status").getAsString();
if (jsonStatus.matches("fail")) {
// the API returns "fail" until the transaction has successfully reached the mempool.
// we return TX_NOT_FOUND which will cause a retry later
return new AutoConfirmResult(AutoConfirmResult.State.TX_NOT_FOUND, null);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.TX_NOT_FOUND, null);
} else if (!jsonStatus.matches("success")) {
return new AutoConfirmResult(AutoConfirmResult.State.API_FAILURE, "Unhandled status value");
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_FAILURE, "Unhandled status value");
}
// validate that the address matches
JsonElement jsonAddress = jsonData.get("address");
if (jsonAddress == null) {
return new AutoConfirmResult(AutoConfirmResult.State.API_INVALID, "Missing address field");
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Missing address field");
} else {
String expectedAddressHex = CryptoNoteAddressValidator.convertToRawHex(this.recipientAddress);
if (!jsonAddress.getAsString().equalsIgnoreCase(expectedAddressHex)) {
log.warn("address {}, expected: {}", jsonAddress.getAsString(), expectedAddressHex);
return new AutoConfirmResult(AutoConfirmResult.State.ADDRESS_INVALID, null);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.ADDRESS_INVALID, null);
}
}
// validate that the txhash matches
JsonElement jsonTxHash = jsonData.get("tx_hash");
if (jsonTxHash == null) {
return new AutoConfirmResult(AutoConfirmResult.State.API_INVALID, "Missing tx_hash field");
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Missing tx_hash field");
} else {
if (!jsonTxHash.getAsString().equalsIgnoreCase(txHash)) {
log.warn("txHash {}, expected: {}", jsonTxHash.getAsString(), txHash);
return new AutoConfirmResult(AutoConfirmResult.State.TX_HASH_INVALID, null);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.TX_HASH_INVALID, null);
}
}
// validate that the txkey matches
JsonElement jsonViewkey = jsonData.get("viewkey");
if (jsonViewkey == null) {
return new AutoConfirmResult(AutoConfirmResult.State.API_INVALID, "Missing viewkey field");
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Missing viewkey field");
} else {
if (!jsonViewkey.getAsString().equalsIgnoreCase(this.txKey)) {
log.warn("viewkey {}, expected: {}", jsonViewkey.getAsString(), txKey);
return new AutoConfirmResult(AutoConfirmResult.State.TX_KEY_INVALID, null);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.TX_KEY_INVALID, null);
}
}
@ -126,7 +126,7 @@ public class XmrProofInfo {
// (except that in dev mode we let this check pass anyway)
JsonElement jsonTimestamp = jsonData.get("tx_timestamp");
if (jsonTimestamp == null) {
return new AutoConfirmResult(AutoConfirmResult.State.API_INVALID, "Missing tx_timestamp field");
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Missing tx_timestamp field");
} else {
long tradeDateSeconds = tradeDate.getTime() / 1000;
long difference = tradeDateSeconds - jsonTimestamp.getAsLong();
@ -134,7 +134,7 @@ public class XmrProofInfo {
if (difference > TimeUnit.HOURS.toSeconds(2) && !DevEnv.isDevMode()) {
log.warn("tx_timestamp {}, tradeDate: {}, difference {}",
jsonTimestamp.getAsLong(), tradeDateSeconds, difference);
return new AutoConfirmResult(AutoConfirmResult.State.TRADE_DATE_NOT_MATCHING, null);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.TRADE_DATE_NOT_MATCHING, null);
}
}
@ -142,7 +142,7 @@ public class XmrProofInfo {
int confirmations;
JsonElement jsonConfs = jsonData.get("tx_confirmations");
if (jsonConfs == null) {
return new AutoConfirmResult(AutoConfirmResult.State.API_INVALID, "Missing tx_confirmations field");
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, "Missing tx_confirmations field");
} else {
confirmations = jsonConfs.getAsInt();
log.info("Confirmations: {}, xmr txHash: {}", confirmations, txHash);
@ -161,23 +161,23 @@ public class XmrProofInfo {
if (jsonAmount == amount || DevEnv.isDevMode()) { // any amount ok in dev mode
if (confirmations < confirmsRequired)
// we return TX_NOT_CONFIRMED which will cause a retry later
return new AutoConfirmResult(AutoConfirmResult.State.TX_NOT_CONFIRMED, confirmations, confirmsRequired);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.TX_NOT_CONFIRMED, confirmations, confirmsRequired);
else
return new AutoConfirmResult(AutoConfirmResult.State.PROOF_OK, confirmations, confirmsRequired);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.PROOF_OK, confirmations, confirmsRequired);
}
}
}
// None of the outputs had a match entry
if (!anyMatchFound) {
return new AutoConfirmResult(AutoConfirmResult.State.NO_MATCH_FOUND, null);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.NO_MATCH_FOUND, null);
}
// reaching this point means there was no matching amount
return new AutoConfirmResult(AutoConfirmResult.State.AMOUNT_NOT_MATCHING, null);
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.AMOUNT_NOT_MATCHING, null);
} catch (JsonParseException | NullPointerException e) {
return new AutoConfirmResult(AutoConfirmResult.State.API_INVALID, e.toString());
return new XmrAutoConfirmResult(XmrAutoConfirmResult.State.API_INVALID, e.toString());
}
}
}

View file

@ -17,7 +17,7 @@
package bisq.core.trade.asset.xmr;
import bisq.core.trade.AutoConfirmResult;
import bisq.core.trade.XmrAutoConfirmResult;
import bisq.network.Socks5ProxyProvider;
@ -50,7 +50,7 @@ public class XmrTransferProofRequester {
"XmrTransferProofRequester", 3, 5, 10 * 60);
private final XmrTxProofHttpClient httpClient;
private final XmrProofInfo xmrProofInfo;
private final Consumer<AutoConfirmResult> resultHandler;
private final Consumer<XmrAutoConfirmResult> resultHandler;
private final FaultHandler faultHandler;
private boolean terminated;
@ -63,7 +63,7 @@ public class XmrTransferProofRequester {
XmrTransferProofRequester(@Nullable Socks5ProxyProvider socks5ProxyProvider,
XmrProofInfo xmrProofInfo,
Consumer<AutoConfirmResult> resultHandler,
Consumer<XmrAutoConfirmResult> resultHandler,
FaultHandler faultHandler) {
this.httpClient = new XmrTxProofHttpClient(socks5ProxyProvider);
this.httpClient.setBaseUrl("http://" + xmrProofInfo.getServiceAddress());
@ -94,7 +94,7 @@ public class XmrTransferProofRequester {
log.info("Request() aborted, this object has been terminated. Service: {}", httpClient.getBaseUrl());
return;
}
ListenableFuture<AutoConfirmResult> future = executorService.submit(() -> {
ListenableFuture<XmrAutoConfirmResult> future = executorService.submit(() -> {
Thread.currentThread().setName("XmrTransferProofRequest-" + xmrProofInfo.getKey());
String param = "/api/outputs?txhash=" + xmrProofInfo.getTxHash() +
"&address=" + xmrProofInfo.getRecipientAddress() +
@ -102,13 +102,13 @@ public class XmrTransferProofRequester {
"&txprove=1";
log.info("Requesting from {} with param {}", httpClient.getBaseUrl(), param);
String json = httpClient.requestWithGET(param, "User-Agent", "bisq/" + Version.VERSION);
AutoConfirmResult autoConfirmResult = xmrProofInfo.checkApiResponse(json);
XmrAutoConfirmResult autoConfirmResult = xmrProofInfo.checkApiResponse(json);
log.info("Response json {} resulted in autoConfirmResult {}", json, autoConfirmResult);
return autoConfirmResult;
});
Futures.addCallback(future, new FutureCallback<>() {
public void onSuccess(AutoConfirmResult result) {
public void onSuccess(XmrAutoConfirmResult result) {
if (terminated) {
log.info("API terminated from higher level: {}", httpClient.getBaseUrl());
return;
@ -127,7 +127,7 @@ public class XmrTransferProofRequester {
String errorMessage = "Request to " + httpClient.getBaseUrl() + " failed";
faultHandler.handleFault(errorMessage, throwable);
UserThread.execute(() -> resultHandler.accept(
new AutoConfirmResult(AutoConfirmResult.State.CONNECTION_FAIL, errorMessage)));
new XmrAutoConfirmResult(XmrAutoConfirmResult.State.CONNECTION_FAIL, errorMessage)));
}
});
}

View file

@ -17,7 +17,7 @@
package bisq.core.trade.asset.xmr;
import bisq.core.trade.AutoConfirmResult;
import bisq.core.trade.XmrAutoConfirmResult;
import bisq.network.Socks5ProxyProvider;
@ -48,7 +48,7 @@ public class XmrTransferProofService {
}
public void requestProof(XmrProofInfo xmrProofInfo,
Consumer<AutoConfirmResult> resultHandler,
Consumer<XmrAutoConfirmResult> resultHandler,
FaultHandler faultHandler) {
String key = xmrProofInfo.getKey();
if (map.containsKey(key)) {

View file

@ -33,10 +33,10 @@ import bisq.core.proto.CoreProtoResolver;
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
import bisq.core.support.dispute.mediation.mediator.MediatorManager;
import bisq.core.support.dispute.refund.refundagent.RefundAgentManager;
import bisq.core.trade.AutoConfirmationManager;
import bisq.core.trade.MakerTrade;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeManager;
import bisq.core.trade.XmrAutoConfirmationManager;
import bisq.core.trade.messages.TradeMessage;
import bisq.core.trade.statistics.ReferralIdService;
import bisq.core.trade.statistics.TradeStatisticsManager;
@ -90,7 +90,7 @@ public class ProcessModel implements Model, PersistablePayload {
transient private User user;
transient private FilterManager filterManager;
transient private AccountAgeWitnessService accountAgeWitnessService;
transient private AutoConfirmationManager autoConfirmationManager;
transient private XmrAutoConfirmationManager xmrAutoConfirmationManager;
transient private TradeStatisticsManager tradeStatisticsManager;
transient private ArbitratorManager arbitratorManager;
transient private MediatorManager mediatorManager;
@ -247,7 +247,7 @@ public class ProcessModel implements Model, PersistablePayload {
User user,
FilterManager filterManager,
AccountAgeWitnessService accountAgeWitnessService,
AutoConfirmationManager autoConfirmationManager,
XmrAutoConfirmationManager xmrAutoConfirmationManager,
TradeStatisticsManager tradeStatisticsManager,
ArbitratorManager arbitratorManager,
MediatorManager mediatorManager,
@ -266,7 +266,7 @@ public class ProcessModel implements Model, PersistablePayload {
this.user = user;
this.filterManager = filterManager;
this.accountAgeWitnessService = accountAgeWitnessService;
this.autoConfirmationManager = autoConfirmationManager;
this.xmrAutoConfirmationManager = xmrAutoConfirmationManager;
this.tradeStatisticsManager = tradeStatisticsManager;
this.arbitratorManager = arbitratorManager;
this.mediatorManager = mediatorManager;

View file

@ -58,7 +58,7 @@ public class SellerProcessCounterCurrencyTransferStartedMessage extends TradeTas
String counterCurrencyExtraData = message.getCounterCurrencyExtraData();
if (counterCurrencyExtraData != null && counterCurrencyExtraData.length() < 100) {
trade.setCounterCurrencyExtraData(counterCurrencyExtraData);
processModel.getAutoConfirmationManager().processCounterCurrencyExtraData(
processModel.getXmrAutoConfirmationManager().processCounterCurrencyExtraData(
trade, processModel.getTradeManager().getTradableList().stream());
}
processModel.removeMailboxMessageAfterProcessing(trade);

View file

@ -1,6 +1,6 @@
package bisq.core.trade.asset.xmr;
import bisq.core.trade.AutoConfirmResult;
import bisq.core.trade.XmrAutoConfirmResult;
import java.time.Instant;
@ -48,13 +48,13 @@ public class XmrProofInfoTest {
public void testJsonRoot() {
// checking what happens when bad input is provided
assertTrue(xmrProofInfo.checkApiResponse(
"invalid json data").getState() == AutoConfirmResult.State.API_INVALID);
"invalid json data").getState() == XmrAutoConfirmResult.State.API_INVALID);
assertTrue(xmrProofInfo.checkApiResponse(
"").getState() == AutoConfirmResult.State.API_INVALID);
"").getState() == XmrAutoConfirmResult.State.API_INVALID);
assertTrue(xmrProofInfo.checkApiResponse(
"[]").getState() == AutoConfirmResult.State.API_INVALID);
"[]").getState() == XmrAutoConfirmResult.State.API_INVALID);
assertTrue(xmrProofInfo.checkApiResponse(
"{}").getState() == AutoConfirmResult.State.API_INVALID);
"{}").getState() == XmrAutoConfirmResult.State.API_INVALID);
}
@Test
@ -62,34 +62,34 @@ public class XmrProofInfoTest {
// testing the top level fields: data and status
assertTrue(xmrProofInfo.checkApiResponse(
"{'data':{'title':''},'status':'fail'}" )
.getState() == AutoConfirmResult.State.TX_NOT_FOUND);
.getState() == XmrAutoConfirmResult.State.TX_NOT_FOUND);
assertTrue(xmrProofInfo.checkApiResponse(
"{'data':{'title':''},'missingstatus':'success'}" )
.getState() == AutoConfirmResult.State.API_INVALID);
.getState() == XmrAutoConfirmResult.State.API_INVALID);
assertTrue(xmrProofInfo.checkApiResponse(
"{'missingdata':{'title':''},'status':'success'}" )
.getState() == AutoConfirmResult.State.API_INVALID);
.getState() == XmrAutoConfirmResult.State.API_INVALID);
}
@Test
public void testJsonAddress() {
assertTrue(xmrProofInfo.checkApiResponse(
"{'data':{'missingaddress':'irrelevant'},'status':'success'}" )
.getState() == AutoConfirmResult.State.API_INVALID);
.getState() == XmrAutoConfirmResult.State.API_INVALID);
assertTrue(xmrProofInfo.checkApiResponse(
"{'data':{'address':'e957dac7'},'status':'success'}" )
.getState() == AutoConfirmResult.State.ADDRESS_INVALID);
.getState() == XmrAutoConfirmResult.State.ADDRESS_INVALID);
}
@Test
public void testJsonTxHash() {
String missing_tx_hash = "{'data':{'address':'" + recipientAddressHex + "'}, 'status':'success'}";
assertTrue(xmrProofInfo.checkApiResponse(missing_tx_hash).getState()
== AutoConfirmResult.State.API_INVALID);
== XmrAutoConfirmResult.State.API_INVALID);
String invalid_tx_hash = "{'data':{'address':'" + recipientAddressHex + "', 'tx_hash':'488e48'}, 'status':'success'}";
assertTrue(xmrProofInfo.checkApiResponse(invalid_tx_hash).getState()
== AutoConfirmResult.State.TX_HASH_INVALID);
== XmrAutoConfirmResult.State.TX_HASH_INVALID);
}
@Test
@ -97,13 +97,13 @@ public class XmrProofInfoTest {
String missing_tx_key = "{'data':{'address':'" + recipientAddressHex + "', " +
"'tx_hash':'" + txHash + "'}, 'status':'success'}";
assertTrue(xmrProofInfo.checkApiResponse(missing_tx_key).getState()
== AutoConfirmResult.State.API_INVALID);
== XmrAutoConfirmResult.State.API_INVALID);
String invalid_tx_key = "{'data':{'address':'" + recipientAddressHex + "', " +
"'tx_hash':'" + txHash + "', " +
"'viewkey':'cdce04'}, 'status':'success'}";
assertTrue(xmrProofInfo.checkApiResponse(invalid_tx_key).getState()
== AutoConfirmResult.State.TX_KEY_INVALID);
== XmrAutoConfirmResult.State.TX_KEY_INVALID);
}
@Test
@ -112,14 +112,14 @@ public class XmrProofInfoTest {
"'tx_hash':'" + txHash + "'," +
"'viewkey':'" + txKey + "'}, 'status':'success'}";
assertTrue(xmrProofInfo.checkApiResponse(missing_tx_timestamp).getState()
== AutoConfirmResult.State.API_INVALID);
== XmrAutoConfirmResult.State.API_INVALID);
String invalid_tx_timestamp = "{'data':{'address':'" + recipientAddressHex + "', " +
"'tx_hash':'" + txHash + "', " +
"'viewkey':'" + txKey + "'," +
"'tx_timestamp':'12345'}, 'status':'success'}";
assertTrue(xmrProofInfo.checkApiResponse(invalid_tx_timestamp).getState()
== AutoConfirmResult.State.TRADE_DATE_NOT_MATCHING);
== XmrAutoConfirmResult.State.TRADE_DATE_NOT_MATCHING);
}
@Test
@ -137,25 +137,25 @@ public class XmrProofInfoTest {
"'tx_timestamp':'" + Long.toString(epochDate) + "'}" +
"}";
assertTrue(xmrProofInfo.checkApiResponse(json).getState()
== AutoConfirmResult.State.PROOF_OK);
== XmrAutoConfirmResult.State.PROOF_OK);
json = json.replaceFirst("777", "0");
assertTrue(xmrProofInfo.checkApiResponse(json).getState()
== AutoConfirmResult.State.TX_NOT_CONFIRMED);
== XmrAutoConfirmResult.State.TX_NOT_CONFIRMED);
json = json.replaceFirst("100000000000", "100000000001");
assertTrue(xmrProofInfo.checkApiResponse(json).getState()
== AutoConfirmResult.State.AMOUNT_NOT_MATCHING);
== XmrAutoConfirmResult.State.AMOUNT_NOT_MATCHING);
// Revert change of amount
json = json.replaceFirst("100000000001", "100000000000");
json = json.replaceFirst("'match':true", "'match':false");
assertTrue(xmrProofInfo.checkApiResponse(json).getState()
== AutoConfirmResult.State.NO_MATCH_FOUND);
== XmrAutoConfirmResult.State.NO_MATCH_FOUND);
}
@Test
public void testJsonFail() {
String failedJson = "{\"data\":null,\"message\":\"Cant parse tx hash: a\",\"status\":\"error\"}";
assertTrue(xmrProofInfo.checkApiResponse(failedJson).getState()
== AutoConfirmResult.State.API_INVALID);
== XmrAutoConfirmResult.State.API_INVALID);
}
}

View file

@ -39,7 +39,6 @@ import bisq.core.util.coin.CoinFormatter;
import bisq.network.p2p.NodeAddress;
import bisq.common.UserThread;
import bisq.common.util.Utilities;
import org.bitcoinj.core.Utils;
@ -160,8 +159,9 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.tradePrice"),
FormattingUtils.formatPrice(trade.getTradePrice()));
String methodText = Res.get(offer.getPaymentMethod().getId());
if (trade.getAutoConfirmResult() != null && trade.getAutoConfirmResult().isSuccessState())
if (trade.getAutoConfirmResult().isSuccessState()) {
methodText += " (" + trade.getAutoConfirmResult().getTextStatus() + ")";
}
addConfirmationLabelLabel(gridPane, ++rowIndex, Res.get("shared.paymentMethod"), methodText);
// second group

View file

@ -117,7 +117,7 @@ public class BuyerStep4View extends TradeStepView {
GridPane.setMargin(hBox2, new Insets(18, -10, -12, -10));
gridPane.getChildren().add(hBox2);
GridPane.setRowSpan(hBox2, 5);
if (trade.getAutoConfirmResult() == null || !trade.getAutoConfirmResult().isSuccessState()) {
if (!trade.getAutoConfirmResult().isSuccessState()) {
autoConfBadge.setVisible(false);
}

View file

@ -40,9 +40,9 @@ import bisq.core.payment.payload.SepaAccountPayload;
import bisq.core.payment.payload.SepaInstantAccountPayload;
import bisq.core.payment.payload.USPostalMoneyOrderAccountPayload;
import bisq.core.payment.payload.WesternUnionAccountPayload;
import bisq.core.trade.AutoConfirmResult;
import bisq.core.trade.Contract;
import bisq.core.trade.Trade;
import bisq.core.trade.AutoConfirmResult;
import bisq.core.user.DontShowAgainLookup;
import bisq.common.Timer;
@ -154,10 +154,7 @@ public class SellerStep3View extends TradeStepView {
if (autoConfirmStatusField != null) {
trade.getAutoConfirmResultProperty().addListener(autoConfirmResultListener);
// display the initial value, or FEATURE_DISABLED if there is none
AutoConfirmResult autoConfirmResult = trade.getAutoConfirmResult();
if (autoConfirmResult == null)
autoConfirmResult = new AutoConfirmResult(AutoConfirmResult.State.FEATURE_DISABLED);
autoConfirmStatusField.setText(autoConfirmResult.getTextStatus());
autoConfirmStatusField.setText(trade.getAutoConfirmResult().getTextStatus());
}
}

View file

@ -1397,7 +1397,7 @@ message Trade {
}
message AutoConfirmResult {
string stateName = 1; // name of AutoConfirmResult.State enum
string stateName = 1;
}
message BuyerAsMakerTrade {