Skip to content

Commit

Permalink
Merge pull request #4543 from chimp1984/dispute-agent-branch
Browse files Browse the repository at this point in the history
Add signature to dispute result and various other improvements
  • Loading branch information
sqrrm committed Sep 25, 2020
2 parents 00ccec8 + 423cc71 commit 2c252e9
Show file tree
Hide file tree
Showing 31 changed files with 1,540 additions and 481 deletions.
44 changes: 43 additions & 1 deletion core/src/main/java/bisq/core/dao/DaoFacade.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@

import bisq.asset.Asset;

import bisq.common.config.Config;
import bisq.common.handlers.ErrorMessageHandler;
import bisq.common.handlers.ExceptionHandler;
import bisq.common.handlers.ResultHandler;
Expand All @@ -95,9 +96,14 @@
import java.io.IOException;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Consumer;

import lombok.extern.slf4j.Slf4j;
Expand Down Expand Up @@ -423,10 +429,18 @@ public int getFirstBlockOfPhaseForDisplay(int height, DaoPhase.Phase phase) {
case RESULT:
break;
}

return firstBlock;
}

public Map<Integer, Date> getBlockStartDateByCycleIndex() {
AtomicInteger index = new AtomicInteger();
Map<Integer, Date> map = new HashMap<>();
periodService.getCycles()
.forEach(cycle -> daoStateService.getBlockAtHeight(cycle.getHeightOfFirstBlock())
.ifPresent(block -> map.put(index.getAndIncrement(), new Date(block.getTime()))));
return map;
}

// Because last block in request and voting phases must not be used for making a tx as it will get confirmed in the
// next block which would be already the next phase we hide that last block to the user and add it to the break.
public int getLastBlockOfPhaseForDisplay(int height, DaoPhase.Phase phase) {
Expand Down Expand Up @@ -750,4 +764,32 @@ public long getRequiredBond(BondedRoleType bondedRoleType) {
long baseFactor = daoStateService.getParamValueAsCoin(Param.BONDED_ROLE_FACTOR, height).value;
return requiredBondUnit * baseFactor;
}

public Set<String> getAllPastParamValues(Param param) {
Set<String> set = new HashSet<>();
periodService.getCycles().forEach(cycle -> {
set.add(getParamValue(param, cycle.getHeightOfFirstBlock()));
});
return set;
}

public Set<String> getAllDonationAddresses() {
// We support any of the past addresses as well as in case the peer has not enabled the DAO or is out of sync we
// do not want to break validation.
Set<String> allPastParamValues = getAllPastParamValues(Param.RECIPIENT_BTC_ADDRESS);

// If Dao is deactivated we need to add the default address as getAllPastParamValues will not return us any.
if (allPastParamValues.isEmpty()) {
allPastParamValues.add(Param.RECIPIENT_BTC_ADDRESS.getDefaultValue());
}

if (Config.baseCurrencyNetwork().isMainnet()) {
// If Dao is deactivated we need to add the past addresses used as well.
// This list need to be updated once a new address gets defined.
allPastParamValues.add("3EtUWqsGThPtjwUczw27YCo6EWvQdaPUyp"); // burning man 2019
allPastParamValues.add("3A8Zc1XioE2HRzYfbb5P8iemCS72M6vRJV"); // burningman2
}

return allPastParamValues;
}
}
19 changes: 19 additions & 0 deletions core/src/main/java/bisq/core/support/dispute/Dispute.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;

