Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve xmr proof service #6217

Merged
merged 14 commits into from
Jun 1, 2022
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -94,12 +94,13 @@ public XmrTxProofRequest.Result parse(XmrTxProofModel model, String jsonTxt) {
}

// validate that the txKey matches
JsonElement jsonViewkey = jsonData.get("viewkey");
if (jsonViewkey == null) {
// The API use viewkey param as txKey for our operation mode
JsonElement jsonTxkey = jsonData.get("viewkey");
if (jsonTxkey == null) {
return XmrTxProofRequest.Result.ERROR.with(XmrTxProofRequest.Detail.API_INVALID.error("Missing viewkey field"));
} else {
if (!jsonViewkey.getAsString().equalsIgnoreCase(model.getTxKey())) {
log.warn("viewkey {}, expected: {}", jsonViewkey.getAsString(), model.getTxKey());
if (!jsonTxkey.getAsString().equalsIgnoreCase(model.getTxKey())) {
log.warn("viewkey {}, expected: {}", jsonTxkey.getAsString(), model.getTxKey());
return XmrTxProofRequest.Result.FAILED.with(XmrTxProofRequest.Detail.TX_KEY_INVALID);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -169,11 +169,11 @@ public String toString() {
if (model.getServiceAddress().regionMatches(0, "http:", 0, 5)) {
httpClient.setBaseUrl(model.getServiceAddress());
httpClient.setIgnoreSocks5Proxy(true);
// any non-onion FQDN starts with https://, use Tor
// any non-onion FQDN starts with https://, use Tor
} else if (model.getServiceAddress().regionMatches(0, "https:", 0, 6)) {
httpClient.setBaseUrl(model.getServiceAddress());
httpClient.setIgnoreSocks5Proxy(false);
// it's a raw onion so add http:// and use Tor proxy
// it's a raw onion so add http:// and use Tor proxy
} else {
httpClient.setBaseUrl("http://" + model.getServiceAddress());
httpClient.setIgnoreSocks5Proxy(false);
Expand Down Expand Up @@ -206,6 +206,8 @@ public void requestFromService(Consumer<Result> resultHandler, FaultHandler faul

ListenableFuture<Result> future = executorService.submit(() -> {
Thread.currentThread().setName("XmrTransferProofRequest-" + this.getShortId());
// The API use the viewkey param for txKey if txprove is true
// https://github.com/moneroexamples/onion-monero-blockchain-explorer/blob/9a37839f37abef0b8b94ceeba41ab51a41f3fbd8/src/page.h#L5254
String param = "/api/outputs?txhash=" + model.getTxHash() +
"&address=" + model.getRecipientAddress() +
"&viewkey=" + model.getTxKey() +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import bisq.core.trade.txproof.AssetTxProofService;
import bisq.core.user.AutoConfirmSettings;
import bisq.core.user.Preferences;
import bisq.core.xmr.knaccc.monero.crypto.CryptoUtil;

import bisq.network.Socks5ProxyProvider;
import bisq.network.p2p.BootstrapListener;
Expand Down Expand Up @@ -163,7 +164,7 @@ private void onP2pNetworkAndWalletReady() {
p2pNetworkAndWalletReadyListener = null;
}

if (!preferences.findAutoConfirmSettings("XMR").isPresent()) {
if (preferences.findAutoConfirmSettings("XMR").isEmpty()) {
log.error("AutoConfirmSettings is not present");
return;
}
Expand Down Expand Up @@ -225,9 +226,18 @@ private void processTradeOrAddListener(SellerTrade trade) {
}

private void startRequestsIfValid(SellerTrade trade) {
String txId = trade.getCounterCurrencyTxId();
String txHash = trade.getCounterCurrencyExtraData();
if (is32BitHexStringInValid(txId) || is32BitHexStringInValid(txHash)) {
String txHash = trade.getCounterCurrencyTxId();
String txKey = trade.getCounterCurrencyExtraData();

if (is32BitHexStringInValid(txHash) || is32BitHexStringInValid(txKey)) {
trade.setAssetTxProofResult(AssetTxProofResult.INVALID_DATA.details(Res.get("portfolio.pending.autoConf.state.txKeyOrTxIdInvalid")));
tradeManager.requestPersistence();
return;
}

String canonicalTxKey = CryptoUtil.toCanonicalTxKey(txKey);
if (!txKey.equals(canonicalTxKey)) {
log.error("Provided txKey is not in canonical form. txKey={}, canonicalTxKey={}", txKey, canonicalTxKey);
trade.setAssetTxProofResult(AssetTxProofResult.INVALID_DATA.details(Res.get("portfolio.pending.autoConf.state.txKeyOrTxIdInvalid")));
tradeManager.requestPersistence();
return;
Expand Down Expand Up @@ -350,7 +360,7 @@ private boolean isExpectedTradeState(Trade.State newValue) {
return newValue == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG;
}

private boolean is32BitHexStringInValid(String hexString) {
static boolean is32BitHexStringInValid(String hexString) {
if (hexString == null || hexString.isEmpty() || !hexString.matches("[a-fA-F0-9]{64}")) {
log.warn("Invalid hexString: {}", hexString);
return true;
Expand All @@ -365,13 +375,14 @@ private boolean isAutoConfDisabledByFilter() {
}

private boolean wasTxKeyReUsed(Trade trade, List<Trade> activeTrades) {
// For dev testing we reuse test data so we ignore that check
// For dev testing we reuse test data, so we ignore that check
if (DevEnv.isDevMode()) {
return false;
}

// We need to prevent that a user tries to scam by reusing a txKey and txHash of a previous XMR trade with
// the same user (same address) and same amount. We check only for the txKey as a same txHash but different
// the same user (same address) and same amount.
// We check additionally to the txKey also the txHash though different
// txKey is not possible to get a valid result at proof.
Stream<Trade> failedAndOpenTrades = Stream.concat(
activeTrades.stream(),
Expand All @@ -380,19 +391,27 @@ private boolean wasTxKeyReUsed(Trade trade, List<Trade> activeTrades) {
.filter(tradable -> tradable instanceof Trade)
.map(tradable -> (Trade) tradable);
Stream<Trade> allTrades = Stream.concat(failedAndOpenTrades, closedTrades);
String txKey = trade.getCounterCurrencyExtraData();
String tradeTxKey = trade.getCounterCurrencyExtraData();
String tradeTxHash = trade.getCounterCurrencyTxId();
return allTrades
.filter(t -> !t.getId().equals(trade.getId())) // ignore same trade
.anyMatch(t -> {
String extra = t.getCounterCurrencyExtraData();
if (extra == null) {
String txKey = t.getCounterCurrencyExtraData();
String txHash = t.getCounterCurrencyTxId();
if (txKey == null || txHash == null) {
return false;
}

boolean alreadyUsed = extra.equals(txKey);
boolean alreadyUsed = txKey.equalsIgnoreCase(tradeTxKey);
if (alreadyUsed) {
log.warn("Peer used the XMR tx key already at another trade with trade ID {}. " +
"This might be a scam attempt.", t.getId());
return alreadyUsed;
}
alreadyUsed = txHash.equalsIgnoreCase(tradeTxHash);
if (alreadyUsed) {
log.warn("Peer used the XMR tx ID already at another trade with trade ID {}. " +
"This might be a scam attempt.", t.getId());
}
return alreadyUsed;
});
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/java/bisq/core/user/Preferences.java
Original file line number Diff line number Diff line change
Expand Up @@ -1147,5 +1147,11 @@ private interface ExcludesDelegateMethods {
void setNotifyOnPreRelease(boolean value);

void setUseFullModeDaoMonitor(boolean value);

void setClearDataAfterDays(int value);

void setBuyScreenCryptoCurrencyCode(String buyScreenCurrencyCode);

void setSellScreenCryptoCurrencyCode(String sellScreenCurrencyCode);
}
}
Loading