stop sending if message state is outdated

This commit is contained in:
woodser 2025-10-08 15:02:29 -04:00
parent ee15988c59
commit 0fd1817053
No known key found for this signature in database
GPG key ID: 55A10DD48ADEE5EF
3 changed files with 87 additions and 59 deletions

View file

@ -26,6 +26,7 @@ import haveno.network.p2p.NodeAddress;
import lombok.extern.slf4j.Slf4j;
import java.math.BigInteger;
import java.util.Date;
import java.util.UUID;
import javax.annotation.Nullable;
@ -36,62 +37,69 @@ import javax.annotation.Nullable;
@Slf4j
public class ArbitratorTrade extends Trade {
public ArbitratorTrade(Offer offer,
BigInteger tradeAmount,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
String uid,
NodeAddress makerNodeAddress,
NodeAddress takerNodeAddress,
NodeAddress arbitratorNodeAddress,
@Nullable String challenge) {
super(offer, tradeAmount, tradePrice, xmrWalletService, processModel, uid, makerNodeAddress, takerNodeAddress, arbitratorNodeAddress, challenge);
}
private static final long resendDisputeOpenedMessageDurationMs = 1L * 30 * 24 * 60 * 60 * 1000; // 30 days
@Override
public BigInteger getPayoutAmountBeforeCost() {
throw new RuntimeException("Arbitrator does not have a payout amount");
}
public ArbitratorTrade(Offer offer,
BigInteger tradeAmount,
long tradePrice,
XmrWalletService xmrWalletService,
ProcessModel processModel,
String uid,
NodeAddress makerNodeAddress,
NodeAddress takerNodeAddress,
NodeAddress arbitratorNodeAddress,
@Nullable String challenge) {
super(offer, tradeAmount, tradePrice, xmrWalletService, processModel, uid, makerNodeAddress, takerNodeAddress, arbitratorNodeAddress, challenge);
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
@Override
public BigInteger getPayoutAmountBeforeCost() {
throw new RuntimeException("Arbitrator does not have a payout amount");
}
@Override
public protobuf.Tradable toProtoMessage() {
return protobuf.Tradable.newBuilder()
.setArbitratorTrade(protobuf.ArbitratorTrade.newBuilder()
.setTrade((protobuf.Trade) super.toProtoMessage()))
.build();
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
public static Tradable fromProto(protobuf.ArbitratorTrade arbitratorTradeProto,
XmrWalletService xmrWalletService,
CoreProtoResolver coreProtoResolver) {
protobuf.Trade proto = arbitratorTradeProto.getTrade();
ProcessModel processModel = ProcessModel.fromProto(proto.getProcessModel(), coreProtoResolver);
String uid = ProtoUtil.stringOrNullFromProto(proto.getUid());
if (uid == null) {
uid = UUID.randomUUID().toString();
}
return fromProto(new ArbitratorTrade(
Offer.fromProto(proto.getOffer()),
BigInteger.valueOf(proto.getAmount()),
proto.getPrice(),
xmrWalletService,
processModel,
uid,
proto.getProcessModel().getMaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getMaker().getNodeAddress()) : null,
proto.getProcessModel().getTaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getTaker().getNodeAddress()) : null,
proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null,
ProtoUtil.stringOrNullFromProto(proto.getChallenge())),
proto,
coreProtoResolver);
}
@Override
public protobuf.Tradable toProtoMessage() {
return protobuf.Tradable.newBuilder()
.setArbitratorTrade(protobuf.ArbitratorTrade.newBuilder()
.setTrade((protobuf.Trade) super.toProtoMessage()))
.build();
}
@Override
public boolean confirmPermitted() {
throw new RuntimeException("ArbitratorTrade.confirmPermitted() not implemented"); // TODO (woodser): implement
}
public static Tradable fromProto(protobuf.ArbitratorTrade arbitratorTradeProto,
XmrWalletService xmrWalletService,
CoreProtoResolver coreProtoResolver) {
protobuf.Trade proto = arbitratorTradeProto.getTrade();
ProcessModel processModel = ProcessModel.fromProto(proto.getProcessModel(), coreProtoResolver);
String uid = ProtoUtil.stringOrNullFromProto(proto.getUid());
if (uid == null) {
uid = UUID.randomUUID().toString();
}
return fromProto(new ArbitratorTrade(
Offer.fromProto(proto.getOffer()),
BigInteger.valueOf(proto.getAmount()),
proto.getPrice(),
xmrWalletService,
processModel,
uid,
proto.getProcessModel().getMaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getMaker().getNodeAddress()) : null,
proto.getProcessModel().getTaker().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getTaker().getNodeAddress()) : null,
proto.getProcessModel().getArbitrator().hasNodeAddress() ? NodeAddress.fromProto(proto.getProcessModel().getArbitrator().getNodeAddress()) : null,
ProtoUtil.stringOrNullFromProto(proto.getChallenge())),
proto,
coreProtoResolver);
}
@Override
public boolean confirmPermitted() {
throw new RuntimeException("ArbitratorTrade.confirmPermitted() not implemented"); // TODO (woodser): implement
}
public boolean resendDisputeOpenedMessageWithinDuration() {
Date startDate = getMaxTradePeriodDate();
return new Date().getTime() <= (startDate.getTime() + resendDisputeOpenedMessageDurationMs);
}
}

