mirror of
https://github.com/boldsuck/haveno.git
synced 2024-12-23 04:29:22 +00:00
check trades, disputes, and offers and add prompt on shut down
Co-authored-by: jmacxx <47253594+jmacxx@users.noreply.github.com>
This commit is contained in:
parent
3b89212c6f
commit
cb7d9364e5
14 changed files with 155 additions and 217 deletions
|
@ -105,8 +105,6 @@ public class CoreDisputesService {
|
||||||
|
|
||||||
PubKeyRing arbitratorPubKeyRing = trade.getArbitrator().getPubKeyRing();
|
PubKeyRing arbitratorPubKeyRing = trade.getArbitrator().getPubKeyRing();
|
||||||
checkNotNull(arbitratorPubKeyRing, "arbitratorPubKeyRing must not be null");
|
checkNotNull(arbitratorPubKeyRing, "arbitratorPubKeyRing must not be null");
|
||||||
byte[] depositTxSerialized = null; // depositTx.bitcoinSerialize(); TODO (woodser)
|
|
||||||
String depositTxHashAsString = null; // depositTx.getHashAsString(); TODO (woodser)
|
|
||||||
Dispute dispute = new Dispute(new Date().getTime(),
|
Dispute dispute = new Dispute(new Date().getTime(),
|
||||||
trade.getId(),
|
trade.getId(),
|
||||||
pubKey.hashCode(), // trader id,
|
pubKey.hashCode(), // trader id,
|
||||||
|
@ -118,9 +116,7 @@ public class CoreDisputesService {
|
||||||
trade.getMaxTradePeriodDate().getTime(),
|
trade.getMaxTradePeriodDate().getTime(),
|
||||||
trade.getContract(),
|
trade.getContract(),
|
||||||
trade.getContractHash(),
|
trade.getContractHash(),
|
||||||
depositTxSerialized,
|
|
||||||
payoutTxSerialized,
|
payoutTxSerialized,
|
||||||
depositTxHashAsString,
|
|
||||||
payoutTxHashAsString,
|
payoutTxHashAsString,
|
||||||
trade.getContractAsJson(),
|
trade.getContractAsJson(),
|
||||||
trade.getMaker().getContractSignature(),
|
trade.getMaker().getContractSignature(),
|
||||||
|
|
|
@ -114,7 +114,7 @@ public abstract class SupportManager {
|
||||||
|
|
||||||
public abstract boolean channelOpen(ChatMessage message);
|
public abstract boolean channelOpen(ChatMessage message);
|
||||||
|
|
||||||
public abstract List<ChatMessage> getAllChatMessages();
|
public abstract List<ChatMessage> getAllChatMessages(String tradeId);
|
||||||
|
|
||||||
public abstract void addAndPersistChatMessage(ChatMessage message);
|
public abstract void addAndPersistChatMessage(ChatMessage message);
|
||||||
|
|
||||||
|
@ -204,7 +204,7 @@ public abstract class SupportManager {
|
||||||
ackMessage.getSourceMsgClassName(), ackMessage.getSourceId(), ackMessage.getErrorMessage());
|
ackMessage.getSourceMsgClassName(), ackMessage.getSourceId(), ackMessage.getErrorMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllChatMessages().stream()
|
getAllChatMessages(ackMessage.getSourceId()).stream()
|
||||||
.filter(msg -> msg.getUid().equals(ackMessage.getSourceUid()))
|
.filter(msg -> msg.getUid().equals(ackMessage.getSourceUid()))
|
||||||
.forEach(msg -> {
|
.forEach(msg -> {
|
||||||
if (ackMessage.isSuccess())
|
if (ackMessage.isSuccess())
|
||||||
|
|
|
@ -92,12 +92,8 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||||
@Nullable
|
@Nullable
|
||||||
private final byte[] contractHash;
|
private final byte[] contractHash;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final byte[] depositTxSerialized;
|
|
||||||
@Nullable
|
|
||||||
private final byte[] payoutTxSerialized;
|
private final byte[] payoutTxSerialized;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final String depositTxId;
|
|
||||||
@Nullable
|
|
||||||
private final String payoutTxId;
|
private final String payoutTxId;
|
||||||
private String contractAsJson;
|
private String contractAsJson;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
@ -171,9 +167,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||||
long tradePeriodEnd,
|
long tradePeriodEnd,
|
||||||
Contract contract,
|
Contract contract,
|
||||||
@Nullable byte[] contractHash,
|
@Nullable byte[] contractHash,
|
||||||
@Nullable byte[] depositTxSerialized,
|
|
||||||
@Nullable byte[] payoutTxSerialized,
|
@Nullable byte[] payoutTxSerialized,
|
||||||
@Nullable String depositTxId,
|
|
||||||
@Nullable String payoutTxId,
|
@Nullable String payoutTxId,
|
||||||
String contractAsJson,
|
String contractAsJson,
|
||||||
@Nullable byte[] makerContractSignature,
|
@Nullable byte[] makerContractSignature,
|
||||||
|
@ -194,9 +188,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||||
this.tradePeriodEnd = tradePeriodEnd;
|
this.tradePeriodEnd = tradePeriodEnd;
|
||||||
this.contract = contract;
|
this.contract = contract;
|
||||||
this.contractHash = contractHash;
|
this.contractHash = contractHash;
|
||||||
this.depositTxSerialized = depositTxSerialized;
|
|
||||||
this.payoutTxSerialized = payoutTxSerialized;
|
this.payoutTxSerialized = payoutTxSerialized;
|
||||||
this.depositTxId = depositTxId;
|
|
||||||
this.payoutTxId = payoutTxId;
|
this.payoutTxId = payoutTxId;
|
||||||
this.contractAsJson = contractAsJson;
|
this.contractAsJson = contractAsJson;
|
||||||
this.makerContractSignature = makerContractSignature;
|
this.makerContractSignature = makerContractSignature;
|
||||||
|
@ -243,9 +235,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||||
.setId(id);
|
.setId(id);
|
||||||
|
|
||||||
Optional.ofNullable(contractHash).ifPresent(e -> builder.setContractHash(ByteString.copyFrom(e)));
|
Optional.ofNullable(contractHash).ifPresent(e -> builder.setContractHash(ByteString.copyFrom(e)));
|
||||||
Optional.ofNullable(depositTxSerialized).ifPresent(e -> builder.setDepositTxSerialized(ByteString.copyFrom(e)));
|
|
||||||
Optional.ofNullable(payoutTxSerialized).ifPresent(e -> builder.setPayoutTxSerialized(ByteString.copyFrom(e)));
|
Optional.ofNullable(payoutTxSerialized).ifPresent(e -> builder.setPayoutTxSerialized(ByteString.copyFrom(e)));
|
||||||
Optional.ofNullable(depositTxId).ifPresent(builder::setDepositTxId);
|
|
||||||
Optional.ofNullable(payoutTxId).ifPresent(builder::setPayoutTxId);
|
Optional.ofNullable(payoutTxId).ifPresent(builder::setPayoutTxId);
|
||||||
Optional.ofNullable(disputePayoutTxId).ifPresent(builder::setDisputePayoutTxId);
|
Optional.ofNullable(disputePayoutTxId).ifPresent(builder::setDisputePayoutTxId);
|
||||||
Optional.ofNullable(makerContractSignature).ifPresent(e -> builder.setMakerContractSignature(ByteString.copyFrom(e)));
|
Optional.ofNullable(makerContractSignature).ifPresent(e -> builder.setMakerContractSignature(ByteString.copyFrom(e)));
|
||||||
|
@ -273,9 +263,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||||
proto.getTradePeriodEnd(),
|
proto.getTradePeriodEnd(),
|
||||||
Contract.fromProto(proto.getContract(), coreProtoResolver),
|
Contract.fromProto(proto.getContract(), coreProtoResolver),
|
||||||
ProtoUtil.byteArrayOrNullFromProto(proto.getContractHash()),
|
ProtoUtil.byteArrayOrNullFromProto(proto.getContractHash()),
|
||||||
ProtoUtil.byteArrayOrNullFromProto(proto.getDepositTxSerialized()),
|
|
||||||
ProtoUtil.byteArrayOrNullFromProto(proto.getPayoutTxSerialized()),
|
ProtoUtil.byteArrayOrNullFromProto(proto.getPayoutTxSerialized()),
|
||||||
ProtoUtil.stringOrNullFromProto(proto.getDepositTxId()),
|
|
||||||
ProtoUtil.stringOrNullFromProto(proto.getPayoutTxId()),
|
ProtoUtil.stringOrNullFromProto(proto.getPayoutTxId()),
|
||||||
proto.getContractAsJson(),
|
proto.getContractAsJson(),
|
||||||
ProtoUtil.byteArrayOrNullFromProto(proto.getMakerContractSignature()),
|
ProtoUtil.byteArrayOrNullFromProto(proto.getMakerContractSignature()),
|
||||||
|
@ -516,9 +504,7 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||||
",\n tradePeriodEnd=" + tradePeriodEnd +
|
",\n tradePeriodEnd=" + tradePeriodEnd +
|
||||||
",\n contract=" + contract +
|
",\n contract=" + contract +
|
||||||
",\n contractHash=" + Utilities.bytesAsHexString(contractHash) +
|
",\n contractHash=" + Utilities.bytesAsHexString(contractHash) +
|
||||||
",\n depositTxSerialized=" + Utilities.bytesAsHexString(depositTxSerialized) +
|
|
||||||
",\n payoutTxSerialized=" + Utilities.bytesAsHexString(payoutTxSerialized) +
|
",\n payoutTxSerialized=" + Utilities.bytesAsHexString(payoutTxSerialized) +
|
||||||
",\n depositTxId='" + depositTxId + '\'' +
|
|
||||||
",\n payoutTxId='" + payoutTxId + '\'' +
|
",\n payoutTxId='" + payoutTxId + '\'' +
|
||||||
",\n contractAsJson='" + contractAsJson + '\'' +
|
",\n contractAsJson='" + contractAsJson + '\'' +
|
||||||
",\n makerContractSignature='" + Utilities.bytesAsHexString(makerContractSignature) + '\'' +
|
",\n makerContractSignature='" + Utilities.bytesAsHexString(makerContractSignature) + '\'' +
|
||||||
|
|
|
@ -91,6 +91,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
protected final DisputeListService<T> disputeListService;
|
protected final DisputeListService<T> disputeListService;
|
||||||
private final Config config;
|
private final Config config;
|
||||||
private final PriceFeedService priceFeedService;
|
private final PriceFeedService priceFeedService;
|
||||||
|
protected String pendingOutgoingMessage;
|
||||||
|
|
||||||
@Getter
|
@Getter
|
||||||
protected final ObservableList<DisputeValidation.ValidationException> validationExceptions =
|
protected final ObservableList<DisputeValidation.ValidationException> validationExceptions =
|
||||||
|
@ -122,6 +123,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
this.disputeListService = disputeListService;
|
this.disputeListService = disputeListService;
|
||||||
this.config = config;
|
this.config = config;
|
||||||
this.priceFeedService = priceFeedService;
|
this.priceFeedService = priceFeedService;
|
||||||
|
clearPendingMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -140,7 +142,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
@Override
|
@Override
|
||||||
public NodeAddress getPeerNodeAddress(ChatMessage message) {
|
public NodeAddress getPeerNodeAddress(ChatMessage message) {
|
||||||
Optional<Dispute> disputeOptional = findDispute(message);
|
Optional<Dispute> disputeOptional = findDispute(message);
|
||||||
if (!disputeOptional.isPresent()) {
|
if (disputeOptional.isEmpty()) {
|
||||||
log.warn("Could not find dispute for tradeId = {} traderId = {}",
|
log.warn("Could not find dispute for tradeId = {} traderId = {}",
|
||||||
message.getTradeId(), message.getTraderId());
|
message.getTradeId(), message.getTraderId());
|
||||||
return null;
|
return null;
|
||||||
|
@ -151,7 +153,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
@Override
|
@Override
|
||||||
public PubKeyRing getPeerPubKeyRing(ChatMessage message) {
|
public PubKeyRing getPeerPubKeyRing(ChatMessage message) {
|
||||||
Optional<Dispute> disputeOptional = findDispute(message);
|
Optional<Dispute> disputeOptional = findDispute(message);
|
||||||
if (!disputeOptional.isPresent()) {
|
if (disputeOptional.isEmpty()) {
|
||||||
log.warn("Could not find dispute for tradeId = {} traderId = {}",
|
log.warn("Could not find dispute for tradeId = {} traderId = {}",
|
||||||
message.getTradeId(), message.getTraderId());
|
message.getTradeId(), message.getTraderId());
|
||||||
return null;
|
return null;
|
||||||
|
@ -161,13 +163,12 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ChatMessage> getAllChatMessages() {
|
public List<ChatMessage> getAllChatMessages(String tradeId) {
|
||||||
synchronized (getDisputeList()) {
|
|
||||||
return getDisputeList().stream()
|
return getDisputeList().stream()
|
||||||
|
.filter(dispute -> dispute.getTradeId().equals(tradeId))
|
||||||
.flatMap(dispute -> dispute.getChatMessages().stream())
|
.flatMap(dispute -> dispute.getChatMessages().stream())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean channelOpen(ChatMessage message) {
|
public boolean channelOpen(ChatMessage message) {
|
||||||
|
@ -369,6 +370,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress,
|
disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress,
|
||||||
disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(),
|
disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(),
|
||||||
chatMessage.getUid());
|
chatMessage.getUid());
|
||||||
|
recordPendingMessage(disputeOpenedMessage.getClass().getSimpleName());
|
||||||
mailboxMessageService.sendEncryptedMailboxMessage(agentNodeAddress,
|
mailboxMessageService.sendEncryptedMailboxMessage(agentNodeAddress,
|
||||||
dispute.getAgentPubKeyRing(),
|
dispute.getAgentPubKeyRing(),
|
||||||
disputeOpenedMessage,
|
disputeOpenedMessage,
|
||||||
|
@ -380,6 +382,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress,
|
disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress,
|
||||||
disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(),
|
disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(),
|
||||||
chatMessage.getUid());
|
chatMessage.getUid());
|
||||||
|
clearPendingMessage();
|
||||||
|
|
||||||
// We use the chatMessage wrapped inside the openNewDisputeMessage for
|
// We use the chatMessage wrapped inside the openNewDisputeMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
|
@ -396,6 +399,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress,
|
disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress,
|
||||||
disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(),
|
disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(),
|
||||||
chatMessage.getUid());
|
chatMessage.getUid());
|
||||||
|
clearPendingMessage();
|
||||||
|
|
||||||
// We use the chatMessage wrapped inside the openNewDisputeMessage for
|
// We use the chatMessage wrapped inside the openNewDisputeMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
|
@ -413,6 +417,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(),
|
disputeOpenedMessage.getTradeId(), disputeOpenedMessage.getUid(),
|
||||||
chatMessage.getUid(), errorMessage);
|
chatMessage.getUid(), errorMessage);
|
||||||
|
|
||||||
|
clearPendingMessage();
|
||||||
// We use the chatMessage wrapped inside the openNewDisputeMessage for
|
// We use the chatMessage wrapped inside the openNewDisputeMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
chatMessage.setSendMessageError(errorMessage);
|
chatMessage.setSendMessageError(errorMessage);
|
||||||
|
@ -586,9 +591,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeFromOpener.getTradePeriodEnd().getTime(),
|
disputeFromOpener.getTradePeriodEnd().getTime(),
|
||||||
contractFromOpener,
|
contractFromOpener,
|
||||||
disputeFromOpener.getContractHash(),
|
disputeFromOpener.getContractHash(),
|
||||||
disputeFromOpener.getDepositTxSerialized(),
|
|
||||||
disputeFromOpener.getPayoutTxSerialized(),
|
disputeFromOpener.getPayoutTxSerialized(),
|
||||||
disputeFromOpener.getDepositTxId(),
|
|
||||||
disputeFromOpener.getPayoutTxId(),
|
disputeFromOpener.getPayoutTxId(),
|
||||||
disputeFromOpener.getContractAsJson(),
|
disputeFromOpener.getContractAsJson(),
|
||||||
disputeFromOpener.getMakerContractSignature(),
|
disputeFromOpener.getMakerContractSignature(),
|
||||||
|
@ -653,6 +656,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
peerOpenedDisputeMessage.getClass().getSimpleName(), peersNodeAddress,
|
peerOpenedDisputeMessage.getClass().getSimpleName(), peersNodeAddress,
|
||||||
peerOpenedDisputeMessage.getTradeId(), peerOpenedDisputeMessage.getUid(),
|
peerOpenedDisputeMessage.getTradeId(), peerOpenedDisputeMessage.getUid(),
|
||||||
chatMessage.getUid());
|
chatMessage.getUid());
|
||||||
|
recordPendingMessage(peerOpenedDisputeMessage.getClass().getSimpleName());
|
||||||
mailboxMessageService.sendEncryptedMailboxMessage(peersNodeAddress,
|
mailboxMessageService.sendEncryptedMailboxMessage(peersNodeAddress,
|
||||||
peersPubKeyRing,
|
peersPubKeyRing,
|
||||||
peerOpenedDisputeMessage,
|
peerOpenedDisputeMessage,
|
||||||
|
@ -665,6 +669,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
peerOpenedDisputeMessage.getTradeId(), peerOpenedDisputeMessage.getUid(),
|
peerOpenedDisputeMessage.getTradeId(), peerOpenedDisputeMessage.getUid(),
|
||||||
chatMessage.getUid());
|
chatMessage.getUid());
|
||||||
|
|
||||||
|
clearPendingMessage();
|
||||||
// We use the chatMessage wrapped inside the peerOpenedDisputeMessage for
|
// We use the chatMessage wrapped inside the peerOpenedDisputeMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
chatMessage.setArrived(true);
|
chatMessage.setArrived(true);
|
||||||
|
@ -679,6 +684,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
peerOpenedDisputeMessage.getTradeId(), peerOpenedDisputeMessage.getUid(),
|
peerOpenedDisputeMessage.getTradeId(), peerOpenedDisputeMessage.getUid(),
|
||||||
chatMessage.getUid());
|
chatMessage.getUid());
|
||||||
|
|
||||||
|
clearPendingMessage();
|
||||||
// We use the chatMessage wrapped inside the peerOpenedDisputeMessage for
|
// We use the chatMessage wrapped inside the peerOpenedDisputeMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
chatMessage.setStoredInMailbox(true);
|
chatMessage.setStoredInMailbox(true);
|
||||||
|
@ -693,6 +699,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
peerOpenedDisputeMessage.getTradeId(), peerOpenedDisputeMessage.getUid(),
|
peerOpenedDisputeMessage.getTradeId(), peerOpenedDisputeMessage.getUid(),
|
||||||
chatMessage.getUid(), errorMessage);
|
chatMessage.getUid(), errorMessage);
|
||||||
|
|
||||||
|
clearPendingMessage();
|
||||||
// We use the chatMessage wrapped inside the peerOpenedDisputeMessage for
|
// We use the chatMessage wrapped inside the peerOpenedDisputeMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
chatMessage.setSendMessageError(errorMessage);
|
chatMessage.setSendMessageError(errorMessage);
|
||||||
|
@ -749,6 +756,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
disputeClosedMessage.getClass().getSimpleName(), receiver.getNodeAddress(),
|
||||||
disputeClosedMessage.getClass().getSimpleName(), disputeClosedMessage.getTradeId(),
|
disputeClosedMessage.getClass().getSimpleName(), disputeClosedMessage.getTradeId(),
|
||||||
disputeClosedMessage.getUid(), disputeResult.getChatMessage().getUid());
|
disputeClosedMessage.getUid(), disputeResult.getChatMessage().getUid());
|
||||||
|
recordPendingMessage(disputeClosedMessage.getClass().getSimpleName());
|
||||||
mailboxMessageService.sendEncryptedMailboxMessage(receiver.getNodeAddress(),
|
mailboxMessageService.sendEncryptedMailboxMessage(receiver.getNodeAddress(),
|
||||||
dispute.getTraderPubKeyRing(),
|
dispute.getTraderPubKeyRing(),
|
||||||
disputeClosedMessage,
|
disputeClosedMessage,
|
||||||
|
@ -761,6 +769,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
||||||
disputeResult.getChatMessage().getUid());
|
disputeResult.getChatMessage().getUid());
|
||||||
|
|
||||||
|
clearPendingMessage();
|
||||||
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
disputeResult.getChatMessage().setArrived(true);
|
disputeResult.getChatMessage().setArrived(true);
|
||||||
|
@ -778,6 +787,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
||||||
disputeResult.getChatMessage().getUid());
|
disputeResult.getChatMessage().getUid());
|
||||||
|
|
||||||
|
clearPendingMessage();
|
||||||
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
disputeResult.getChatMessage().setStoredInMailbox(true);
|
disputeResult.getChatMessage().setStoredInMailbox(true);
|
||||||
|
@ -795,6 +805,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
disputeClosedMessage.getTradeId(), disputeClosedMessage.getUid(),
|
||||||
disputeResult.getChatMessage().getUid(), errorMessage);
|
disputeResult.getChatMessage().getUid(), errorMessage);
|
||||||
|
|
||||||
|
clearPendingMessage();
|
||||||
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
// We use the chatMessage wrapped inside the DisputeClosedMessage for
|
||||||
// the state, as that is displayed to the user and we only persist that msg
|
// the state, as that is displayed to the user and we only persist that msg
|
||||||
disputeResult.getChatMessage().setSendMessageError(errorMessage);
|
disputeResult.getChatMessage().setSendMessageError(errorMessage);
|
||||||
|
@ -1091,4 +1102,20 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasPendingMessageAtShutdown() {
|
||||||
|
if (pendingOutgoingMessage.length() > 0) {
|
||||||
|
log.warn("{} has an outgoing message pending: {}", this.getClass().getSimpleName(), pendingOutgoingMessage);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void recordPendingMessage(String className) {
|
||||||
|
pendingOutgoingMessage = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void clearPendingMessage() {
|
||||||
|
pendingOutgoingMessage = "";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,6 @@ package haveno.core.support.dispute;
|
||||||
|
|
||||||
import haveno.common.config.Config;
|
import haveno.common.config.Config;
|
||||||
import haveno.common.crypto.Hash;
|
import haveno.common.crypto.Hash;
|
||||||
import haveno.common.util.Tuple3;
|
|
||||||
import haveno.core.support.SupportType;
|
|
||||||
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;
|
||||||
|
@ -35,13 +33,7 @@ import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.core.TransactionOutput;
|
import org.bitcoinj.core.TransactionOutput;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
@ -131,122 +123,6 @@ public class DisputeValidation {
|
||||||
"; dispute.getDonationAddressOfDelayedPayoutTx()=" + dispute.getDonationAddressOfDelayedPayoutTx());
|
"; dispute.getDonationAddressOfDelayedPayoutTx()=" + dispute.getDonationAddressOfDelayedPayoutTx());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void testIfAnyDisputeTriedReplay(List<Dispute> disputeList,
|
|
||||||
Consumer<DisputeReplayException> exceptionHandler) {
|
|
||||||
var tuple = getTestReplayHashMaps(disputeList);
|
|
||||||
Map<String, Set<String>> disputesPerTradeId = tuple.first;
|
|
||||||
Map<String, Set<String>> disputesPerDelayedPayoutTxId = tuple.second;
|
|
||||||
Map<String, Set<String>> disputesPerDepositTxId = tuple.third;
|
|
||||||
|
|
||||||
disputeList.forEach(disputeToTest -> {
|
|
||||||
try {
|
|
||||||
testIfDisputeTriesReplay(disputeToTest,
|
|
||||||
disputesPerTradeId,
|
|
||||||
disputesPerDelayedPayoutTxId,
|
|
||||||
disputesPerDepositTxId);
|
|
||||||
|
|
||||||
} catch (DisputeReplayException e) {
|
|
||||||
exceptionHandler.accept(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void testIfDisputeTriesReplay(Dispute dispute,
|
|
||||||
List<Dispute> disputeList) throws DisputeReplayException {
|
|
||||||
var tuple = getTestReplayHashMaps(disputeList);
|
|
||||||
Map<String, Set<String>> disputesPerTradeId = tuple.first;
|
|
||||||
Map<String, Set<String>> disputesPerDelayedPayoutTxId = tuple.second;
|
|
||||||
Map<String, Set<String>> disputesPerDepositTxId = tuple.third;
|
|
||||||
|
|
||||||
testIfDisputeTriesReplay(dispute,
|
|
||||||
disputesPerTradeId,
|
|
||||||
disputesPerDelayedPayoutTxId,
|
|
||||||
disputesPerDepositTxId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Tuple3<Map<String, Set<String>>, Map<String, Set<String>>, Map<String, Set<String>>> getTestReplayHashMaps(
|
|
||||||
List<Dispute> disputeList) {
|
|
||||||
Map<String, Set<String>> disputesPerTradeId = new HashMap<>();
|
|
||||||
Map<String, Set<String>> disputesPerDelayedPayoutTxId = new HashMap<>();
|
|
||||||
Map<String, Set<String>> disputesPerDepositTxId = new HashMap<>();
|
|
||||||
disputeList.forEach(dispute -> {
|
|
||||||
String uid = dispute.getUid();
|
|
||||||
|
|
||||||
String tradeId = dispute.getTradeId();
|
|
||||||
disputesPerTradeId.putIfAbsent(tradeId, new HashSet<>());
|
|
||||||
Set<String> set = disputesPerTradeId.get(tradeId);
|
|
||||||
set.add(uid);
|
|
||||||
|
|
||||||
String delayedPayoutTxId = dispute.getDelayedPayoutTxId();
|
|
||||||
if (delayedPayoutTxId != null) {
|
|
||||||
disputesPerDelayedPayoutTxId.putIfAbsent(delayedPayoutTxId, new HashSet<>());
|
|
||||||
set = disputesPerDelayedPayoutTxId.get(delayedPayoutTxId);
|
|
||||||
set.add(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
String depositTxId = dispute.getDepositTxId();
|
|
||||||
if (depositTxId != null) {
|
|
||||||
disputesPerDepositTxId.putIfAbsent(depositTxId, new HashSet<>());
|
|
||||||
set = disputesPerDepositTxId.get(depositTxId);
|
|
||||||
set.add(uid);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return new Tuple3<>(disputesPerTradeId, disputesPerDelayedPayoutTxId, disputesPerDepositTxId);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testIfDisputeTriesReplay(Dispute disputeToTest,
|
|
||||||
Map<String, Set<String>> disputesPerTradeId,
|
|
||||||
Map<String, Set<String>> disputesPerDelayedPayoutTxId,
|
|
||||||
Map<String, Set<String>> disputesPerDepositTxId)
|
|
||||||
throws DisputeReplayException {
|
|
||||||
try {
|
|
||||||
String disputeToTestTradeId = disputeToTest.getTradeId();
|
|
||||||
String disputeToTestDelayedPayoutTxId = disputeToTest.getDelayedPayoutTxId();
|
|
||||||
String disputeToTestDepositTxId = disputeToTest.getDepositTxId();
|
|
||||||
String disputeToTestUid = disputeToTest.getUid();
|
|
||||||
|
|
||||||
// For pre v1.4.0 we do not get the delayed payout tx sent in mediation cases but in refund agent case we do.
|
|
||||||
// So until all users have updated to 1.4.0 we only check in refund agent case. With 1.4.0 we send the
|
|
||||||
// delayed payout tx also in mediation cases and that if check can be removed.
|
|
||||||
if (disputeToTest.getSupportType() == SupportType.REFUND) {
|
|
||||||
checkNotNull(disputeToTestDelayedPayoutTxId,
|
|
||||||
"Delayed payout transaction ID is null. " +
|
|
||||||
"Trade ID: " + disputeToTestTradeId);
|
|
||||||
}
|
|
||||||
checkNotNull(disputeToTestDepositTxId,
|
|
||||||
"depositTxId must not be null. Trade ID: " + disputeToTestTradeId);
|
|
||||||
checkNotNull(disputeToTestUid,
|
|
||||||
"agentsUid must not be null. Trade ID: " + disputeToTestTradeId);
|
|
||||||
|
|
||||||
Set<String> disputesPerTradeIdItems = disputesPerTradeId.get(disputeToTestTradeId);
|
|
||||||
checkArgument(disputesPerTradeIdItems != null && disputesPerTradeIdItems.size() <= 2,
|
|
||||||
"We found more then 2 disputes with the same trade ID. " +
|
|
||||||
"Trade ID: " + disputeToTestTradeId);
|
|
||||||
if (!disputesPerDelayedPayoutTxId.isEmpty()) {
|
|
||||||
Set<String> disputesPerDelayedPayoutTxIdItems = disputesPerDelayedPayoutTxId.get(disputeToTestDelayedPayoutTxId);
|
|
||||||
checkArgument(disputesPerDelayedPayoutTxIdItems != null && disputesPerDelayedPayoutTxIdItems.size() <= 2,
|
|
||||||
"We found more then 2 disputes with the same delayedPayoutTxId. " +
|
|
||||||
"Trade ID: " + disputeToTestTradeId);
|
|
||||||
}
|
|
||||||
if (!disputesPerDepositTxId.isEmpty()) {
|
|
||||||
Set<String> disputesPerDepositTxIdItems = disputesPerDepositTxId.get(disputeToTestDepositTxId);
|
|
||||||
checkArgument(disputesPerDepositTxIdItems != null && disputesPerDepositTxIdItems.size() <= 2,
|
|
||||||
"We found more then 2 disputes with the same depositTxId. " +
|
|
||||||
"Trade ID: " + disputeToTestTradeId);
|
|
||||||
}
|
|
||||||
} catch (IllegalArgumentException e) {
|
|
||||||
throw new DisputeReplayException(disputeToTest, e.getMessage());
|
|
||||||
} catch (NullPointerException e) {
|
|
||||||
log.error("NullPointerException at testIfDisputeTriesReplay: " +
|
|
||||||
"disputeToTest={}, disputesPerTradeId={}, disputesPerDelayedPayoutTxId={}, " +
|
|
||||||
"disputesPerDepositTxId={}",
|
|
||||||
disputeToTest, disputesPerTradeId, disputesPerDelayedPayoutTxId, disputesPerDepositTxId);
|
|
||||||
throw new DisputeReplayException(disputeToTest, e.toString() + " at dispute " + disputeToTest.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Exceptions
|
// Exceptions
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -31,13 +31,14 @@ import haveno.core.trade.TradeManager;
|
||||||
import haveno.network.p2p.AckMessageSourceType;
|
import haveno.network.p2p.AckMessageSourceType;
|
||||||
import haveno.network.p2p.NodeAddress;
|
import haveno.network.p2p.NodeAddress;
|
||||||
import haveno.network.p2p.P2PService;
|
import haveno.network.p2p.P2PService;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Singleton;
|
import javax.inject.Singleton;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Collectors;
|
import java.util.Optional;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Singleton
|
@Singleton
|
||||||
|
@ -97,10 +98,9 @@ public class TraderChatManager extends SupportManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<ChatMessage> getAllChatMessages() {
|
public List<ChatMessage> getAllChatMessages(String tradeId) {
|
||||||
return tradeManager.getObservableList().stream()
|
return Optional.of(tradeManager.getTrade(tradeId)).map(Trade::getChatMessages)
|
||||||
.flatMap(trade -> trade.getChatMessages().stream())
|
.orElse(FXCollections.emptyObservableList());
|
||||||
.collect(Collectors.toList());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1137,7 +1137,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
|
|
||||||
// TODO (woodser): make Optional<Trade> versus Trade return types consistent
|
// TODO (woodser): make Optional<Trade> versus Trade return types consistent
|
||||||
public Trade getTrade(String tradeId) {
|
public Trade getTrade(String tradeId) {
|
||||||
return getOpenTrade(tradeId).orElseGet(() -> getClosedTrade(tradeId).orElseGet(() -> null));
|
return getOpenTrade(tradeId).orElseGet(() -> getClosedTrade(tradeId).orElseGet(() -> getFailedTrade(tradeId).orElseGet(() -> null)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<Trade> getOpenTrade(String tradeId) {
|
public Optional<Trade> getOpenTrade(String tradeId) {
|
||||||
|
@ -1177,6 +1177,10 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
return closedTradableManager.getClosedTrades().stream().filter(e -> e.getId().equals(tradeId)).findFirst();
|
return closedTradableManager.getClosedTrades().stream().filter(e -> e.getId().equals(tradeId)).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Trade> getFailedTrade(String tradeId) {
|
||||||
|
return failedTradesManager.getTradeById(tradeId);
|
||||||
|
}
|
||||||
|
|
||||||
private void addTrade(Trade trade) {
|
private void addTrade(Trade trade) {
|
||||||
synchronized (tradableList) {
|
synchronized (tradableList) {
|
||||||
if (tradableList.add(trade)) {
|
if (tradableList.add(trade)) {
|
||||||
|
|
|
@ -2159,6 +2159,9 @@ popup.info.shutDownWithOpenOffers=Haveno is being shut down, but there are open
|
||||||
(i.e., make sure it doesn't go into standby mode...monitor standby is not a problem).
|
(i.e., make sure it doesn't go into standby mode...monitor standby is not a problem).
|
||||||
popup.info.shutDownWithTradeInit={0}\n\
|
popup.info.shutDownWithTradeInit={0}\n\
|
||||||
This trade has not finished initializing; shutting down now will probably make it corrupted. Please wait a minute and try again.
|
This trade has not finished initializing; shutting down now will probably make it corrupted. Please wait a minute and try again.
|
||||||
|
popup.info.shutDownWithDisputeInit=Haveno is being shut down, but there is a Dispute system message still pending.\n\
|
||||||
|
Please wait a minute before shutting down.
|
||||||
|
popup.info.shutDownQuery=Are you sure you want to exit Haveno?
|
||||||
popup.info.qubesOSSetupInfo=It appears you are running Haveno on Qubes OS. \n\n\
|
popup.info.qubesOSSetupInfo=It appears you are running Haveno on Qubes OS. \n\n\
|
||||||
Please make sure your Haveno qube is setup according to our Setup Guide at [HYPERLINK:https://bisq.wiki/Running_Haveno_on_Qubes].
|
Please make sure your Haveno qube is setup according to our Setup Guide at [HYPERLINK:https://bisq.wiki/Running_Haveno_on_Qubes].
|
||||||
popup.info.p2pStatusIndicator.red={0}\n\n\
|
popup.info.p2pStatusIndicator.red={0}\n\n\
|
||||||
|
|
|
@ -178,8 +178,6 @@ public class AccountAgeWitnessServiceTest {
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
null,
|
|
||||||
null,
|
|
||||||
"contractAsJson",
|
"contractAsJson",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
|
|
||||||
package haveno.desktop.app;
|
package haveno.desktop.app;
|
||||||
|
|
||||||
|
import static haveno.desktop.util.Layout.INITIAL_WINDOW_HEIGHT;
|
||||||
|
import static haveno.desktop.util.Layout.INITIAL_WINDOW_WIDTH;
|
||||||
|
import static haveno.desktop.util.Layout.MIN_WINDOW_HEIGHT;
|
||||||
|
import static haveno.desktop.util.Layout.MIN_WINDOW_WIDTH;
|
||||||
|
|
||||||
import ch.qos.logback.classic.Level;
|
import ch.qos.logback.classic.Level;
|
||||||
import ch.qos.logback.classic.Logger;
|
import ch.qos.logback.classic.Logger;
|
||||||
import com.google.common.base.Joiner;
|
import com.google.common.base.Joiner;
|
||||||
|
@ -26,12 +31,18 @@ import com.google.inject.name.Names;
|
||||||
import haveno.common.app.DevEnv;
|
import haveno.common.app.DevEnv;
|
||||||
import haveno.common.app.Log;
|
import haveno.common.app.Log;
|
||||||
import haveno.common.config.Config;
|
import haveno.common.config.Config;
|
||||||
|
import haveno.common.crypto.Hash;
|
||||||
import haveno.common.setup.GracefulShutDownHandler;
|
import haveno.common.setup.GracefulShutDownHandler;
|
||||||
import haveno.common.setup.UncaughtExceptionHandler;
|
import haveno.common.setup.UncaughtExceptionHandler;
|
||||||
import haveno.common.util.Utilities;
|
import haveno.common.util.Utilities;
|
||||||
import haveno.core.locale.Res;
|
import haveno.core.locale.Res;
|
||||||
import haveno.core.offer.OpenOffer;
|
import haveno.core.offer.OpenOffer;
|
||||||
import haveno.core.offer.OpenOfferManager;
|
import haveno.core.offer.OpenOfferManager;
|
||||||
|
import haveno.core.support.dispute.arbitration.ArbitrationManager;
|
||||||
|
import haveno.core.support.dispute.mediation.MediationManager;
|
||||||
|
import haveno.core.support.dispute.refund.RefundManager;
|
||||||
|
import haveno.core.trade.Trade;
|
||||||
|
import haveno.core.trade.TradeManager;
|
||||||
import haveno.core.user.Cookie;
|
import haveno.core.user.Cookie;
|
||||||
import haveno.core.user.CookieKey;
|
import haveno.core.user.CookieKey;
|
||||||
import haveno.core.user.Preferences;
|
import haveno.core.user.Preferences;
|
||||||
|
@ -47,7 +58,15 @@ import haveno.desktop.main.overlays.windows.FilterWindow;
|
||||||
import haveno.desktop.main.overlays.windows.SendAlertMessageWindow;
|
import haveno.desktop.main.overlays.windows.SendAlertMessageWindow;
|
||||||
import haveno.desktop.main.overlays.windows.ShowWalletDataWindow;
|
import haveno.desktop.main.overlays.windows.ShowWalletDataWindow;
|
||||||
import haveno.desktop.util.CssTheme;
|
import haveno.desktop.util.CssTheme;
|
||||||
|
import haveno.desktop.util.DisplayUtils;
|
||||||
import haveno.desktop.util.ImageUtil;
|
import haveno.desktop.util.ImageUtil;
|
||||||
|
import java.time.Instant;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import javafx.application.Application;
|
import javafx.application.Application;
|
||||||
import javafx.geometry.BoundingBox;
|
import javafx.geometry.BoundingBox;
|
||||||
import javafx.geometry.Rectangle2D;
|
import javafx.geometry.Rectangle2D;
|
||||||
|
@ -65,16 +84,6 @@ import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import static haveno.desktop.util.Layout.INITIAL_WINDOW_HEIGHT;
|
|
||||||
import static haveno.desktop.util.Layout.INITIAL_WINDOW_WIDTH;
|
|
||||||
import static haveno.desktop.util.Layout.MIN_WINDOW_HEIGHT;
|
|
||||||
import static haveno.desktop.util.Layout.MIN_WINDOW_WIDTH;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class HavenoApp extends Application implements UncaughtExceptionHandler {
|
public class HavenoApp extends Application implements UncaughtExceptionHandler {
|
||||||
@Setter
|
@Setter
|
||||||
|
@ -327,30 +336,80 @@ public class HavenoApp extends Application implements UncaughtExceptionHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void shutDownByUser() {
|
private void shutDownByUser() {
|
||||||
boolean hasOpenOffers = false;
|
String potentialIssues = checkTradesAtShutdown() + checkDisputesAtShutdown() + checkOffersAtShutdown();
|
||||||
for (OpenOffer openOffer : injector.getInstance(OpenOfferManager.class).getObservableList()) {
|
promptUserAtShutdown(potentialIssues).thenAccept(asyncOkToShutDown -> {
|
||||||
if (openOffer.getState().equals(OpenOffer.State.AVAILABLE)) {
|
if (asyncOkToShutDown) {
|
||||||
hasOpenOffers = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!hasOpenOffers) {
|
|
||||||
// No open offers, so no need to show the popup.
|
|
||||||
stop();
|
stop();
|
||||||
return;
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// We show a popup to inform user that open offers will be removed if Haveno is not running.
|
private String checkTradesAtShutdown() {
|
||||||
String key = "showOpenOfferWarnPopupAtShutDown";
|
log.info("Checking trades at shutdown");
|
||||||
|
Instant fiveMinutesAgo = Instant.ofEpochSecond(Instant.now().getEpochSecond() - TimeUnit.MINUTES.toSeconds(5));
|
||||||
|
for (Trade trade : injector.getInstance(TradeManager.class).getObservableList()) {
|
||||||
|
if (trade.getPhase().equals(Trade.Phase.DEPOSIT_REQUESTED) &&
|
||||||
|
trade.getTakeOfferDate().toInstant().isAfter(fiveMinutesAgo)) {
|
||||||
|
String tradeDateString = DisplayUtils.formatDateTime(trade.getTakeOfferDate());
|
||||||
|
String tradeInfo = Res.get("shared.tradeId") + ": " + trade.getShortId() + " " +
|
||||||
|
Res.get("shared.dateTime") + ": " + tradeDateString;
|
||||||
|
return Res.get("popup.info.shutDownWithTradeInit", tradeInfo) + System.lineSeparator() + System.lineSeparator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkDisputesAtShutdown() {
|
||||||
|
log.info("Checking disputes at shutdown");
|
||||||
|
if (injector.getInstance(ArbitrationManager.class).hasPendingMessageAtShutdown() ||
|
||||||
|
injector.getInstance(MediationManager.class).hasPendingMessageAtShutdown() ||
|
||||||
|
injector.getInstance(RefundManager.class).hasPendingMessageAtShutdown()) {
|
||||||
|
return Res.get("popup.info.shutDownWithDisputeInit") + System.lineSeparator() + System.lineSeparator();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String checkOffersAtShutdown() {
|
||||||
|
log.info("Checking offers at shutdown");
|
||||||
|
for (OpenOffer openOffer : injector.getInstance(OpenOfferManager.class).getObservableList()) {
|
||||||
|
if (openOffer.getState().equals(OpenOffer.State.AVAILABLE)) {
|
||||||
|
return Res.get("popup.info.shutDownWithOpenOffers") + System.lineSeparator() + System.lineSeparator();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private CompletableFuture<Boolean> promptUserAtShutdown(String issueInfo) {
|
||||||
|
final CompletableFuture<Boolean> asyncStatus = new CompletableFuture<>();
|
||||||
|
if (issueInfo.length() > 0) {
|
||||||
|
// We maybe show a popup to inform user that some issues are pending
|
||||||
|
String key = Utilities.encodeToHex(Hash.getSha256Hash(issueInfo));
|
||||||
if (injector.getInstance(Preferences.class).showAgain(key) && !DevEnv.isDevMode()) {
|
if (injector.getInstance(Preferences.class).showAgain(key) && !DevEnv.isDevMode()) {
|
||||||
new Popup().information(Res.get("popup.info.shutDownWithOpenOffers"))
|
new Popup().warning(issueInfo)
|
||||||
|
.actionButtonText(Res.get("shared.okWait"))
|
||||||
|
.onAction(() -> asyncStatus.complete(false))
|
||||||
|
.closeButtonText(Res.get("shared.closeAnywayDanger"))
|
||||||
|
.onClose(() -> asyncStatus.complete(true))
|
||||||
|
.dontShowAgainId(key)
|
||||||
|
.width(800)
|
||||||
|
.show();
|
||||||
|
return asyncStatus;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// if no warning popup has been shown yet, prompt user if they really intend to shut down
|
||||||
|
String key = "popup.info.shutDownQuery";
|
||||||
|
if (injector.getInstance(Preferences.class).showAgain(key) && !DevEnv.isDevMode()) {
|
||||||
|
new Popup().headLine(Res.get("popup.info.shutDownQuery"))
|
||||||
|
.actionButtonText(Res.get("shared.yes"))
|
||||||
|
.onAction(() -> asyncStatus.complete(true))
|
||||||
|
.closeButtonText(Res.get("shared.no"))
|
||||||
|
.onClose(() -> asyncStatus.complete(false))
|
||||||
.dontShowAgainId(key)
|
.dontShowAgainId(key)
|
||||||
.useShutDownButton()
|
|
||||||
.closeButtonText(Res.get("shared.cancel"))
|
|
||||||
.show();
|
.show();
|
||||||
} else {
|
} else {
|
||||||
stop();
|
asyncStatus.complete(true);
|
||||||
}
|
}
|
||||||
|
return asyncStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Used for debugging trade process
|
// Used for debugging trade process
|
||||||
|
|
|
@ -125,8 +125,6 @@ public class ContractWindow extends Overlay<ContractWindow> {
|
||||||
boolean showAcceptedCountryCodes = acceptedCountryCodes != null && !acceptedCountryCodes.isEmpty();
|
boolean showAcceptedCountryCodes = acceptedCountryCodes != null && !acceptedCountryCodes.isEmpty();
|
||||||
|
|
||||||
int rows = 18;
|
int rows = 18;
|
||||||
if (dispute.getDepositTxSerialized() != null)
|
|
||||||
rows++;
|
|
||||||
if (dispute.getPayoutTxSerialized() != null)
|
if (dispute.getPayoutTxSerialized() != null)
|
||||||
rows++;
|
rows++;
|
||||||
if (dispute.getDelayedPayoutTxId() != null)
|
if (dispute.getDelayedPayoutTxId() != null)
|
||||||
|
|
|
@ -473,9 +473,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||||
// trade.getMaxTradePeriodDate().getTime(),
|
// trade.getMaxTradePeriodDate().getTime(),
|
||||||
// trade.getContract(),
|
// trade.getContract(),
|
||||||
// trade.getContractHash(),
|
// trade.getContractHash(),
|
||||||
// depositTxSerialized,
|
|
||||||
// payoutTxSerialized,
|
// payoutTxSerialized,
|
||||||
// depositTxHashAsString,
|
|
||||||
// payoutTxHashAsString,
|
// payoutTxHashAsString,
|
||||||
// trade.getContractAsJson(),
|
// trade.getContractAsJson(),
|
||||||
// trade.getMakerContractSignature(),
|
// trade.getMakerContractSignature(),
|
||||||
|
@ -503,9 +501,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||||
trade.getMaxTradePeriodDate().getTime(),
|
trade.getMaxTradePeriodDate().getTime(),
|
||||||
trade.getContract(),
|
trade.getContract(),
|
||||||
trade.getContractHash(),
|
trade.getContractHash(),
|
||||||
depositTxSerialized,
|
|
||||||
payoutTxSerialized,
|
payoutTxSerialized,
|
||||||
depositTxId,
|
|
||||||
payoutTxHashAsString,
|
payoutTxHashAsString,
|
||||||
trade.getContractAsJson(),
|
trade.getContractAsJson(),
|
||||||
trade.getMaker().getContractSignature(),
|
trade.getMaker().getContractSignature(),
|
||||||
|
|
|
@ -440,9 +440,6 @@ public abstract class DisputeView extends ActivatableView<VBox, Void> {
|
||||||
return FilterResult.SELLER_ACCOUNT_DETAILS;
|
return FilterResult.SELLER_ACCOUNT_DETAILS;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dispute.getDepositTxId() != null && dispute.getDepositTxId().contains(filter)) {
|
|
||||||
return FilterResult.DEPOSIT_TX;
|
|
||||||
}
|
|
||||||
if (dispute.getPayoutTxId() != null && dispute.getPayoutTxId().contains(filter)) {
|
if (dispute.getPayoutTxId() != null && dispute.getPayoutTxId().contains(filter)) {
|
||||||
return FilterResult.PAYOUT_TX;
|
return FilterResult.PAYOUT_TX;
|
||||||
}
|
}
|
||||||
|
|
|
@ -691,28 +691,26 @@ message Dispute {
|
||||||
int64 trade_date = 9;
|
int64 trade_date = 9;
|
||||||
Contract contract = 10;
|
Contract contract = 10;
|
||||||
bytes contract_hash = 11;
|
bytes contract_hash = 11;
|
||||||
bytes deposit_tx_serialized = 12;
|
bytes payout_tx_serialized = 12;
|
||||||
bytes payout_tx_serialized = 13;
|
string payout_tx_id = 13;
|
||||||
string deposit_tx_id = 14;
|
string contract_as_json = 14;
|
||||||
string payout_tx_id = 15;
|
bytes maker_contract_signature = 15;
|
||||||
string contract_as_json = 16;
|
bytes taker_contract_signature = 16;
|
||||||
bytes maker_contract_signature = 17;
|
PaymentAccountPayload maker_payment_account_payload = 17;
|
||||||
bytes taker_contract_signature = 18;
|
PaymentAccountPayload taker_payment_account_payload = 18;
|
||||||
PaymentAccountPayload maker_payment_account_payload = 19;
|
PubKeyRing agent_pub_key_ring = 19;
|
||||||
PaymentAccountPayload taker_payment_account_payload = 20;
|
bool is_support_ticket = 20;
|
||||||
PubKeyRing agent_pub_key_ring = 21;
|
repeated ChatMessage chat_message = 21;
|
||||||
bool is_support_ticket = 22;
|
bool is_closed = 22;
|
||||||
repeated ChatMessage chat_message = 23;
|
DisputeResult dispute_result = 23;
|
||||||
bool is_closed = 24;
|
string dispute_payout_tx_id = 24;
|
||||||
DisputeResult dispute_result = 25;
|
SupportType support_type = 25;
|
||||||
string dispute_payout_tx_id = 26;
|
string mediators_dispute_result = 26;
|
||||||
SupportType support_type = 27;
|
string delayed_payout_tx_id = 27;
|
||||||
string mediators_dispute_result = 28;
|
string donation_address_of_delayed_payout_tx = 28;
|
||||||
string delayed_payout_tx_id = 29;
|
State state = 29;
|
||||||
string donation_address_of_delayed_payout_tx = 30;
|
int64 trade_period_end = 30;
|
||||||
State state = 31;
|
map<string, string> extra_data = 31;
|
||||||
int64 trade_period_end = 32;
|
|
||||||
map<string, string> extra_data = 33;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
message Attachment {
|
message Attachment {
|
||||||
|
|
Loading…
Reference in a new issue