Add timeout and resend support for p2p messages in trade process

This commit is contained in:
Manfred Karrer 2017-06-26 16:09:59 +02:00
parent 443f871b58
commit 728dead33c
8 changed files with 85 additions and 22 deletions

View file

@ -105,6 +105,7 @@ shared.TheBTCBuyer=The BTC buyer
shared.You=You
shared.reasonForPayment=Reason for payment
shared.sendingConfirmation=Sending confirmation...
shared.sendingConfirmationAgain=Please send confirmation again
shared.exportCSV=Export to csv
shared.noDateAvailable=No date available
shared.arbitratorsFee=Arbitrator's fee

View file

@ -23,7 +23,6 @@ import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.protobuf.ByteString;
import com.google.protobuf.Message;
import io.bisq.common.app.DevEnv;
import io.bisq.common.app.Log;
import io.bisq.common.crypto.KeyRing;
import io.bisq.common.crypto.PubKeyRing;
@ -584,24 +583,22 @@ public abstract class Trade implements Tradable, Model {
public void setState(State state) {
log.info("Set new state at {} (id={}): {}", this.getClass().getSimpleName(), getShortId(), state);
if (state.getPhase().ordinal() >= this.state.getPhase().ordinal()) {
boolean changed = this.state != state;
this.state = state;
stateProperty.set(state);
statePhaseProperty.set(state.getPhase());
if (state == State.WITHDRAW_COMPLETED && tradeProtocol != null)
tradeProtocol.completed();
if (changed)
persist();
} else {
final String message = "we got a state change to a previous phase. that is likely a bug.\n" +
"old state is: " + this.state + ". New state is: " + state;
log.error(message);
if (DevEnv.DEV_MODE)
throw new RuntimeException(message);
if (state.getPhase().ordinal() < this.state.getPhase().ordinal()) {
final String message = "We got a state change to a previous phase.\n" +
"Old state is: " + this.state + ". New state is: " + state;
log.warn(message);
}
boolean changed = this.state != state;
this.state = state;
stateProperty.set(state);
statePhaseProperty.set(state.getPhase());
if (state == State.WITHDRAW_COMPLETED && tradeProtocol != null)
tradeProtocol.completed();
if (changed)
persist();
}
public void setDisputeState(DisputeState disputeState) {

View file

@ -39,6 +39,7 @@ public class BuyerSendCounterCurrencyTransferStartedMessage extends TradeTask {
protected void run() {
try {
runInterceptHook();
BtcWalletService walletService = processModel.getBtcWalletService();
final String id = processModel.getOfferId();
AddressEntry payoutAddressEntry = walletService.getOrCreateAddressEntry(id,

View file

@ -27,7 +27,6 @@ import lombok.extern.slf4j.Slf4j;
import java.util.UUID;
@Slf4j
// TODO remove
public class SellerSendPayoutTxPublishedMessage extends TradeTask {
@SuppressWarnings({"WeakerAccess", "unused"})
public SellerSendPayoutTxPublishedMessage(TaskRunner taskHandler, Trade trade) {

View file

@ -88,6 +88,7 @@ public class SellerSignAndFinalizePayoutTx extends TradeTask {
);
trade.setPayoutTx(transaction);
walletService.swapTradeEntryToAvailableEntry(id, AddressEntry.Context.MULTI_SIG);
complete();

View file

@ -168,7 +168,7 @@ public class DepositView extends ActivatableView<VBox, Void> {
amountLabel = amountTuple.first;
amountTextField = amountTuple.second;
if (DevEnv.DEV_MODE)
amountTextField.setText("1000");
amountTextField.setText("10");
titledGroupBg.setVisible(false);
titledGroupBg.setManaged(false);

View file

@ -17,6 +17,8 @@
package io.bisq.gui.main.portfolio.pendingtrades.steps.buyer;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.DevEnv;
import io.bisq.common.locale.CurrencyUtil;
import io.bisq.common.locale.Res;
@ -46,6 +48,7 @@ public class BuyerStep2View extends TradeStepView {
private Label statusLabel;
private BusyAnimation busyAnimation;
private Subscription tradeStatePropertySubscription;
private Timer timeoutTimer;
///////////////////////////////////////////////////////////////////////////////////////////
@ -59,13 +62,19 @@ public class BuyerStep2View extends TradeStepView {
@Override
public void activate() {
super.activate();
if (timeoutTimer != null)
timeoutTimer.stop();
//TODO we get called twice, check why
if (tradeStatePropertySubscription == null) {
tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
if (timeoutTimer != null)
timeoutTimer.stop();
if (trade.isDepositConfirmed() && !trade.isFiatSent()) {
showPopup();
} else if (state.ordinal() <= Trade.State.BUYER_SEND_FAILED_FIAT_PAYMENT_INITIATED_MSG.ordinal()) {
busyAnimation.stop();
if (!trade.hasFailed()) {
switch (state) {
case BUYER_CONFIRMED_IN_UI_FIAT_PAYMENT_INITIATED:
@ -73,18 +82,33 @@ public class BuyerStep2View extends TradeStepView {
busyAnimation.play();
confirmButton.setDisable(true);
statusLabel.setText(Res.get("shared.sendingConfirmation"));
timeoutTimer = UserThread.runAfter(() -> {
busyAnimation.stop();
confirmButton.setDisable(false);
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
}, 10);
break;
case BUYER_SAW_ARRIVED_FIAT_PAYMENT_INITIATED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageArrived"));
break;
case BUYER_STORED_IN_MAILBOX_FIAT_PAYMENT_INITIATED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageStoredInMailbox"));
break;
case BUYER_SEND_FAILED_FIAT_PAYMENT_INITIATED_MSG:
// We get a popup and the trade closed, so we dont need to show anything here
busyAnimation.stop();
confirmButton.setDisable(false);
statusLabel.setText("");
break;
default:
log.warn("Unexpected case: State={}, tradeId={} " + state.name(), trade.getId());
busyAnimation.stop();
confirmButton.setDisable(false);
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
break;
}
} else {
confirmButton.setDisable(true);
@ -101,6 +125,9 @@ public class BuyerStep2View extends TradeStepView {
busyAnimation.stop();
if (timeoutTimer != null)
timeoutTimer.stop();
if (tradeStatePropertySubscription != null) {
tradeStatePropertySubscription.unsubscribe();
tradeStatePropertySubscription = null;
@ -276,6 +303,11 @@ public class BuyerStep2View extends TradeStepView {
private void confirmPaymentStarted() {
confirmButton.setDisable(true);
busyAnimation.play();
statusLabel.setText(Res.get("shared.sendingConfirmation"));
if (trade.isFiatSent())
trade.setState(Trade.State.DEPOSIT_CONFIRMED_IN_BLOCK_CHAIN);
model.dataModel.onPaymentStarted(() -> {
// In case the first send failed we got the support button displayed.
// If it succeeds at a second try we remove the support button again.

View file

@ -17,12 +17,15 @@
package io.bisq.gui.main.portfolio.pendingtrades.steps.seller;
import io.bisq.common.Timer;
import io.bisq.common.UserThread;
import io.bisq.common.app.DevEnv;
import io.bisq.common.locale.CurrencyUtil;
import io.bisq.common.locale.Res;
import io.bisq.common.util.Tuple3;
import io.bisq.core.payment.payload.*;
import io.bisq.core.trade.Contract;
import io.bisq.core.trade.Trade;
import io.bisq.core.user.DontShowAgainLookup;
import io.bisq.gui.components.BusyAnimation;
import io.bisq.gui.components.TextFieldWithCopyIcon;
@ -48,6 +51,7 @@ public class SellerStep3View extends TradeStepView {
private Label statusLabel;
private BusyAnimation busyAnimation;
private Subscription tradeStatePropertySubscription;
private Timer timeoutTimer;
///////////////////////////////////////////////////////////////////////////////////////////
@ -62,11 +66,16 @@ public class SellerStep3View extends TradeStepView {
public void activate() {
super.activate();
if (timeoutTimer != null)
timeoutTimer.stop();
tradeStatePropertySubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
if (timeoutTimer != null)
timeoutTimer.stop();
if (trade.isFiatSent() && !trade.isFiatReceived()) {
showPopup();
} else if (trade.isFiatReceived()) {
busyAnimation.stop();
if (!trade.hasFailed()) {
switch (state) {
case SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT:
@ -75,18 +84,33 @@ public class SellerStep3View extends TradeStepView {
busyAnimation.play();
confirmButton.setDisable(true);
statusLabel.setText(Res.get("shared.sendingConfirmation"));
timeoutTimer = UserThread.runAfter(() -> {
busyAnimation.stop();
confirmButton.setDisable(false);
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
}, 10);
break;
case SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageArrived"));
break;
case SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG:
busyAnimation.stop();
statusLabel.setText(Res.get("shared.messageStoredInMailbox"));
break;
case SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG:
// We get a popup and the trade closed, so we dont need to show anything here
busyAnimation.stop();
confirmButton.setDisable(false);
statusLabel.setText("");
break;
default:
log.warn("Unexpected case: State={}, tradeId={} " + state.name(), trade.getId());
busyAnimation.stop();
confirmButton.setDisable(false);
statusLabel.setText(Res.get("shared.sendingConfirmationAgain"));
break;
}
} else {
confirmButton.setDisable(true);
@ -104,7 +128,11 @@ public class SellerStep3View extends TradeStepView {
tradeStatePropertySubscription.unsubscribe();
tradeStatePropertySubscription = null;
}
busyAnimation.stop();
if (timeoutTimer != null)
timeoutTimer.stop();
}
@ -295,6 +323,10 @@ public class SellerStep3View extends TradeStepView {
private void confirmPaymentReceived() {
confirmButton.setDisable(true);
busyAnimation.play();
statusLabel.setText(Res.get("shared.sendingConfirmation"));
if (trade.isPayoutPublished())
trade.setState(Trade.State.SELLER_CONFIRMED_IN_UI_FIAT_PAYMENT_RECEIPT);
model.dataModel.onFiatPaymentReceived(() -> {
// In case the first send failed we got the support button displayed.