View file

@ -30,7 +30,7 @@ import java.util.Date;
@Slf4j
public abstract class SellerTrade extends Trade {
private static final long resendPaymentReceivedMessagesDurationMs = 1L * 30 * 24 * 60 * 60 * 1000; // ~1 month
private static final long resendPaymentReceivedMessagesDurationMs = 1L * 30 * 24 * 60 * 60 * 1000; // 30 days
SellerTrade(Offer offer,
BigInteger tradeAmount,

View file

@ -25,7 +25,9 @@ import haveno.common.UserThread;
import haveno.common.taskrunner.TaskRunner;
import haveno.core.network.MessageState;
import haveno.core.support.dispute.Dispute;
import haveno.core.support.dispute.messages.DisputeOpenedMessage;
import haveno.core.support.messages.ChatMessage;
import haveno.core.trade.ArbitratorTrade;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade;
import haveno.network.p2p.mailbox.MailboxMessage;
@ -50,6 +52,7 @@ public abstract class ArbitratorSendDisputeOpenedMessage extends SendMailboxMess
private static final int MAX_RESEND_ATTEMPTS = 20;
private int delayInMin = 10;
private int resendCounter = 0;
private DisputeOpenedMessage message = null;
public ArbitratorSendDisputeOpenedMessage(TaskRunner<Trade> taskHandler, Trade trade) {
super(taskHandler, trade);
@ -60,12 +63,19 @@ public abstract class ArbitratorSendDisputeOpenedMessage extends SendMailboxMess
try {
runInterceptHook();
// reset nack state
if (getReceiver().isDisputeOpenedMessageReceived()) {
getReceiver().setDisputeOpenedMessageState(MessageState.UNDEFINED);
}
// skip if not applicable or already acked
if (getReceiver().getDisputeOpenedMessage() == null || isAckedByReceiver()) {
if (stopSending()) {
if (!isCompleted()) complete();
return;
}
// reset ack state
getReceiver().setPaymentReceivedMessageState(MessageState.UNDEFINED);
super.run();
} catch (Throwable t) {
failed(t);
@ -82,7 +92,8 @@ public abstract class ArbitratorSendDisputeOpenedMessage extends SendMailboxMess
@Override
protected MailboxMessage getMailboxMessage(String tradeId) {
return getReceiver().getDisputeOpenedMessage();
if (message == null) message = getReceiver().getDisputeOpenedMessage();
return message;
}
@Override
@ -125,7 +136,7 @@ public abstract class ArbitratorSendDisputeOpenedMessage extends SendMailboxMess
private void tryToSendAgainLater() {
// skip if already acked
if (isAckedByReceiver()) return;
if (stopSending()) return;
if (resendCounter >= MAX_RESEND_ATTEMPTS) {
cleanup();
@ -159,12 +170,21 @@ public abstract class ArbitratorSendDisputeOpenedMessage extends SendMailboxMess
}
private void onMessageStateChange(MessageState newValue) {
if (isAckedByReceiver()) {
if (isMessageReceived()) {
cleanup();
}
}
protected boolean isAckedByReceiver() {
protected boolean isMessageReceived() {
return getReceiver().isDisputeOpenedMessageReceived();
}
protected boolean stopSending() {
if (getReceiver().getDisputeOpenedMessage() == null) return true; // stop if no message to send
if (isMessageReceived()) return true; // stop if message received
if (trade.isPayoutPublished()) return true; // stop if payout is published
if (!((ArbitratorTrade) trade).resendDisputeOpenedMessageWithinDuration()) return true; // stop if payout is published and we are not in the resend period
if (message != null && !message.equals(getReceiver().getDisputeOpenedMessage())) return true; // stop if message state is outdated
return false;
}
}