arbitrator sends nack if trade already taken

This commit is contained in:
woodser 2023-12-29 08:59:54 -05:00
parent c6b1d03283
commit fdb24ba9f8
2 changed files with 95 additions and 75 deletions

View File

@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList;
import common.utils.GenUtils; import common.utils.GenUtils;
import haveno.common.ClockWatcher; import haveno.common.ClockWatcher;
import haveno.common.crypto.KeyRing; import haveno.common.crypto.KeyRing;
import haveno.common.crypto.PubKeyRing;
import haveno.common.handlers.ErrorMessageHandler; import haveno.common.handlers.ErrorMessageHandler;
import haveno.common.handlers.FaultHandler; import haveno.common.handlers.FaultHandler;
import haveno.common.handlers.ResultHandler; import haveno.common.handlers.ResultHandler;
@ -69,11 +70,14 @@ import haveno.core.user.User;
import haveno.core.util.Validator; import haveno.core.util.Validator;
import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletService; import haveno.core.xmr.wallet.XmrWalletService;
import haveno.network.p2p.AckMessage;
import haveno.network.p2p.AckMessageSourceType;
import haveno.network.p2p.BootstrapListener; import haveno.network.p2p.BootstrapListener;
import haveno.network.p2p.DecryptedDirectMessageListener; import haveno.network.p2p.DecryptedDirectMessageListener;
import haveno.network.p2p.DecryptedMessageWithPubKey; import haveno.network.p2p.DecryptedMessageWithPubKey;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import haveno.network.p2p.P2PService; import haveno.network.p2p.P2PService;
import haveno.network.p2p.SendMailboxMessageListener;
import haveno.network.p2p.network.TorNetworkNode; import haveno.network.p2p.network.TorNetworkNode;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.LongProperty; import javafx.beans.property.LongProperty;
@ -523,47 +527,52 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
boolean isArbitrator = request.getArbitratorNodeAddress().equals(p2PService.getNetworkNode().getNodeAddress()); boolean isArbitrator = request.getArbitratorNodeAddress().equals(p2PService.getNetworkNode().getNodeAddress());
if (isArbitrator) { if (isArbitrator) {
// verify this node is registered arbitrator // verify this node is registered arbitrator
Arbitrator thisArbitrator = user.getRegisteredArbitrator(); Arbitrator thisArbitrator = user.getRegisteredArbitrator();
NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress(); NodeAddress thisAddress = p2PService.getNetworkNode().getNodeAddress();
if (thisArbitrator == null || !thisArbitrator.getNodeAddress().equals(thisAddress)) { if (thisArbitrator == null || !thisArbitrator.getNodeAddress().equals(thisAddress)) {
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because we are not an arbitrator", sender, request.getTradeId()); log.warn("Ignoring InitTradeRequest from {} with tradeId {} because we are not an arbitrator", sender, request.getTradeId());
return; return;
} }
// get offer associated with trade // get offer associated with trade
Offer offer = null; Offer offer = null;
for (Offer anOffer : offerBookService.getOffers()) { for (Offer anOffer : offerBookService.getOffers()) {
if (anOffer.getId().equals(request.getTradeId())) { if (anOffer.getId().equals(request.getTradeId())) {
offer = anOffer; offer = anOffer;
} }
} }
if (offer == null) { if (offer == null) {
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because offer is not on the books", sender, request.getTradeId()); log.warn("Ignoring InitTradeRequest from {} with tradeId {} because offer is not on the books", sender, request.getTradeId());
return; return;
} }
// verify arbitrator is payload signer unless they are offline // verify arbitrator is payload signer unless they are offline
// TODO (woodser): handle if payload signer differs from current arbitrator (verify signer is offline) // TODO (woodser): handle if payload signer differs from current arbitrator (verify signer is offline)
// verify maker is offer owner // verify maker is offer owner
// TODO (woodser): maker address might change if they disconnect and reconnect, should allow maker address to differ if pubKeyRing is same ? // TODO (woodser): maker address might change if they disconnect and reconnect, should allow maker address to differ if pubKeyRing is same?
if (!offer.getOwnerNodeAddress().equals(request.getMakerNodeAddress())) { if (!offer.getOwnerNodeAddress().equals(request.getMakerNodeAddress())) {
log.warn("Ignoring InitTradeRequest from {} with tradeId {} because maker is not offer owner", sender, request.getTradeId()); log.warn("Ignoring InitTradeRequest from {} with tradeId {} because maker is not offer owner", sender, request.getTradeId());
return; return;
} }
Trade trade; // handle trade
Optional<Trade> tradeOptional = getOpenTrade(offer.getId()); Trade trade;
if (tradeOptional.isPresent()) { Optional<Trade> tradeOptional = getOpenTrade(offer.getId());
trade = tradeOptional.get(); if (tradeOptional.isPresent()) {
trade = tradeOptional.get();
// verify request is from maker // verify request is from maker
if (!sender.equals(request.getMakerNodeAddress())) { if (!sender.equals(request.getMakerNodeAddress())) {
log.warn("Trade is already taken"); // TODO (woodser): need to respond with bad ack
return; // send nack if trade already taken
} String errMsg = "Trade is already taken, tradeId=" + request.getTradeId();
} else { log.warn(errMsg);
sendAckMessage(sender, request.getPubKeyRing(), request, false, errMsg);
return;
}
} else {
// verify request is from taker // verify request is from taker
if (!sender.equals(request.getTakerNodeAddress())) { if (!sender.equals(request.getTakerNodeAddress())) {
@ -600,6 +609,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} }
} }
// process with protocol
((ArbitratorProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> { ((ArbitratorProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> {
log.warn("Arbitrator error during trade initialization for trade {}: {}", trade.getId(), errorMessage); log.warn("Arbitrator error during trade initialization for trade {}: {}", trade.getId(), errorMessage);
maybeRemoveTradeOnError(trade); maybeRemoveTradeOnError(trade);
@ -643,6 +653,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// get expected taker fee // get expected taker fee
BigInteger takerFee = HavenoUtils.getTakerFee(BigInteger.valueOf(request.getTradeAmount())); BigInteger takerFee = HavenoUtils.getTakerFee(BigInteger.valueOf(request.getTradeAmount()));
// initialize trade
Trade trade; Trade trade;
if (offer.isBuyOffer()) if (offer.isBuyOffer())
trade = new BuyerAsMakerTrade(offer, trade = new BuyerAsMakerTrade(offer,
@ -687,6 +698,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
} }
}); });
// process with protocol
((MakerProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> { ((MakerProtocol) getTradeProtocol(trade)).handleInitTradeRequest(request, sender, errorMessage -> {
log.warn("Maker error during trade initialization: " + errorMessage); log.warn("Maker error during trade initialization: " + errorMessage);
maybeRemoveTradeOnError(trade); maybeRemoveTradeOnError(trade);
@ -872,7 +884,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
initTradeAndProtocol(trade, tradeProtocol); initTradeAndProtocol(trade, tradeProtocol);
// take offer and persist trade on success // process with protocol
((TakerProtocol) tradeProtocol).onTakeOffer(result -> { ((TakerProtocol) tradeProtocol).onTakeOffer(result -> {
tradeResultHandler.handleResult(trade); tradeResultHandler.handleResult(trade);
requestPersistence(); requestPersistence();
@ -1115,6 +1127,48 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
// Getters, Utils // Getters, Utils
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void sendAckMessage(NodeAddress peer, PubKeyRing peersPubKeyRing, TradeMessage message, boolean result, @Nullable String errorMessage) {
// create ack message
String tradeId = message.getTradeId();
String sourceUid = message.getUid();
AckMessage ackMessage = new AckMessage(P2PService.getMyNodeAddress(),
AckMessageSourceType.TRADE_MESSAGE,
message.getClass().getSimpleName(),
sourceUid,
tradeId,
result,
errorMessage);
// send ack message
log.info("Send AckMessage for {} to peer {}. tradeId={}, sourceUid={}",
ackMessage.getSourceMsgClassName(), peer, tradeId, sourceUid);
p2PService.getMailboxMessageService().sendEncryptedMailboxMessage(
peer,
peersPubKeyRing,
ackMessage,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.info("AckMessage for {} arrived at peer {}. tradeId={}, sourceUid={}",
ackMessage.getSourceMsgClassName(), peer, tradeId, sourceUid);
}
@Override
public void onStoredInMailbox() {
log.info("AckMessage for {} stored in mailbox for peer {}. tradeId={}, sourceUid={}",
ackMessage.getSourceMsgClassName(), peer, tradeId, sourceUid);
}
@Override
public void onFault(String errorMessage) {
log.error("AckMessage for {} failed. Peer {}. tradeId={}, sourceUid={}, errorMessage={}",
ackMessage.getSourceMsgClassName(), peer, tradeId, sourceUid, errorMessage);
}
}
);
}
public ObservableList<Trade> getObservableList() { public ObservableList<Trade> getObservableList() {
synchronized (tradableList) { synchronized (tradableList) {
return tradableList.getObservableList(); return tradableList.getObservableList();

View File

@ -62,7 +62,6 @@ import haveno.network.p2p.AckMessageSourceType;
import haveno.network.p2p.DecryptedDirectMessageListener; import haveno.network.p2p.DecryptedDirectMessageListener;
import haveno.network.p2p.DecryptedMessageWithPubKey; import haveno.network.p2p.DecryptedMessageWithPubKey;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import haveno.network.p2p.SendMailboxMessageListener;
import haveno.network.p2p.mailbox.MailboxMessage; import haveno.network.p2p.mailbox.MailboxMessage;
import haveno.network.p2p.mailbox.MailboxMessageService; import haveno.network.p2p.mailbox.MailboxMessageService;
import haveno.network.p2p.messaging.DecryptedMailboxListener; import haveno.network.p2p.messaging.DecryptedMailboxListener;
@ -666,7 +665,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();
} }
handleError(err); handleError(ackMessage.getErrorMessage());
} }
} }
@ -679,44 +678,11 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
return; return;
} }
String tradeId = message.getTradeId(); // send ack message
String sourceUid = message.getUid(); processModel.getTradeManager().sendAckMessage(peer, peersPubKeyRing, message, result, errorMessage);
AckMessage ackMessage = new AckMessage(processModel.getMyNodeAddress(),
AckMessageSourceType.TRADE_MESSAGE,
message.getClass().getSimpleName(),
sourceUid,
tradeId,
result,
errorMessage);
log.info("Send AckMessage for {} to peer {}. tradeId={}, sourceUid={}",
ackMessage.getSourceMsgClassName(), peer, tradeId, sourceUid);
processModel.getP2PService().getMailboxMessageService().sendEncryptedMailboxMessage(
peer,
peersPubKeyRing,
ackMessage,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.info("AckMessage for {} arrived at peer {}. tradeId={}, sourceUid={}",
ackMessage.getSourceMsgClassName(), peer, tradeId, sourceUid);
}
@Override
public void onStoredInMailbox() {
log.info("AckMessage for {} stored in mailbox for peer {}. tradeId={}, sourceUid={}",
ackMessage.getSourceMsgClassName(), peer, tradeId, sourceUid);
}
@Override
public void onFault(String errorMessage) {
log.error("AckMessage for {} failed. Peer {}. tradeId={}, sourceUid={}, errorMessage={}",
ackMessage.getSourceMsgClassName(), peer, tradeId, sourceUid, errorMessage);
}
}
);
} }
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
// Timeout // Timeout
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////