fix message spam by resending payment received messages when applicable

This commit is contained in:
woodser 2025-09-27 00:42:58 -04:00
parent 6214f4a120
commit b65f83372d
No known key found for this signature in database
GPG key ID: 55A10DD48ADEE5EF
5 changed files with 37 additions and 18 deletions

View file

@ -69,7 +69,9 @@ public abstract class SellerTrade extends Trade {
}
public boolean needsToResendPaymentReceivedMessages() {
return !isShutDownStarted() && getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal() && !getProcessModel().isPaymentReceivedMessagesReceived() && resendPaymentReceivedMessagesEnabled() && resendPaymentReceivedMessagesWithinDuration();
boolean hasNoPaymentReceivedMessages = getBuyer().getPaymentReceivedMessage() == null && getArbitrator().getPaymentReceivedMessage() == null;
if (!walletExists() && !hasNoPaymentReceivedMessages) return false; // cannot provide any updated state
return !isShutDownStarted() && getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal() && !getProcessModel().isPaymentReceivedMessagesAckedOrStored() && resendPaymentReceivedMessagesEnabled() && resendPaymentReceivedMessagesWithinDuration();
}
private boolean resendPaymentReceivedMessagesEnabled() {
@ -81,4 +83,3 @@ public abstract class SellerTrade extends Trade {
return new Date().getTime() <= (startDate.getTime() + resendPaymentReceivedMessagesDurationMs);
}
}

View file

