Skip to content

Commit

Permalink
Add keyboard shortcut for trade process refresh, fix bisq-network#3905
Browse files Browse the repository at this point in the history
Trades have been getting stuck in the `Wait for payment` state, perhaps
due to lost mailbox messages, but it's hard to know for sure. There is
currently no way to get out of this state except going to mediation.

With ctrl+R the seller can ask the buyer to refresh the current trade
state and the buyer will resend the
`CounterCurrencyTransferStartedMessage` if they are in the phase
FIAT_SENT.
  • Loading branch information
sqrrm committed Jan 27, 2020
1 parent 32244cd commit 5087be2
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 24 deletions.
8 changes: 8 additions & 0 deletions common/src/main/proto/pb.proto
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ message NetworkEnvelope {
DelayedPayoutTxSignatureResponse delayed_payout_tx_signature_response = 47;
DepositTxAndDelayedPayoutTxMessage deposit_tx_and_delayed_payout_tx_message = 48;
PeerPublishedDelayedPayoutTxMessage peer_published_delayed_payout_tx_message = 49;

RefreshTradeStateRequest refresh_trade_state_request = 50;
}
}

Expand Down Expand Up @@ -321,6 +323,12 @@ message MediatedPayoutTxSignatureMessage {
NodeAddress sender_node_address = 4;
}

message RefreshTradeStateRequest {
string uid = 1;
string trade_id = 2;
NodeAddress sender_node_address = 3;
}

// dispute

enum SupportType {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import bisq.core.trade.messages.MediatedPayoutTxSignatureMessage;
import bisq.core.trade.messages.PayoutTxPublishedMessage;
import bisq.core.trade.messages.PeerPublishedDelayedPayoutTxMessage;
import bisq.core.trade.messages.RefreshTradeStateRequest;
import bisq.core.trade.statistics.TradeStatistics;

import bisq.network.p2p.AckMessage;
Expand Down Expand Up @@ -142,6 +143,8 @@ public NetworkEnvelope fromProto(protobuf.NetworkEnvelope proto) throws Protobuf
return PrefixedSealedAndSignedMessage.fromProto(proto.getPrefixedSealedAndSignedMessage(), messageVersion);

// trade protocol messages
case REFRESH_TRADE_STATE_REQUEST:
return RefreshTradeStateRequest.fromProto(proto.getRefreshTradeStateRequest(), messageVersion);
case INPUTS_FOR_DEPOSIT_TX_REQUEST:
return InputsForDepositTxRequest.fromProto(proto.getInputsForDepositTxRequest(), this, messageVersion);
case INPUTS_FOR_DEPOSIT_TX_RESPONSE:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.core.trade.messages;

import bisq.network.p2p.MailboxMessage;
import bisq.network.p2p.NodeAddress;

import bisq.common.app.Version;

import lombok.EqualsAndHashCode;
import lombok.Value;

@EqualsAndHashCode(callSuper = true)
@Value
public class RefreshTradeStateRequest extends TradeMessage implements MailboxMessage {
private final NodeAddress senderNodeAddress;

public RefreshTradeStateRequest(String uid,
String tradeId,
NodeAddress senderNodeAddress) {
this(Version.getP2PMessageVersion(),
uid,
tradeId,
senderNodeAddress);
}


///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////

private RefreshTradeStateRequest(int messageVersion,
String uid,
String tradeId,
NodeAddress senderNodeAddress) {
super(messageVersion, tradeId, uid);
this.senderNodeAddress = senderNodeAddress;
}

@Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
final protobuf.RefreshTradeStateRequest.Builder builder = protobuf.RefreshTradeStateRequest.newBuilder();
builder.setUid(uid)
.setTradeId(tradeId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage());
return getNetworkEnvelopeBuilder().setRefreshTradeStateRequest(builder).build();
}

public static RefreshTradeStateRequest fromProto(protobuf.RefreshTradeStateRequest proto, int messageVersion) {
return new RefreshTradeStateRequest(messageVersion,
proto.getUid(),
proto.getTradeId(),
NodeAddress.fromProto(proto.getSenderNodeAddress()));
}

@Override
public String toString() {
return "RefreshTradeStateRequest{" +
"\n senderNodeAddress=" + senderNodeAddress +
"\n} " + super.toString();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import bisq.core.trade.messages.DepositTxAndDelayedPayoutTxMessage;
import bisq.core.trade.messages.InputsForDepositTxRequest;
import bisq.core.trade.messages.PayoutTxPublishedMessage;
import bisq.core.trade.messages.RefreshTradeStateRequest;
import bisq.core.trade.messages.TradeMessage;
import bisq.core.trade.protocol.tasks.ApplyFilter;
import bisq.core.trade.protocol.tasks.PublishTradeStatistics;
Expand Down Expand Up @@ -98,6 +99,8 @@ public void doApplyMailboxTradeMessage(TradeMessage tradeMessage, NodeAddress pe
handle((DepositTxAndDelayedPayoutTxMessage) tradeMessage, peerNodeAddress);
} else if (tradeMessage instanceof PayoutTxPublishedMessage) {
handle((PayoutTxPublishedMessage) tradeMessage, peerNodeAddress);
} else if (tradeMessage instanceof RefreshTradeStateRequest) {
handle((RefreshTradeStateRequest) tradeMessage, peerNodeAddress);
}
}

Expand Down Expand Up @@ -176,6 +179,18 @@ private void handle(DepositTxAndDelayedPayoutTxMessage tradeMessage, NodeAddress
taskRunner.run();
}

private void handle(RefreshTradeStateRequest tradeMessage, NodeAddress peerNodeAddress) {
log.debug("handle RefreshTradeStateRequest called");
// Resend CounterCurrencyTransferStartedMessage if it hasn't been acked yet and counterparty asked for a refresh
if (trade.getState().getPhase() == Trade.Phase.FIAT_SENT &&
trade.getState().ordinal() >= Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG.ordinal()) {
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsMakerTrade,
() -> handleTaskRunnerSuccess("onFiatPaymentStarted"),
this::handleTaskRunnerFault);
taskRunner.addTasks(BuyerSendCounterCurrencyTransferStartedMessage.class);
taskRunner.run();
}
}

