Use fluent interface for checking state and conditions

This commit is contained in:
chimp1984 2020-09-23 01:48:02 -05:00
parent 7c6f0ac9b2
commit 6fa2225b65
No known key found for this signature in database
GPG Key ID: 9801B4EC591F90E3
11 changed files with 345 additions and 309 deletions

View File

@ -53,8 +53,6 @@ import bisq.common.handlers.ResultHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
@Slf4j @Slf4j
public class BuyerAsMakerProtocol extends TradeProtocol implements BuyerProtocol, MakerProtocol { public class BuyerAsMakerProtocol extends TradeProtocol implements BuyerProtocol, MakerProtocol {
private final BuyerAsMakerTrade buyerAsMakerTrade; private final BuyerAsMakerTrade buyerAsMakerTrade;
@ -118,8 +116,9 @@ public class BuyerAsMakerProtocol extends TradeProtocol implements BuyerProtocol
public void handleTakeOfferRequest(InputsForDepositTxRequest tradeMessage, public void handleTakeOfferRequest(InputsForDepositTxRequest tradeMessage,
NodeAddress peerNodeAddress, NodeAddress peerNodeAddress,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
ifInPhase(Trade.Phase.INIT, tradeMessage) from(Trade.Phase.INIT)
.run(() -> { .onMessage(tradeMessage)
.process(() -> {
Validator.checkTradeId(processModel.getOfferId(), tradeMessage); Validator.checkTradeId(processModel.getOfferId(), tradeMessage);
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress); processModel.setTempTradingPeerNodeAddress(peerNodeAddress);
@ -151,8 +150,9 @@ public class BuyerAsMakerProtocol extends TradeProtocol implements BuyerProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handle(DelayedPayoutTxSignatureRequest tradeMessage, NodeAddress peerNodeAddress) { private void handle(DelayedPayoutTxSignatureRequest tradeMessage, NodeAddress peerNodeAddress) {
ifInPhase(Trade.Phase.TAKER_FEE_PUBLISHED, tradeMessage) from(Trade.Phase.TAKER_FEE_PUBLISHED)
.run(() -> { .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress); processModel.setTempTradingPeerNodeAddress(peerNodeAddress);
@ -178,17 +178,17 @@ public class BuyerAsMakerProtocol extends TradeProtocol implements BuyerProtocol
// mailbox message but the stored in mailbox case is not expected and the seller would try to send the message again // mailbox message but the stored in mailbox case is not expected and the seller would try to send the message again
// in the hope to reach the buyer directly. // in the hope to reach the buyer directly.
private void handle(DepositTxAndDelayedPayoutTxMessage tradeMessage, NodeAddress peerNodeAddress) { private void handle(DepositTxAndDelayedPayoutTxMessage tradeMessage, NodeAddress peerNodeAddress) {
if (trade.getDepositTx() != null && trade.getDelayedPayoutTx() != null) { fromAny(Trade.Phase.TAKER_FEE_PUBLISHED, Trade.Phase.DEPOSIT_PUBLISHED)
log.warn("We received a DepositTxAndDelayedPayoutTxMessage but we have already processed the deposit and " + .onMessage(tradeMessage)
"delayed payout tx so we ignore the message. This can happen if the ACK message to the peer did not " + .condition(trade.getDepositTx() == null || trade.getDelayedPayoutTx() == null,
"arrive and the peer repeats sending us the message. We send another ACK msg."); () -> {
sendAckMessage(tradeMessage, true, null); log.warn("We received a DepositTxAndDelayedPayoutTxMessage but we have already processed the deposit and " +
processModel.removeMailboxMessageAfterProcessing(trade); "delayed payout tx so we ignore the message. This can happen if the ACK message to the peer did not " +
return; "arrive and the peer repeats sending us the message. We send another ACK msg.");
} sendAckMessage(tradeMessage, true, null);
processModel.removeMailboxMessageAfterProcessing(trade);
ifInPhase(Trade.Phase.TAKER_FEE_PUBLISHED, tradeMessage).orInPhase(Trade.Phase.DEPOSIT_PUBLISHED) })
.run(() -> { .process(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress); processModel.setTempTradingPeerNodeAddress(peerNodeAddress);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade,
@ -213,11 +213,10 @@ public class BuyerAsMakerProtocol extends TradeProtocol implements BuyerProtocol
@Override @Override
public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
checkArgument(!wasDisputed(), "A call to onFiatPaymentStarted is not permitted once a " + from(Trade.Phase.DEPOSIT_CONFIRMED)
"dispute has been opened."); .onEvent(BuyerEvent.PAYMENT_SENT)
.condition(!wasDisputed())
ifInPhase(Trade.Phase.DEPOSIT_CONFIRMED) .process(() -> {
.run(() -> {
buyerAsMakerTrade.setState(Trade.State.BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED); buyerAsMakerTrade.setState(Trade.State.BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade,
() -> { () -> {
@ -245,8 +244,9 @@ public class BuyerAsMakerProtocol extends TradeProtocol implements BuyerProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeAddress) { private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeAddress) {
ifInPhase(Trade.Phase.FIAT_SENT, tradeMessage).orInPhase(Trade.Phase.PAYOUT_PUBLISHED) fromAny(Trade.Phase.FIAT_SENT, Trade.Phase.PAYOUT_PUBLISHED)
.run(() -> { .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress); processModel.setTempTradingPeerNodeAddress(peerNodeAddress);

View File

@ -57,7 +57,6 @@ import bisq.common.handlers.ResultHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
@ -125,8 +124,9 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
@Override @Override
public void takeAvailableOffer() { public void takeAvailableOffer() {
ifInPhase(Trade.Phase.INIT) from(Trade.Phase.INIT)
.run(() -> { .onEvent(TakerEvent.TAKE_OFFER)
.process(() -> {
processModel.setTempTradingPeerNodeAddress(trade.getTradingPeerNodeAddress()); processModel.setTempTradingPeerNodeAddress(trade.getTradingPeerNodeAddress());
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade,
() -> handleTaskRunnerSuccess("takeAvailableOffer"), () -> handleTaskRunnerSuccess("takeAvailableOffer"),
@ -153,8 +153,9 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handle(InputsForDepositTxResponse tradeMessage, NodeAddress sender) { private void handle(InputsForDepositTxResponse tradeMessage, NodeAddress sender) {
ifInPhase(Trade.Phase.INIT, tradeMessage) from(Trade.Phase.INIT)
.run(() -> { .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender); processModel.setTempTradingPeerNodeAddress(sender);
@ -178,8 +179,9 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
} }
private void handle(DelayedPayoutTxSignatureRequest tradeMessage, NodeAddress sender) { private void handle(DelayedPayoutTxSignatureRequest tradeMessage, NodeAddress sender) {
ifInPhase(Trade.Phase.TAKER_FEE_PUBLISHED, tradeMessage) from(Trade.Phase.TAKER_FEE_PUBLISHED)
.run(() -> { .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender); processModel.setTempTradingPeerNodeAddress(sender);
@ -208,17 +210,17 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
// mailbox message but the stored in mailbox case is not expected and the seller would try to send the message again // mailbox message but the stored in mailbox case is not expected and the seller would try to send the message again
// in the hope to reach the buyer directly. // in the hope to reach the buyer directly.
private void handle(DepositTxAndDelayedPayoutTxMessage tradeMessage, NodeAddress peerNodeAddress) { private void handle(DepositTxAndDelayedPayoutTxMessage tradeMessage, NodeAddress peerNodeAddress) {
if (trade.getDepositTx() != null && trade.getDelayedPayoutTx() != null) { fromAny(Trade.Phase.TAKER_FEE_PUBLISHED, Trade.Phase.DEPOSIT_PUBLISHED)
log.warn("We received a DepositTxAndDelayedPayoutTxMessage but we have already processed the deposit and " + .onMessage(tradeMessage)
"delayed payout tx so we ignore the message. This can happen if the ACK message to the peer did not " + .condition(trade.getDepositTx() == null || trade.getDelayedPayoutTx() == null,
"arrive and the peer repeats sending us the message. We send another ACK msg."); () -> {
sendAckMessage(tradeMessage, true, null); log.warn("We received a DepositTxAndDelayedPayoutTxMessage but we have already processed the deposit and " +
processModel.removeMailboxMessageAfterProcessing(trade); "delayed payout tx so we ignore the message. This can happen if the ACK message to the peer did not " +
return; "arrive and the peer repeats sending us the message. We send another ACK msg.");
} sendAckMessage(tradeMessage, true, null);
processModel.removeMailboxMessageAfterProcessing(trade);
ifInPhase(Trade.Phase.TAKER_FEE_PUBLISHED, tradeMessage).orInPhase(Trade.Phase.DEPOSIT_PUBLISHED) })
.run(() -> { .process(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress); processModel.setTempTradingPeerNodeAddress(peerNodeAddress);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade,
@ -231,9 +233,7 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
); );
taskRunner.run(); taskRunner.run();
processModel.witnessDebugLog(buyerAsTakerTrade); processModel.witnessDebugLog(buyerAsTakerTrade);
}).otherWise(() -> { });
log.warn("");
});
} }
@ -244,11 +244,10 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
// User clicked the "bank transfer started" button // User clicked the "bank transfer started" button
@Override @Override
public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
checkArgument(!wasDisputed(), "A call to onFiatPaymentStarted is not permitted once a " + from(Trade.Phase.DEPOSIT_CONFIRMED)
"dispute has been opened."); .onEvent(BuyerEvent.PAYMENT_SENT)
.condition(!wasDisputed())
ifInPhase(Trade.Phase.DEPOSIT_CONFIRMED) .process(() -> {
.run(() -> {
buyerAsTakerTrade.setState(Trade.State.BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED); buyerAsTakerTrade.setState(Trade.State.BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED);
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade,
() -> { () -> {
@ -276,8 +275,9 @@ public class BuyerAsTakerProtocol extends TradeProtocol implements BuyerProtocol
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeAddress) { private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeAddress) {
ifInPhase(Trade.Phase.FIAT_SENT, tradeMessage).orInPhase(Trade.Phase.PAYOUT_PUBLISHED) fromAny(Trade.Phase.FIAT_SENT, Trade.Phase.PAYOUT_PUBLISHED)
.run(() -> { .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress); processModel.setTempTradingPeerNodeAddress(peerNodeAddress);

View File

@ -22,4 +22,8 @@ import bisq.common.handlers.ResultHandler;
public interface BuyerProtocol { public interface BuyerProtocol {
void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler); void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler);
enum BuyerEvent implements TradeProtocol.Event {
PAYMENT_SENT
}
} }

View File

@ -25,5 +25,7 @@ import bisq.network.p2p.NodeAddress;
import bisq.common.handlers.ErrorMessageHandler; import bisq.common.handlers.ErrorMessageHandler;
public interface MakerProtocol { public interface MakerProtocol {
void handleTakeOfferRequest(InputsForDepositTxRequest message, NodeAddress taker, ErrorMessageHandler errorMessageHandler); void handleTakeOfferRequest(InputsForDepositTxRequest message,
NodeAddress taker,
ErrorMessageHandler errorMessageHandler);
} }

View File

@ -56,8 +56,6 @@ import bisq.common.handlers.ResultHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
@Slf4j @Slf4j
public class SellerAsMakerProtocol extends TradeProtocol implements SellerProtocol, MakerProtocol { public class SellerAsMakerProtocol extends TradeProtocol implements SellerProtocol, MakerProtocol {
private final SellerAsMakerTrade sellerAsMakerTrade; private final SellerAsMakerTrade sellerAsMakerTrade;
@ -96,29 +94,33 @@ public class SellerAsMakerProtocol extends TradeProtocol implements SellerProtoc
public void handleTakeOfferRequest(InputsForDepositTxRequest tradeMessage, public void handleTakeOfferRequest(InputsForDepositTxRequest tradeMessage,
NodeAddress sender, NodeAddress sender,
ErrorMessageHandler errorMessageHandler) { ErrorMessageHandler errorMessageHandler) {
Validator.checkTradeId(processModel.getOfferId(), tradeMessage); from(Trade.Phase.INIT)
processModel.setTradeMessage(tradeMessage); .onMessage(tradeMessage)
processModel.setTempTradingPeerNodeAddress(sender); .process(() -> {
Validator.checkTradeId(processModel.getOfferId(), tradeMessage);
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> handleTaskRunnerSuccess(tradeMessage, "handleTakeOfferRequest"), () -> handleTaskRunnerSuccess(tradeMessage, "handleTakeOfferRequest"),
errorMessage -> { errorMessage -> {
errorMessageHandler.handleErrorMessage(errorMessage); errorMessageHandler.handleErrorMessage(errorMessage);
handleTaskRunnerFault(tradeMessage, errorMessage); handleTaskRunnerFault(tradeMessage, errorMessage);
});
taskRunner.addTasks(
MakerProcessesInputsForDepositTxRequest.class,
ApplyFilter.class,
VerifyPeersAccountAgeWitness.class,
MakerVerifyTakerFeePayment.class,
MakerSetsLockTime.class,
MakerCreateAndSignContract.class,
SellerAsMakerCreatesUnsignedDepositTx.class,
SellerAsMakerSendsInputsForDepositTxResponse.class
);
taskRunner.run();
}); });
taskRunner.addTasks(
MakerProcessesInputsForDepositTxRequest.class,
ApplyFilter.class,
VerifyPeersAccountAgeWitness.class,
MakerVerifyTakerFeePayment.class,
MakerSetsLockTime.class,
MakerCreateAndSignContract.class,
SellerAsMakerCreatesUnsignedDepositTx.class,
SellerAsMakerSendsInputsForDepositTxResponse.class
);
taskRunner.run();
} }
@ -127,43 +129,51 @@ public class SellerAsMakerProtocol extends TradeProtocol implements SellerProtoc
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
protected void handle(DepositTxMessage tradeMessage, NodeAddress sender) { protected void handle(DepositTxMessage tradeMessage, NodeAddress sender) {
processModel.setTradeMessage(tradeMessage); from(Trade.Phase.TAKER_FEE_PUBLISHED)
processModel.setTempTradingPeerNodeAddress(sender); .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> handleTaskRunnerSuccess(tradeMessage, "handle DepositTxMessage"), () -> handleTaskRunnerSuccess(tradeMessage, "handle DepositTxMessage"),
errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage));
taskRunner.addTasks( taskRunner.addTasks(
SellerAsMakerProcessDepositTxMessage.class, SellerAsMakerProcessDepositTxMessage.class,
SellerAsMakerFinalizesDepositTx.class, SellerAsMakerFinalizesDepositTx.class,
SellerCreatesDelayedPayoutTx.class, SellerCreatesDelayedPayoutTx.class,
SellerSendDelayedPayoutTxSignatureRequest.class SellerSendDelayedPayoutTxSignatureRequest.class
); );
taskRunner.run(); taskRunner.run();
});
} }
private void handle(DelayedPayoutTxSignatureResponse tradeMessage, NodeAddress sender) { private void handle(DelayedPayoutTxSignatureResponse tradeMessage, NodeAddress sender) {
processModel.setTradeMessage(tradeMessage); from(Trade.Phase.TAKER_FEE_PUBLISHED)
processModel.setTempTradingPeerNodeAddress(sender); .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> { () -> {
stopTimeout(); stopTimeout();
handleTaskRunnerSuccess(tradeMessage, "handle DelayedPayoutTxSignatureResponse"); handleTaskRunnerSuccess(tradeMessage, "handle DelayedPayoutTxSignatureResponse");
}, },
errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage));
taskRunner.addTasks( taskRunner.addTasks(
SellerProcessDelayedPayoutTxSignatureResponse.class, SellerProcessDelayedPayoutTxSignatureResponse.class,
SellerSignsDelayedPayoutTx.class, SellerSignsDelayedPayoutTx.class,
SellerFinalizesDelayedPayoutTx.class, SellerFinalizesDelayedPayoutTx.class,
SellerSendsDepositTxAndDelayedPayoutTxMessage.class, SellerSendsDepositTxAndDelayedPayoutTxMessage.class,
SellerPublishesDepositTx.class, SellerPublishesDepositTx.class,
PublishTradeStatistics.class PublishTradeStatistics.class
); );
taskRunner.run(); taskRunner.run();
processModel.witnessDebugLog(sellerAsMakerTrade); processModel.witnessDebugLog(sellerAsMakerTrade);
});
} }
@ -172,17 +182,17 @@ public class SellerAsMakerProtocol extends TradeProtocol implements SellerProtoc
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handle(CounterCurrencyTransferStartedMessage tradeMessage, NodeAddress sender) { private void handle(CounterCurrencyTransferStartedMessage tradeMessage, NodeAddress sender) {
if (trade.getPayoutTx() != null) { from(Trade.Phase.DEPOSIT_CONFIRMED)
log.warn("We received a CounterCurrencyTransferStartedMessage but we have already created the payout tx " + .onMessage(tradeMessage)
"so we ignore the message. This can happen if the ACK message to the peer did not " + .condition(trade.getPayoutTx() == null,
"arrive and the peer repeats sending us the message. We send another ACK msg."); () -> {
sendAckMessage(tradeMessage, true, null); log.warn("We received a CounterCurrencyTransferStartedMessage but we have already created the payout tx " +
processModel.removeMailboxMessageAfterProcessing(trade); "so we ignore the message. This can happen if the ACK message to the peer did not " +
return; "arrive and the peer repeats sending us the message. We send another ACK msg.");
} sendAckMessage(tradeMessage, true, null);
processModel.removeMailboxMessageAfterProcessing(trade);
ifInPhase(Trade.Phase.DEPOSIT_CONFIRMED, tradeMessage) })
.run(() -> { .process(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender); processModel.setTempTradingPeerNodeAddress(sender);
@ -206,52 +216,30 @@ public class SellerAsMakerProtocol extends TradeProtocol implements SellerProtoc
@Override @Override
public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
checkArgument(!wasDisputed(), "A call to onFiatPaymentReceived is not permitted once a " + from(Trade.Phase.FIAT_SENT)
"dispute has been opened."); .onEvent(SellerEvent.PAYMENT_RECEIVED)
.condition(!wasDisputed())
.process(() -> {
sellerAsMakerTrade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> {
resultHandler.handleResult();
handleTaskRunnerSuccess("onFiatPaymentReceived");
},
(errorMessage) -> {
errorMessageHandler.handleErrorMessage(errorMessage);
handleTaskRunnerFault(errorMessage);
});
if (trade.getPayoutTx() == null) { taskRunner.addTasks(
sellerAsMakerTrade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT); ApplyFilter.class,
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade, MakerVerifyTakerFeePayment.class,
() -> { SellerSignAndFinalizePayoutTx.class,
resultHandler.handleResult(); SellerBroadcastPayoutTx.class,
handleTaskRunnerSuccess("onFiatPaymentReceived 1"); SellerSendPayoutTxPublishedMessage.class
}, );
(errorMessage) -> { taskRunner.run();
errorMessageHandler.handleErrorMessage(errorMessage); });
handleTaskRunnerFault(errorMessage);
});
taskRunner.addTasks(
ApplyFilter.class,
MakerVerifyTakerFeePayment.class,
SellerSignAndFinalizePayoutTx.class,
SellerBroadcastPayoutTx.class,
SellerSendPayoutTxPublishedMessage.class
);
taskRunner.run();
} else {
// we don't set the state as we have already a later phase reached
log.info("onFiatPaymentReceived called twice. " +
"That can happen if message did not arrive the first time and we send msg again.\n" +
"state=" + sellerAsMakerTrade.getState());
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsMakerTrade,
() -> {
resultHandler.handleResult();
handleTaskRunnerSuccess("onFiatPaymentReceived 2");
},
(errorMessage) -> {
errorMessageHandler.handleErrorMessage(errorMessage);
handleTaskRunnerFault(errorMessage);
});
taskRunner.addTasks(
ApplyFilter.class,
MakerVerifyTakerFeePayment.class,
SellerSendPayoutTxPublishedMessage.class
);
taskRunner.run();
}
} }

View File

@ -55,7 +55,6 @@ import bisq.common.handlers.ResultHandler;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
@ -97,21 +96,25 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
@Override @Override
public void takeAvailableOffer() { public void takeAvailableOffer() {
processModel.setTempTradingPeerNodeAddress(trade.getTradingPeerNodeAddress()); from(Trade.Phase.INIT)
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade, .onEvent(TakerEvent.TAKE_OFFER)
() -> handleTaskRunnerSuccess("takeAvailableOffer"), .process(() -> {
this::handleTaskRunnerFault); processModel.setTempTradingPeerNodeAddress(trade.getTradingPeerNodeAddress());
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade,
() -> handleTaskRunnerSuccess("takeAvailableOffer"),
this::handleTaskRunnerFault);
taskRunner.addTasks( taskRunner.addTasks(
ApplyFilter.class, ApplyFilter.class,
TakerVerifyMakerFeePayment.class, TakerVerifyMakerFeePayment.class,
CreateTakerFeeTx.class, CreateTakerFeeTx.class, //
SellerAsTakerCreatesDepositTxInputs.class, SellerAsTakerCreatesDepositTxInputs.class,
TakerSendInputsForDepositTxRequest.class TakerSendInputsForDepositTxRequest.class
); );
startTimeout(); startTimeout();
taskRunner.run(); taskRunner.run();
});
} }
@ -120,49 +123,57 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handle(InputsForDepositTxResponse tradeMessage, NodeAddress sender) { private void handle(InputsForDepositTxResponse tradeMessage, NodeAddress sender) {
processModel.setTradeMessage(tradeMessage); from(Trade.Phase.INIT)
processModel.setTempTradingPeerNodeAddress(sender); .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade,
() -> { () -> {
handleTaskRunnerSuccess(tradeMessage, "handle InputsForDepositTxResponse"); handleTaskRunnerSuccess(tradeMessage, "handle InputsForDepositTxResponse");
}, },
errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage));
taskRunner.addTasks( taskRunner.addTasks(
TakerProcessesInputsForDepositTxResponse.class, TakerProcessesInputsForDepositTxResponse.class,
ApplyFilter.class, ApplyFilter.class,
VerifyPeersAccountAgeWitness.class, VerifyPeersAccountAgeWitness.class,
TakerVerifyAndSignContract.class, TakerVerifyAndSignContract.class,
TakerPublishFeeTx.class, TakerPublishFeeTx.class,
SellerAsTakerSignsDepositTx.class, SellerAsTakerSignsDepositTx.class,
SellerCreatesDelayedPayoutTx.class, SellerCreatesDelayedPayoutTx.class,
SellerSendDelayedPayoutTxSignatureRequest.class SellerSendDelayedPayoutTxSignatureRequest.class
); );
taskRunner.run(); taskRunner.run();
});
} }
private void handle(DelayedPayoutTxSignatureResponse tradeMessage, NodeAddress sender) { private void handle(DelayedPayoutTxSignatureResponse tradeMessage, NodeAddress sender) {
processModel.setTradeMessage(tradeMessage); from(Trade.Phase.TAKER_FEE_PUBLISHED)
processModel.setTempTradingPeerNodeAddress(sender); .onMessage(tradeMessage)
.process(() -> {
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade, TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade,
() -> { () -> {
stopTimeout(); stopTimeout();
handleTaskRunnerSuccess(tradeMessage, "handle DelayedPayoutTxSignatureResponse"); handleTaskRunnerSuccess(tradeMessage, "handle DelayedPayoutTxSignatureResponse");
}, },
errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage)); errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage));
taskRunner.addTasks( taskRunner.addTasks(
SellerProcessDelayedPayoutTxSignatureResponse.class, SellerProcessDelayedPayoutTxSignatureResponse.class,
SellerSignsDelayedPayoutTx.class, SellerSignsDelayedPayoutTx.class,
SellerFinalizesDelayedPayoutTx.class, SellerFinalizesDelayedPayoutTx.class,
SellerSendsDepositTxAndDelayedPayoutTxMessage.class, SellerSendsDepositTxAndDelayedPayoutTxMessage.class, // SEND_MSG(DepositTxAndDelayedPayoutTxMessage)
SellerPublishesDepositTx.class, SellerPublishesDepositTx.class, // PUBLISH_DEPOSIT_TX
PublishTradeStatistics.class PublishTradeStatistics.class
); );
taskRunner.run(); taskRunner.run();
processModel.witnessDebugLog(sellerAsTakerTrade); processModel.witnessDebugLog(sellerAsTakerTrade);
});
} }
@ -171,17 +182,26 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
private void handle(CounterCurrencyTransferStartedMessage tradeMessage, NodeAddress sender) { private void handle(CounterCurrencyTransferStartedMessage tradeMessage, NodeAddress sender) {
if (trade.getPayoutTx() != null) { from(Trade.Phase.DEPOSIT_CONFIRMED)
log.warn("We received a CounterCurrencyTransferStartedMessage but we have already created the payout tx " + .onMessage(tradeMessage)
"so we ignore the message. This can happen if the ACK message to the peer did not " + .condition(trade.getPayoutTx() == null,
"arrive and the peer repeats sending us the message. We send another ACK msg."); () -> {
sendAckMessage(tradeMessage, true, null); log.warn("We received a CounterCurrencyTransferStartedMessage but we have already created the payout tx " +
processModel.removeMailboxMessageAfterProcessing(trade); "so we ignore the message. This can happen if the ACK message to the peer did not " +
return; "arrive and the peer repeats sending us the message. We send another ACK msg.");
} sendAckMessage(tradeMessage, true, null);
processModel.removeMailboxMessageAfterProcessing(trade);
})
.process(() -> {
if (trade.getPayoutTx() != null) {
log.warn("We received a CounterCurrencyTransferStartedMessage but we have already created the payout tx " +
"so we ignore the message. This can happen if the ACK message to the peer did not " +
"arrive and the peer repeats sending us the message. We send another ACK msg.");
sendAckMessage(tradeMessage, true, null);
processModel.removeMailboxMessageAfterProcessing(trade);
return;
}
ifInPhase(Trade.Phase.DEPOSIT_CONFIRMED, tradeMessage)
.run(() -> {
processModel.setTradeMessage(tradeMessage); processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(sender); processModel.setTempTradingPeerNodeAddress(sender);
@ -205,52 +225,30 @@ public class SellerAsTakerProtocol extends TradeProtocol implements SellerProtoc
@Override @Override
public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
checkArgument(!wasDisputed(), "A call to onFiatPaymentReceived is not permitted once a " + from(Trade.Phase.FIAT_SENT)
"dispute has been opened."); .onEvent(SellerEvent.PAYMENT_RECEIVED)
.condition(!wasDisputed())
.process(() -> {
sellerAsTakerTrade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT);
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade,
() -> {
resultHandler.handleResult();
handleTaskRunnerSuccess("onFiatPaymentReceived");
},
(errorMessage) -> {
errorMessageHandler.handleErrorMessage(errorMessage);
handleTaskRunnerFault(errorMessage);
});
if (trade.getPayoutTx() == null) { taskRunner.addTasks(
sellerAsTakerTrade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT); ApplyFilter.class,
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade, TakerVerifyMakerFeePayment.class,
() -> { SellerSignAndFinalizePayoutTx.class,
resultHandler.handleResult(); SellerBroadcastPayoutTx.class,
handleTaskRunnerSuccess("onFiatPaymentReceived 1"); SellerSendPayoutTxPublishedMessage.class //TODO add repeated msg send, check UI
}, );
(errorMessage) -> { taskRunner.run();
errorMessageHandler.handleErrorMessage(errorMessage); });
handleTaskRunnerFault(errorMessage);
});
taskRunner.addTasks(
ApplyFilter.class,
TakerVerifyMakerFeePayment.class,
SellerSignAndFinalizePayoutTx.class,
SellerBroadcastPayoutTx.class,
SellerSendPayoutTxPublishedMessage.class
);
taskRunner.run();
} else {
// we don't set the state as we have already a higher phase reached
log.info("onFiatPaymentReceived called twice. " +
"That can happen if message did not arrive the first time and we send msg again.\n" +
"state=" + sellerAsTakerTrade.getState());
TradeTaskRunner taskRunner = new TradeTaskRunner(sellerAsTakerTrade,
() -> {
resultHandler.handleResult();
handleTaskRunnerSuccess("onFiatPaymentReceived 2");
},
(errorMessage) -> {
errorMessageHandler.handleErrorMessage(errorMessage);
handleTaskRunnerFault(errorMessage);
});
taskRunner.addTasks(
ApplyFilter.class,
TakerVerifyMakerFeePayment.class,
SellerSendPayoutTxPublishedMessage.class
);
taskRunner.run();
}
} }

View File

@ -22,4 +22,8 @@ import bisq.common.handlers.ResultHandler;
public interface SellerProtocol { public interface SellerProtocol {
void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler); void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler);
enum SellerEvent implements TradeProtocol.Event {
PAYMENT_RECEIVED
}
} }

View File

@ -19,4 +19,8 @@ package bisq.core.trade.protocol;
public interface TakerProtocol { public interface TakerProtocol {
void takeAvailableOffer(); void takeAvailableOffer();
enum TakerEvent implements TradeProtocol.Event {
TAKE_OFFER
}
} }

View File

@ -68,6 +68,10 @@ import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
public abstract class TradeProtocol { public abstract class TradeProtocol {
interface Event {
String name();
}
private static final long TIMEOUT = 180; private static final long TIMEOUT = 180;
protected final ProcessModel processModel; protected final ProcessModel processModel;
@ -412,64 +416,98 @@ public abstract class TradeProtocol {
} }
} }
protected TradeStateValidation ifInPhase(Trade.Phase phase) {
return new TradeStateValidation(trade, phase, null); ///////////////////////////////////////////////////////////////////////////////////////////
// FluentProcess
///////////////////////////////////////////////////////////////////////////////////////////
protected FluentProcess from(Trade.Phase phase) {
return new FluentProcess(trade, phase);
} }
protected TradeStateValidation ifInPhase(Trade.Phase phase, protected FluentProcess fromAny(Trade.Phase... phase) {
@Nullable TradeMessage tradeMessage) { return new FluentProcess(trade, phase);
return new TradeStateValidation(trade, phase, tradeMessage);
} }
static class TradeStateValidation { static class FluentProcess {
private final Trade trade; private final Trade trade;
private final Trade.Phase expectedPhase;
@Nullable @Nullable
private final TradeMessage tradeMessage; private TradeMessage tradeMessage;
private Set<Trade.Phase> alternativePhase = new HashSet<>(); private final Set<Trade.Phase> expectedPhases = new HashSet<>();
@Nullable
private Event event;
private boolean condition = true;
private Runnable conditionFailedHandler;
protected TradeStateValidation run(Runnable runnable) { protected FluentProcess process(Runnable runnable) {
if (isValidPhase()) { if (isPhaseValid() && condition) {
runnable.run(); runnable.run();
} }
if (!condition && conditionFailedHandler != null) {
conditionFailedHandler.run();
}
return this; return this;
} }
protected void otherWise(Runnable runnable) { public FluentProcess(Trade trade,
runnable.run(); Trade.Phase expectedPhase) {
}
public TradeStateValidation(Trade trade,
Trade.Phase expectedPhase,
@Nullable TradeMessage tradeMessage) {
this.trade = trade; this.trade = trade;
this.expectedPhase = expectedPhase; this.expectedPhases.add(expectedPhase);
this.tradeMessage = tradeMessage;
} }
public boolean isValidPhase() { public FluentProcess(Trade trade,
boolean isValidPhase = trade.getPhase() == expectedPhase || Trade.Phase... expectedPhases) {
(alternativePhase.stream().anyMatch(e -> e == trade.getPhase())); this.trade = trade;
this.expectedPhases.addAll(Set.of(expectedPhases));
}
if (!isValidPhase) { private boolean isPhaseValid() {
if (tradeMessage != null) { boolean isPhaseValid = expectedPhases.stream().anyMatch(e -> e == trade.getPhase());
log.error("We received a {} but we are not in the correct phase. Expected phase={}, Trade phase={}, Trade state= {} ", String trigger = tradeMessage != null ?
tradeMessage.getClass().getSimpleName(), tradeMessage.getClass().getSimpleName() :
expectedPhase, event != null ?
trade.getPhase(), event.name() + " event" :
trade.getState()); "";
} else { if (isPhaseValid) {
log.error("We are not in the correct phase. Expected phase={}, Trade phase={}, Trade state= {} ", log.info("We received {} at phase {} and state {}",
expectedPhase, trigger,
trade.getPhase(), trade.getPhase(),
trade.getState()); trade.getState());
} } else {
log.error("We received {} but we are are not in the correct phase. Expected phases={}, " +
"Trade phase={}, Trade state= {} ",
trigger,
expectedPhases,
trade.getPhase(),
trade.getState());
} }
return isValidPhase;
return isPhaseValid;
} }
public TradeStateValidation orInPhase(Trade.Phase phase) { public FluentProcess orInPhase(Trade.Phase phase) {
alternativePhase.add(phase); expectedPhases.add(phase);
return this;
}
public FluentProcess onEvent(Event event) {
this.event = event;
return this;
}
public FluentProcess onMessage(TradeMessage tradeMessage) {
this.tradeMessage = tradeMessage;
return this;
}
public FluentProcess condition(boolean condition) {
this.condition = condition;
return this;
}
public FluentProcess condition(boolean condition, Runnable conditionFailedHandler) {
this.condition = condition;
this.conditionFailedHandler = conditionFailedHandler;
return this; return this;
} }
} }

View File

@ -33,6 +33,7 @@ import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkNotNull;
//TODO add repeated msg send
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
@Slf4j @Slf4j
public class SellerSendPayoutTxPublishedMessage extends SendMailboxMessageTask { public class SellerSendPayoutTxPublishedMessage extends SendMailboxMessageTask {

View File

@ -45,7 +45,6 @@ import bisq.core.payment.payload.SepaInstantAccountPayload;
import bisq.core.payment.payload.USPostalMoneyOrderAccountPayload; import bisq.core.payment.payload.USPostalMoneyOrderAccountPayload;
import bisq.core.payment.payload.WesternUnionAccountPayload; import bisq.core.payment.payload.WesternUnionAccountPayload;
import bisq.core.trade.Contract; import bisq.core.trade.Contract;
import bisq.core.trade.Trade;
import bisq.core.trade.txproof.AssetTxProofResult; import bisq.core.trade.txproof.AssetTxProofResult;
import bisq.core.user.DontShowAgainLookup; import bisq.core.user.DontShowAgainLookup;
@ -458,8 +457,6 @@ public class SellerStep3View extends TradeStepView {
log.info("User pressed the [Confirm payment receipt] button for Trade {}", trade.getShortId()); log.info("User pressed the [Confirm payment receipt] button for Trade {}", trade.getShortId());
busyAnimation.play(); busyAnimation.play();
statusLabel.setText(Res.get("shared.sendingConfirmation")); statusLabel.setText(Res.get("shared.sendingConfirmation"));
if (!trade.isPayoutPublished())
trade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT);
model.dataModel.onFiatPaymentReceived(() -> { model.dataModel.onFiatPaymentReceived(() -> {
// In case the first send failed we got the support button displayed. // In case the first send failed we got the support button displayed.