@ -1704,14 +1704,14 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
}
// TODO: clear other process data
if (processModel.isPaymentReceivedMessagesReceived()) setPayoutTxHex(null);
if (processModel.isPaymentReceivedMessagesAckedOrStored()) setPayoutTxHex(null);
for (TradePeer peer : getAllPeers()) {
peer.setUpdatedMultisigHex(null);
peer.setDisputeClosedMessage(null);
peer.setPaymentSentMessage(null);
peer.setDepositTxHex(null);
peer.setDepositTxKey(null);
if (peer.isPaymentReceivedMessageReceived()) {
if (peer.isPaymentReceivedMessageAckedOrStored()) {
peer.setUnsignedPayoutTxHex(null);
peer.setPaymentReceivedMessage(null);
}
@ -3249,8 +3249,8 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
persistNow(null);
// send updated payment received message when payout is confirmed
if (resendPaymentReceivedMessages) {
// send updated payment received message if applicable
if (resendPaymentReceivedMessages && walletExists()) {
if (!isSeller()) throw new IllegalArgumentException("Only the seller can resend PaymentReceivedMessages after a payout error for " + getClass().getSimpleName() + " " + getId());
if (!isPaymentReceived()) throw new IllegalStateException("Cannot resend PaymentReceivedMessages after a payout error for " + getClass().getSimpleName() + " " + getId() + " because payment not marked received");
log.warn("Sending updated PaymentReceivedMessages for {} {} after payout error", getClass().getSimpleName(), getId());

View file

@ -309,8 +309,8 @@ public class ProcessModel implements Model, PersistablePayload {
return getP2PService().getAddress();
}
public boolean isPaymentReceivedMessagesReceived() {
return getArbitrator().isPaymentReceivedMessageReceived() && getBuyer().isPaymentReceivedMessageReceived();
public boolean isPaymentReceivedMessagesAckedOrStored() {
return getArbitrator().isPaymentReceivedMessageAckedOrStored() && getBuyer().isPaymentReceivedMessageAckedOrStored();
}
void setDepositTxSentAckMessage(AckMessage ackMessage) {

View file

@ -256,9 +256,17 @@ public final class TradePeer implements PersistablePayload {
}
public boolean isPaymentReceivedMessageReceived() {
return isPaymentReceivedMessageAckedOrStored() || isPaymentReceivedMessageNacked();
}
public boolean isPaymentReceivedMessageAckedOrStored() {
return paymentReceivedMessageStateProperty.get() == MessageState.ACKNOWLEDGED || paymentReceivedMessageStateProperty.get() == MessageState.STORED_IN_MAILBOX;
}
public boolean isPaymentReceivedMessageNacked() {
return paymentReceivedMessageStateProperty.get() == MessageState.NACKED;
}
public boolean isPaymentReceivedMessageArrived() {
return paymentReceivedMessageStateProperty.get() == MessageState.ARRIVED;
}

View file

@ -74,6 +74,7 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
private String unsignedPayoutTxHex = null;
private String signedPayoutTxHex = null;
private String updatedMultisigHex = null;
private PaymentReceivedMessage message = null;
public SellerSendPaymentReceivedMessage(TaskRunner<Trade> taskHandler, Trade trade) {
super(taskHandler, trade);
@ -96,8 +97,10 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
try {
runInterceptHook();
// reset ack state
getReceiver().setPaymentReceivedMessageState(MessageState.UNDEFINED);
// reset nack state
if (getReceiver().isPaymentReceivedMessageNacked()) {
getReceiver().setPaymentReceivedMessageState(MessageState.UNDEFINED);
}
// skip if stopped
if (stopSending()) {
@ -105,6 +108,8 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
return;
}
// reset ack state
getReceiver().setPaymentReceivedMessageState(MessageState.UNDEFINED);
super.run();
} catch (Throwable t) {
failed(t);
@ -116,13 +121,17 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
if (getReceiver().getPaymentReceivedMessage() == null) {
// sign account witness
AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService();
if (accountAgeWitnessService.isSignWitnessTrade(trade)) {
try {
accountAgeWitnessService.traderSignAndPublishPeersAccountAgeWitness(trade).ifPresent(witness -> signedWitness = witness);
log.info("{} {} signed and published peers account age witness", trade.getClass().getSimpleName(), trade.getId());
} catch (Exception e) {
log.warn("Failed to sign and publish peer's account age witness for {} {}, error={}\n", getClass().getSimpleName(), trade.getId(), e.getMessage(), e);
if (trade.getSelf().getPaymentAccountPayload() == null) {
log.warn("Cannot sign account age witness for {} {} as no payment account is set", trade.getClass().getSimpleName(), trade.getId());
} else {
AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService();
if (accountAgeWitnessService.isSignWitnessTrade(trade)) {
try {
accountAgeWitnessService.traderSignAndPublishPeersAccountAgeWitness(trade).ifPresent(witness -> signedWitness = witness);
log.info("{} {} signed and published peers account age witness", trade.getClass().getSimpleName(), trade.getId());
} catch (Exception e) {
log.warn("Failed to sign and publish peer's account age witness for {} {}, error={}\n", getClass().getSimpleName(), trade.getId(), e.getMessage(), e);
}
}
}
@ -135,7 +144,7 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
unsignedPayoutTxHex = trade.getPayoutTxHex() == null ? trade.getSelf().getUnsignedPayoutTxHex() : null; // signed
signedPayoutTxHex = trade.getPayoutTxHex();
updatedMultisigHex = trade.getSelf().getUpdatedMultisigHex();
PaymentReceivedMessage message = new PaymentReceivedMessage(
message = new PaymentReceivedMessage(
tradeId,
processModel.getMyNodeAddress(),
deterministicId,
@ -258,6 +267,7 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
if (trade.isPayoutPublished() && !((SellerTrade) trade).resendPaymentReceivedMessagesWithinDuration()) return true; // stop if payout is published and we are not in the resend period
// check if message state is outdated
if (message != null && !message.equals(getReceiver().getPaymentReceivedMessage())) return true;
if (unsignedPayoutTxHex != null && !StringUtils.equals(unsignedPayoutTxHex, trade.getSelf().getUnsignedPayoutTxHex())) return true;
if (signedPayoutTxHex != null && !StringUtils.equals(signedPayoutTxHex, trade.getPayoutTxHex())) return true;
if (updatedMultisigHex != null && !StringUtils.equals(updatedMultisigHex, trade.getSelf().getUpdatedMultisigHex())) return true;