///////////////////////////////////////////////////////////////////////////////////////////
// Called from UI
Expand Down Expand Up @@ -230,7 +245,7 @@ private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeA


///////////////////////////////////////////////////////////////////////////////////////////
// Massage dispatcher
// Message dispatcher
///////////////////////////////////////////////////////////////////////////////////////////

@Override
Expand All @@ -246,6 +261,8 @@ protected void doHandleDecryptedMessage(TradeMessage tradeMessage, NodeAddress s
handle((DepositTxAndDelayedPayoutTxMessage) tradeMessage, sender);
} else if (tradeMessage instanceof PayoutTxPublishedMessage) {
handle((PayoutTxPublishedMessage) tradeMessage, sender);
} else if (tradeMessage instanceof RefreshTradeStateRequest) {
handle((RefreshTradeStateRequest) tradeMessage, sender);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import bisq.core.trade.messages.DepositTxAndDelayedPayoutTxMessage;
import bisq.core.trade.messages.InputsForDepositTxResponse;
import bisq.core.trade.messages.PayoutTxPublishedMessage;
import bisq.core.trade.messages.RefreshTradeStateRequest;
import bisq.core.trade.messages.TradeMessage;
import bisq.core.trade.protocol.tasks.ApplyFilter;
import bisq.core.trade.protocol.tasks.PublishTradeStatistics;
Expand Down Expand Up @@ -107,6 +108,8 @@ public void doApplyMailboxTradeMessage(TradeMessage tradeMessage, NodeAddress pe
handle((DepositTxAndDelayedPayoutTxMessage) tradeMessage, peerNodeAddress);
} else if (tradeMessage instanceof PayoutTxPublishedMessage) {
handle((PayoutTxPublishedMessage) tradeMessage, peerNodeAddress);
} else if (tradeMessage instanceof RefreshTradeStateRequest) {
handle((RefreshTradeStateRequest) tradeMessage, peerNodeAddress);
}
}

Expand Down Expand Up @@ -200,6 +203,33 @@ private void handle(DepositTxAndDelayedPayoutTxMessage tradeMessage, NodeAddress
taskRunner.run();
}

private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeAddress) {
log.debug("handle PayoutTxPublishedMessage called");
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress);

TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade,
() -> handleTaskRunnerSuccess(tradeMessage, "handle PayoutTxPublishedMessage"),
errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage));

taskRunner.addTasks(
BuyerProcessPayoutTxPublishedMessage.class
);
taskRunner.run();
}

private void handle(RefreshTradeStateRequest tradeMessage, NodeAddress peerNodeAddress) {
log.debug("handle RefreshTradeStateRequest called");
// Resend CounterCurrencyTransferStartedMessage if it hasn't been acked yet and counterparty asked for a refresh
if (trade.getState().getPhase() == Trade.Phase.FIAT_SENT &&
trade.getState().ordinal() >= Trade.State.BUYER_SENT_FIAT_PAYMENT_INITIATED_MSG.ordinal()) {
TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade,
() -> handleTaskRunnerSuccess("onFiatPaymentStarted"),
this::handleTaskRunnerFault);
taskRunner.addTasks(BuyerSendCounterCurrencyTransferStartedMessage.class);
taskRunner.run();
}
}

///////////////////////////////////////////////////////////////////////////////////////////
// Called from UI
Expand Down Expand Up @@ -233,29 +263,8 @@ public void onFiatPaymentStarted(ResultHandler resultHandler, ErrorMessageHandle
log.warn("onFiatPaymentStarted called twice. tradeState=" + trade.getState());
}
}