import lombok.EqualsAndHashCode;
Expand Down Expand Up @@ -103,6 +104,15 @@ public final class Dispute implements NetworkPayload {
@Nullable
private String delayedPayoutTxId;

// Added at v1.3.9
@Setter
@Nullable
private String donationAddressOfDelayedPayoutTx;
// We do not persist uid, it is only used by dispute agents to guarantee an uid.
@Setter
@Nullable
private transient String uid;


///////////////////////////////////////////////////////////////////////////////////////////
// Constructor
Expand Down Expand Up @@ -192,6 +202,7 @@ public Dispute(String tradeId,
this.supportType = supportType;

id = tradeId + "_" + traderId;
uid = UUID.randomUUID().toString();
}

@Override
Expand Down Expand Up @@ -228,6 +239,7 @@ public protobuf.Dispute toProtoMessage() {
Optional.ofNullable(supportType).ifPresent(result -> builder.setSupportType(SupportType.toProtoMessage(supportType)));
Optional.ofNullable(mediatorsDisputeResult).ifPresent(result -> builder.setMediatorsDisputeResult(mediatorsDisputeResult));
Optional.ofNullable(delayedPayoutTxId).ifPresent(result -> builder.setDelayedPayoutTxId(delayedPayoutTxId));
Optional.ofNullable(donationAddressOfDelayedPayoutTx).ifPresent(result -> builder.setDonationAddressOfDelayedPayoutTx(donationAddressOfDelayedPayoutTx));
return builder.build();
}

Expand Down Expand Up @@ -271,6 +283,11 @@ public static Dispute fromProto(protobuf.Dispute proto, CoreProtoResolver corePr
dispute.setDelayedPayoutTxId(delayedPayoutTxId);
}

String donationAddressOfDelayedPayoutTx = proto.getDonationAddressOfDelayedPayoutTx();
if (!donationAddressOfDelayedPayoutTx.isEmpty()) {
dispute.setDonationAddressOfDelayedPayoutTx(donationAddressOfDelayedPayoutTx);
}

return dispute;
}

Expand Down Expand Up @@ -357,6 +374,7 @@ public String toString() {
return "Dispute{" +
"\n tradeId='" + tradeId + '\'' +
",\n id='" + id + '\'' +
",\n uid='" + uid + '\'' +
",\n traderId=" + traderId +
",\n disputeOpenerIsBuyer=" + disputeOpenerIsBuyer +
",\n disputeOpenerIsMaker=" + disputeOpenerIsMaker +
Expand All @@ -382,6 +400,7 @@ public String toString() {
",\n supportType=" + supportType +
",\n mediatorsDisputeResult='" + mediatorsDisputeResult + '\'' +
",\n delayedPayoutTxId='" + delayedPayoutTxId + '\'' +
",\n donationAddressOfDelayedPayoutTx='" + donationAddressOfDelayedPayoutTx + '\'' +
"\n}";
}
}
89 changes: 80 additions & 9 deletions core/src/main/java/bisq/core/support/dispute/DisputeManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import bisq.core.btc.wallet.BtcWalletService;
import bisq.core.btc.wallet.Restrictions;
import bisq.core.btc.wallet.TradeWalletService;
import bisq.core.dao.DaoFacade;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.monetary.Altcoin;
Expand All @@ -36,6 +37,7 @@
import bisq.core.support.messages.ChatMessage;
import bisq.core.trade.Contract;
import bisq.core.trade.Trade;
import bisq.core.trade.TradeDataValidation;
import bisq.core.trade.TradeManager;
import bisq.core.trade.closed.ClosedTradableManager;

Expand All @@ -46,6 +48,8 @@

import bisq.common.UserThread;
import bisq.common.app.Version;
import bisq.common.config.Config;
import bisq.common.crypto.KeyRing;
import bisq.common.crypto.PubKeyRing;
import bisq.common.handlers.FaultHandler;
import bisq.common.handlers.ResultHandler;
Expand All @@ -58,14 +62,18 @@

import javafx.beans.property.IntegerProperty;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

import java.security.KeyPair;

import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nullable;
Expand All @@ -81,7 +89,15 @@ public abstract class DisputeManager<T extends DisputeList<? extends DisputeList
protected final OpenOfferManager openOfferManager;
protected final PubKeyRing pubKeyRing;
protected final DisputeListService<T> disputeListService;
private final Config config;
private final PriceFeedService priceFeedService;
protected final DaoFacade daoFacade;

@Getter
protected final ObservableList<TradeDataValidation.ValidationException> validationExceptions =
FXCollections.observableArrayList();
@Getter
private final KeyPair signatureKeyPair;


