prefer overriding state if valid phase transition

This commit is contained in:
woodser 2025-11-21 18:00:12 -05:00
parent 8ef279522d
commit d607508eee
No known key found for this signature in database
GPG key ID: 55A10DD48ADEE5EF
11 changed files with 34 additions and 29 deletions

View file

@ -660,7 +660,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
if (trade.isArbitrator() && message.getPaymentSentMessage() != null) { if (trade.isArbitrator() && message.getPaymentSentMessage() != null) {
HavenoUtils.verifyPaymentSentMessage(trade, message.getPaymentSentMessage()); HavenoUtils.verifyPaymentSentMessage(trade, message.getPaymentSentMessage());
trade.getBuyer().setUpdatedMultisigHex(message.getPaymentSentMessage().getUpdatedMultisigHex()); trade.getBuyer().setUpdatedMultisigHex(message.getPaymentSentMessage().getUpdatedMultisigHex());
trade.advanceState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); trade.setStateIfValidTransitionTo(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG);
} }
// update opener's multisig hex // update opener's multisig hex

View file

@ -2275,12 +2275,6 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
// Setters // Setters
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public void setStateIfValidTransitionTo(State newState) {
if (state.isValidTransitionTo(newState)) {
setState(newState);
}
}
public void addInitProgressStep() { public void addInitProgressStep() {
startProtocolTimeout(); startProtocolTimeout();
initProgress = Math.min(1.0, (double) ++initStep / TOTAL_INIT_STEPS); initProgress = Math.min(1.0, (double) ++initStep / TOTAL_INIT_STEPS);
@ -2299,6 +2293,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
protocol.stopTimeout(); protocol.stopTimeout();
} }
public void setStateIfValidTransitionTo(State newState) {
if (state.isValidTransitionTo(newState)) {
setState(newState);
}
}
public void setState(State state) { public void setState(State state) {
// skip if no change // skip if no change
@ -2751,10 +2751,6 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
} }
} }
public boolean isPaymentSent() {
return getState().getPhase().ordinal() >= Phase.PAYMENT_SENT.ordinal() && getState() != State.BUYER_SEND_FAILED_PAYMENT_SENT_MSG;
}
public boolean hasPaymentSentMessage() { public boolean hasPaymentSentMessage() {
return (isBuyer() ? getSeller() : getBuyer()).getPaymentSentMessage() != null; // buyer stores message to seller and arbitrator, peers store message from buyer return (isBuyer() ? getSeller() : getBuyer()).getPaymentSentMessage() != null; // buyer stores message to seller and arbitrator, peers store message from buyer
} }
@ -2777,6 +2773,10 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
return getState().getPhase().ordinal() >= Phase.PAYMENT_SENT.ordinal(); return getState().getPhase().ordinal() >= Phase.PAYMENT_SENT.ordinal();
} }
public boolean isPaymentSent() {
return getState().getPhase().ordinal() >= Phase.PAYMENT_SENT.ordinal() && getState() != State.BUYER_SEND_FAILED_PAYMENT_SENT_MSG;
}
public boolean isPaymentMarkedReceived() { public boolean isPaymentMarkedReceived() {
return getState().getPhase().ordinal() >= Phase.PAYMENT_RECEIVED.ordinal(); return getState().getPhase().ordinal() >= Phase.PAYMENT_RECEIVED.ordinal();
} }
@ -3813,7 +3813,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
} }
private void setStateDepositsFinalized() { private void setStateDepositsFinalized() {
if (!isPaymentSent() && !isDepositsFinalized()) advanceState(State.DEPOSIT_TXS_FINALIZED_IN_BLOCKCHAIN); // do not revert state if payment already sent if (!isPaymentSent() && !isDepositsFinalized()) setStateIfValidTransitionTo(State.DEPOSIT_TXS_FINALIZED_IN_BLOCKCHAIN); // do not revert state if payment already sent
try { try {
maybeUpdateTradePeriod(); maybeUpdateTradePeriod();
} catch (Exception e) { } catch (Exception e) {

View file

@ -127,7 +127,7 @@ public class BuyerProtocol extends DisputeProtocol {
// advance trade state // advance trade state
if (trade.isDepositsUnlocked() || trade.isDepositsFinalized() || trade.isPaymentSent()) { if (trade.isDepositsUnlocked() || trade.isDepositsFinalized() || trade.isPaymentSent()) {
trade.advanceState(Trade.State.BUYER_CONFIRMED_PAYMENT_SENT); trade.setStateIfValidTransitionTo(Trade.State.BUYER_CONFIRMED_PAYMENT_SENT);
} else { } else {
errorMessageHandler.handleErrorMessage("Cannot confirm payment sent for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " in state " + trade.getState()); errorMessageHandler.handleErrorMessage("Cannot confirm payment sent for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " in state " + trade.getState());
return; return;

View file

@ -123,7 +123,7 @@ public class SellerProtocol extends DisputeProtocol {
// advance trade state // advance trade state
if (trade.isPaymentSent() || trade.isPaymentReceived()) { if (trade.isPaymentSent() || trade.isPaymentReceived()) {
trade.advanceState(Trade.State.SELLER_CONFIRMED_PAYMENT_RECEIPT); trade.setStateIfValidTransitionTo(Trade.State.SELLER_CONFIRMED_PAYMENT_RECEIPT);
} else { } else {
errorMessageHandler.handleErrorMessage("Cannot confirm payment received for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " in state " + trade.getState()); errorMessageHandler.handleErrorMessage("Cannot confirm payment received for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " in state " + trade.getState());
return; return;

View file

@ -620,7 +620,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
// the mailbox msg once wallet is ready and trade state set. // the mailbox msg once wallet is ready and trade state set.
synchronized (trade.getLock()) { synchronized (trade.getLock()) {
if (!trade.isInitialized() || trade.isShutDownStarted()) return; if (!trade.isInitialized() || trade.isShutDownStarted()) return;
if (trade.getPhase().ordinal() >= Trade.Phase.PAYMENT_SENT.ordinal()) { if (trade.getState().ordinal() >= Trade.State.BUYER_SENT_PAYMENT_SENT_MSG.ordinal()) {
log.warn("Received another PaymentSentMessage which was already processed for {} {}, ACKing", trade.getClass().getSimpleName(), trade.getId()); log.warn("Received another PaymentSentMessage which was already processed for {} {}, ACKing", trade.getClass().getSimpleName(), trade.getId());
handleTaskRunnerSuccess(trade.getBuyer().getNodeAddress(), message); handleTaskRunnerSuccess(trade.getBuyer().getNodeAddress(), message);
return; return;

View file

@ -39,7 +39,11 @@ public class BuyerSendPaymentSentMessageToSeller extends BuyerSendPaymentSentMes
@Override @Override
protected void setStateSent() { protected void setStateSent() {
if (trade.getState().ordinal() < Trade.State.BUYER_SENT_PAYMENT_SENT_MSG.ordinal()) trade.setStateIfValidTransitionTo(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); if (trade.getState().equals(Trade.State.BUYER_SEND_FAILED_PAYMENT_SENT_MSG)) {
trade.setState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG);
} else {
trade.advanceState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); // do not revert previous send progress
}
super.setStateSent(); super.setStateSent();
} }

View file

@ -25,7 +25,6 @@ import haveno.core.trade.BuyerTrade;
import haveno.core.trade.HavenoUtils; import haveno.core.trade.HavenoUtils;
import haveno.core.trade.MakerTrade; import haveno.core.trade.MakerTrade;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.Trade.State;
import haveno.core.trade.messages.SignContractRequest; import haveno.core.trade.messages.SignContractRequest;
import haveno.core.trade.protocol.TradeProtocol; import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.model.XmrAddressEntry;
@ -217,7 +216,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
} }
private void completeAux() { private void completeAux() {
trade.setState(State.CONTRACT_SIGNATURE_REQUESTED); trade.setState(Trade.State.CONTRACT_SIGNATURE_REQUESTED);
trade.addInitProgressStep(); trade.addInitProgressStep();
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();
complete(); complete();

View file

@ -40,7 +40,6 @@ import haveno.core.support.dispute.Dispute;
import haveno.core.trade.BuyerTrade; import haveno.core.trade.BuyerTrade;
import haveno.core.trade.HavenoUtils; import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.Trade.State;
import haveno.core.trade.messages.PaymentReceivedMessage; import haveno.core.trade.messages.PaymentReceivedMessage;
import haveno.core.trade.messages.PaymentSentMessage; import haveno.core.trade.messages.PaymentSentMessage;
import haveno.core.util.Validator; import haveno.core.util.Validator;
@ -74,14 +73,14 @@ public class ProcessPaymentReceivedMessage extends TradeTask {
trade.requestPersistence(); trade.requestPersistence();
// ack and complete if already processed // ack and complete if already processed
if (trade.getState().ordinal() >= State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal() && trade.isPayoutPublished()) { if (trade.getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal() && trade.isPayoutPublished()) {
log.warn("Received another PaymentReceivedMessage which was already processed, ACKing"); log.warn("Received another PaymentReceivedMessage which was already processed, ACKing");
complete(); complete();
return; return;
} }
// set state to confirmed payment receipt before processing // set state to confirmed payment receipt before processing
trade.advanceState(State.SELLER_CONFIRMED_PAYMENT_RECEIPT); trade.setStateIfValidTransitionTo(Trade.State.SELLER_CONFIRMED_PAYMENT_RECEIPT);
// cannot process until wallet sees deposits unlocked // cannot process until wallet sees deposits unlocked
if (!trade.isDepositsUnlocked()) { if (!trade.isDepositsUnlocked()) {
@ -111,7 +110,7 @@ public class ProcessPaymentReceivedMessage extends TradeTask {
} }
// advance state, arbitrator auto completes when payout published // advance state, arbitrator auto completes when payout published
trade.advanceState(Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG); trade.setStateIfValidTransitionTo(Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG);
// buyer republishes signed witness for resilience // buyer republishes signed witness for resilience
SignedWitness signedWitness = message.getBuyerSignedWitness(); SignedWitness signedWitness = message.getBuyerSignedWitness();

View file

@ -68,7 +68,7 @@ public class ProcessPaymentSentMessage extends TradeTask {
if (trade.isSeller()) trade.decryptPeerPaymentAccountPayload(message.getPaymentAccountKey()); if (trade.isSeller()) trade.decryptPeerPaymentAccountPayload(message.getPaymentAccountKey());
// update state // update state
trade.advanceState(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG); trade.setStateIfValidTransitionTo(Trade.State.BUYER_SENT_PAYMENT_SENT_MSG);
trade.requestPersistence(); trade.requestPersistence();
complete(); complete();
} catch (Throwable t) { } catch (Throwable t) {

View file

@ -29,7 +29,6 @@ import haveno.core.trade.ArbitratorTrade;
import haveno.core.trade.Contract; import haveno.core.trade.Contract;
import haveno.core.trade.HavenoUtils; import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.Trade.State;
import haveno.core.trade.messages.SignContractRequest; import haveno.core.trade.messages.SignContractRequest;
import haveno.core.trade.messages.SignContractResponse; import haveno.core.trade.messages.SignContractResponse;
import haveno.core.trade.protocol.TradePeer; import haveno.core.trade.protocol.TradePeer;
@ -162,7 +161,7 @@ public class ProcessSignContractRequest extends TradeTask {
private void completeAux() { private void completeAux() {
trade.addInitProgressStep(); trade.addInitProgressStep();
trade.setState(State.CONTRACT_SIGNED); trade.setState(Trade.State.CONTRACT_SIGNED);
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();
complete(); complete();
} }

View file

@ -39,25 +39,29 @@ public class SellerSendPaymentReceivedMessageToBuyer extends SellerSendPaymentRe
@Override @Override
protected void setStateSent() { protected void setStateSent() {
trade.advanceState(Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG); if (trade.getState().equals(Trade.State.SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG)) {
trade.setState(Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG);
} else {
trade.advanceState(Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG); // do not revert previous send progress
}
super.setStateSent(); super.setStateSent();
} }
@Override @Override
protected void setStateFault() { protected void setStateFault() {
trade.advanceState(Trade.State.SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG); trade.setStateIfValidTransitionTo(Trade.State.SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG);
super.setStateFault(); super.setStateFault();
} }
@Override @Override
protected void setStateStoredInMailbox() { protected void setStateStoredInMailbox() {
trade.advanceState(Trade.State.SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG); trade.setStateIfValidTransitionTo(Trade.State.SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG);
super.setStateStoredInMailbox(); super.setStateStoredInMailbox();
} }
@Override @Override
protected void setStateArrived() { protected void setStateArrived() {
trade.advanceState(Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG); trade.setStateIfValidTransitionTo(Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG);
super.setStateArrived(); super.setStateArrived();
} }