///////////////////////////////////////////////////////////////////////////////////////////
// Incoming message handling
///////////////////////////////////////////////////////////////////////////////////////////

private void handle(PayoutTxPublishedMessage tradeMessage, NodeAddress peerNodeAddress) {
log.debug("handle PayoutTxPublishedMessage called");
processModel.setTradeMessage(tradeMessage);
processModel.setTempTradingPeerNodeAddress(peerNodeAddress);

TradeTaskRunner taskRunner = new TradeTaskRunner(buyerAsTakerTrade,
() -> handleTaskRunnerSuccess(tradeMessage, "handle PayoutTxPublishedMessage"),
errorMessage -> handleTaskRunnerFault(tradeMessage, errorMessage));

taskRunner.addTasks(
BuyerProcessPayoutTxPublishedMessage.class
);
taskRunner.run();
}


///////////////////////////////////////////////////////////////////////////////////////////
// Massage dispatcher
// Message dispatcher
///////////////////////////////////////////////////////////////////////////////////////////

@Override
Expand All @@ -273,6 +282,8 @@ protected void doHandleDecryptedMessage(TradeMessage tradeMessage, NodeAddress s
handle((DepositTxAndDelayedPayoutTxMessage) tradeMessage, sender);
} else if (tradeMessage instanceof PayoutTxPublishedMessage) {
handle((PayoutTxPublishedMessage) tradeMessage, sender);
} else if (tradeMessage instanceof RefreshTradeStateRequest) {
handle((RefreshTradeStateRequest) tradeMessage, sender);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,13 @@
import bisq.core.trade.SellerTrade;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeManager;
import bisq.core.trade.messages.RefreshTradeStateRequest;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;

import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.P2PService;
import bisq.network.p2p.SendMailboxMessageListener;

import bisq.common.crypto.PubKeyRing;
import bisq.common.handlers.ErrorMessageHandler;
Expand All @@ -77,6 +80,7 @@

import org.spongycastle.crypto.params.KeyParameter;

import java.util.UUID;
import java.util.stream.Collectors;

import lombok.Getter;
Expand Down Expand Up @@ -231,6 +235,42 @@ public void onMoveToFailedTrades() {
tradeManager.addTradeToFailedTrades(getTrade());
}

// Ask counterparty to resend last action (in case message was lost)
public void refreshTradeState() {
Trade trade = getTrade();
if (trade == null) return;

NodeAddress tradingPeerNodeAddress = trade.getTradingPeerNodeAddress();

RefreshTradeStateRequest refreshReq = new RefreshTradeStateRequest(UUID.randomUUID().toString(),
trade.getId(),
tradingPeerNodeAddress);
p2PService.sendEncryptedMailboxMessage(
tradingPeerNodeAddress,
trade.getProcessModel().getTradingPeer().getPubKeyRing(),
refreshReq,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.info("SendMailboxMessageListener onArrived tradeId={} at peer {}",
trade.getId(), tradingPeerNodeAddress);
}

@Override
public void onStoredInMailbox() {
log.info("SendMailboxMessageListener onStoredInMailbox tradeId={} at peer {}",
trade.getId(), tradingPeerNodeAddress);
}

@Override
public void onFault(String errorMessage) {
log.error("SendMailboxMessageListener onFault tradeId={} at peer {}",
trade.getId(), tradingPeerNodeAddress);
}
}
);
}

///////////////////////////////////////////////////////////////////////////////////////////
// Getters
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,13 @@
import bisq.core.support.traderchat.TradeChatSession;
import bisq.core.support.traderchat.TraderChatManager;
import bisq.core.trade.Trade;
import bisq.core.trade.messages.RefreshTradeStateRequest;
import bisq.core.user.Preferences;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.CoinFormatter;

import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.SendMailboxMessageListener;

import bisq.common.UserThread;
import bisq.common.config.Config;
Expand Down Expand Up @@ -94,6 +96,7 @@
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

@FxmlView
public class PendingTradesView extends ActivatableViewAndModel<VBox, PendingTradesViewModel> {
Expand Down Expand Up @@ -213,6 +216,8 @@ public void initialize() {
new Popup().warning(Res.get("portfolio.pending.removeFailedTrade"))
.onAction(model.dataModel::onMoveToFailedTrades)
.show();
} else if (Utilities.isAltOrCtrlPressed(KeyCode.R, keyEvent)) {
model.dataModel.refreshTradeState();
}
};

Expand Down Expand Up @@ -456,7 +461,6 @@ private void updateTableSelection() {
}
}


///////////////////////////////////////////////////////////////////////////////////////////
// CellFactories
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down

0 comments on commit 5087be2

Please sign in to comment.