///////////////////////////////////////////////////////////////////////////////////////////
Expand All @@ -95,8 +111,10 @@ public DisputeManager(P2PService p2PService,
TradeManager tradeManager,
ClosedTradableManager closedTradableManager,
OpenOfferManager openOfferManager,
PubKeyRing pubKeyRing,
DaoFacade daoFacade,
KeyRing keyRing,
DisputeListService<T> disputeListService,
Config config,
PriceFeedService priceFeedService) {
super(p2PService, walletsSetup);

Expand All @@ -105,8 +123,11 @@ public DisputeManager(P2PService p2PService,
this.tradeManager = tradeManager;
this.closedTradableManager = closedTradableManager;
this.openOfferManager = openOfferManager;
this.pubKeyRing = pubKeyRing;
this.daoFacade = daoFacade;
this.pubKeyRing = keyRing.getPubKeyRing();
signatureKeyPair = keyRing.getSignatureKeyPair();
this.disputeListService = disputeListService;
this.config = config;
this.priceFeedService = priceFeedService;
}

Expand Down Expand Up @@ -178,7 +199,7 @@ public void addAndPersistChatMessage(ChatMessage message) {
@Nullable
public abstract NodeAddress getAgentNodeAddress(Dispute dispute);

protected abstract Trade.DisputeState getDisputeState_StartedByPeer();
protected abstract Trade.DisputeState getDisputeStateStartedByPeer();

public abstract void cleanupDisputes();

Expand Down Expand Up @@ -209,7 +230,7 @@ public String getNrOfDisputes(boolean isBuyer, Contract contract) {
return disputeListService.getNrOfDisputes(isBuyer, contract);
}

private T getDisputeList() {
protected T getDisputeList() {
return disputeListService.getDisputeList();
}

Expand Down Expand Up @@ -241,6 +262,24 @@ public void onUpdatedDataReceived() {

tryApplyMessages();
cleanupDisputes();

ObservableList<Dispute> disputes = getDisputeList().getList();
disputes.forEach(dispute -> {
try {
TradeDataValidation.validateDonationAddress(dispute, dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade);
TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getBuyerNodeAddress(), config);
TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getSellerNodeAddress(), config);
} catch (TradeDataValidation.AddressException | TradeDataValidation.NodeAddressException e) {
log.error(e.toString());
validationExceptions.add(e);
}
});

TradeDataValidation.testIfAnyDisputeTriedReplay(disputes,
disputeReplayException -> {
log.error(disputeReplayException.toString());
validationExceptions.add(disputeReplayException);
});
}

public boolean isTrader(Dispute dispute) {
Expand Down Expand Up @@ -308,9 +347,21 @@ protected void onOpenNewDisputeMessage(OpenNewDisputeMessage openNewDisputeMessa
}

addMediationResultMessage(dispute);

try {
TradeDataValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx(), daoFacade);
TradeDataValidation.testIfDisputeTriesReplay(dispute, disputeList.getList());
TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getBuyerNodeAddress(), config);
TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getSellerNodeAddress(), config);
} catch (TradeDataValidation.AddressException |
TradeDataValidation.DisputeReplayException |
TradeDataValidation.NodeAddressException e) {
log.error(e.toString());
validationExceptions.add(e);
}
}

// not dispute requester receives that from dispute agent
// Not-dispute-requester receives that msg from dispute agent
protected void onPeerOpenedDisputeMessage(PeerOpenedDisputeMessage peerOpenedDisputeMessage) {
T disputeList = getDisputeList();
if (disputeList == null) {
Expand All @@ -320,14 +371,33 @@ protected void onPeerOpenedDisputeMessage(PeerOpenedDisputeMessage peerOpenedDis

String errorMessage = null;
Dispute dispute = peerOpenedDisputeMessage.getDispute();

Optional<Trade> optionalTrade = tradeManager.getTradeById(dispute.getTradeId());
if (!optionalTrade.isPresent()) {
return;
}

Trade trade = optionalTrade.get();
try {
TradeDataValidation.validatePayoutTx(trade,
trade.getDelayedPayoutTx(),
dispute,
daoFacade,
btcWalletService);
} catch (TradeDataValidation.ValidationException e) {
// The peer sent us an invalid donation address. We do not return here as we don't want to break
// mediation/arbitration and log only the issue. The dispute agent will run validation as well and will get
// a popup displayed to react.
log.warn("Donation address is invalid. {}", e.toString());
}

if (!isAgent(dispute)) {
if (!disputeList.contains(dispute)) {
Optional<Dispute> storedDisputeOptional = findDispute(dispute);
if (!storedDisputeOptional.isPresent()) {
dispute.setStorage(disputeListService.getStorage());
disputeList.add(dispute);
Optional<Trade> tradeOptional = tradeManager.getTradeById(dispute.getTradeId());
tradeOptional.ifPresent(trade -> trade.setDisputeState(getDisputeState_StartedByPeer()));
trade.setDisputeState(getDisputeStateStartedByPeer());
errorMessage = null;
} else {
// valid case if both have opened a dispute and agent was not online.
Expand Down Expand Up @@ -516,6 +586,7 @@ private void doSendPeerOpenedDisputeMessage(Dispute disputeFromOpener,
disputeFromOpener.isSupportTicket(),
disputeFromOpener.getSupportType());
dispute.setDelayedPayoutTxId(disputeFromOpener.getDelayedPayoutTxId());
dispute.setDonationAddressOfDelayedPayoutTx(disputeFromOpener.getDonationAddressOfDelayedPayoutTx());

Optional<Dispute> storedDisputeOptional = findDispute(dispute);

Expand Down Expand Up @@ -609,7 +680,7 @@ public void onFault(String errorMessage) {
}

// dispute agent send result to trader
public void sendDisputeResultMessage(DisputeResult disputeResult, Dispute dispute, String text) {
public void sendDisputeResultMessage(DisputeResult disputeResult, Dispute dispute, String summaryText) {
T disputeList = getDisputeList();
if (disputeList == null) {
log.warn("disputes is null");
Expand All @@ -621,7 +692,7 @@ public void sendDisputeResultMessage(DisputeResult disputeResult, Dispute disput
dispute.getTradeId(),
dispute.getTraderPubKeyRing().hashCode(),
false,
text,
summaryText,
p2PService.getAddress());

disputeResult.setChatMessage(chatMessage);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import bisq.core.btc.wallet.TradeWalletService;
import bisq.core.btc.wallet.TxBroadcaster;
import bisq.core.btc.wallet.WalletService;
import bisq.core.dao.DaoFacade;
import bisq.core.locale.Res;
import bisq.core.offer.OpenOffer;
import bisq.core.offer.OpenOfferManager;
Expand Down Expand Up @@ -53,6 +54,8 @@
import bisq.common.Timer;
import bisq.common.UserThread;
import bisq.common.app.Version;
import bisq.common.config.Config;
import bisq.common.crypto.KeyRing;
import bisq.common.crypto.PubKeyRing;

import org.bitcoinj.core.AddressFormatException;
Expand Down Expand Up @@ -89,11 +92,13 @@ public ArbitrationManager(P2PService p2PService,
TradeManager tradeManager,
ClosedTradableManager closedTradableManager,
OpenOfferManager openOfferManager,
PubKeyRing pubKeyRing,
DaoFacade daoFacade,
KeyRing keyRing,
ArbitrationDisputeListService arbitrationDisputeListService,
Config config,
PriceFeedService priceFeedService) {
super(p2PService, tradeWalletService, walletService, walletsSetup, tradeManager, closedTradableManager,
openOfferManager, pubKeyRing, arbitrationDisputeListService, priceFeedService);
openOfferManager, daoFacade, keyRing, arbitrationDisputeListService, config, priceFeedService);
}


Expand Down Expand Up @@ -135,7 +140,7 @@ public NodeAddress getAgentNodeAddress(Dispute dispute) {
}

@Override
protected Trade.DisputeState getDisputeState_StartedByPeer() {
protected Trade.DisputeState getDisputeStateStartedByPeer() {
return Trade.DisputeState.DISPUTE_STARTED_BY_PEER;
}

Expand Down
Loading

0 comments on commit 2c252e9

Please sign in to comment.