diff --git a/Makefile b/Makefile index bfb15290..e872bdc0 100644 --- a/Makefile +++ b/Makefile @@ -145,6 +145,17 @@ arbitrator-desktop-local: --apiPassword=apitest \ --apiPort=9998 +arbitrator2-daemon-local: + # Arbitrator needs to be registered before making trades + ./haveno-daemon$(APP_EXT) \ + --baseCurrencyNetwork=XMR_LOCAL \ + --useLocalhostForP2P=true \ + --useDevPrivilegeKeys=true \ + --nodePort=7777 \ + --appName=haveno-XMR_LOCAL_arbitrator2 \ + --apiPassword=apitest \ + --apiPort=10001 + arbitrator2-desktop-local: # Arbitrator needs to be registered before making trades ./haveno-desktop$(APP_EXT) \ diff --git a/core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java b/core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java index 859e731e..4f30e99d 100644 --- a/core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java +++ b/core/src/main/java/haveno/core/offer/availability/DisputeAgentSelection.java @@ -32,6 +32,7 @@ import java.util.Comparator; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Random; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Collectors; @@ -47,7 +48,7 @@ public class DisputeAgentSelection { return getLeastUsedDisputeAgent(tradeStatisticsManager, disputeAgentManager, null); -} + } public static T getLeastUsedArbitrator(TradeStatisticsManager tradeStatisticsManager, DisputeAgentManager disputeAgentManager, @@ -109,4 +110,40 @@ public class DisputeAgentSelection { disputeAgentTuples.sort(Comparator.comparingInt(e -> e.second.get())); return disputeAgentTuples.get(0).first; } + + public static T getRandomArbitrator(DisputeAgentManager disputeAgentManager) { + return getRandomArbitrator(disputeAgentManager, null); + } + + public static T getRandomArbitrator(DisputeAgentManager disputeAgentManager, + Set excludedArbitrator) { + return getRandomDisputeAgent(disputeAgentManager, excludedArbitrator); + } + + private static T getRandomDisputeAgent(DisputeAgentManager disputeAgentManager, + Set excludedDisputeAgents) { + + // get all dispute agents + Set disputeAgents = disputeAgentManager.getObservableMap().values().stream() + .map(disputeAgent -> disputeAgent.getNodeAddress().getFullAddress()) + .collect(Collectors.toSet()); + + // remove excluded dispute agents + if (excludedDisputeAgents != null) disputeAgents.removeAll(excludedDisputeAgents.stream().map(NodeAddress::getFullAddress).collect(Collectors.toList())); + if (disputeAgents.isEmpty()) return null; + + // get random dispute agent + String result = getRandomDisputeAgent(disputeAgents); + Optional optionalDisputeAgent = disputeAgentManager.getObservableMap().values().stream() + .filter(e -> e.getNodeAddress().getFullAddress().equals(result)) + .findAny(); + checkArgument(optionalDisputeAgent.isPresent(), "optionalDisputeAgent has to be present"); + return optionalDisputeAgent.get(); + } + + private static String getRandomDisputeAgent(Set disputeAgents) { + int randomIndex = new Random().nextInt(disputeAgents.size()); + List elements = new ArrayList(disputeAgents); + return elements.get(randomIndex); + } } diff --git a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerSendSignOfferRequest.java b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerSendSignOfferRequest.java index dfdd60b0..c0b34087 100644 --- a/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerSendSignOfferRequest.java +++ b/core/src/main/java/haveno/core/offer/placeoffer/tasks/MakerSendSignOfferRequest.java @@ -92,12 +92,12 @@ public class MakerSendSignOfferRequest extends Task { } private void sendSignOfferRequests(SignOfferRequest request, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { - Arbitrator leastUsedArbitrator = DisputeAgentSelection.getLeastUsedArbitrator(model.getTradeStatisticsManager(), model.getArbitratorManager()); - if (leastUsedArbitrator == null) { - errorMessageHandler.handleErrorMessage("Could not get least used arbitrator to send " + request.getClass().getSimpleName() + " for offer " + request.getOfferId()); + Arbitrator randomArbitrator = DisputeAgentSelection.getRandomArbitrator(model.getArbitratorManager()); + if (randomArbitrator == null) { + errorMessageHandler.handleErrorMessage("Could not get random arbitrator to send " + request.getClass().getSimpleName() + " for offer " + request.getOfferId()); return; } - sendSignOfferRequests(request, leastUsedArbitrator.getNodeAddress(), new HashSet(), resultHandler, errorMessageHandler); + sendSignOfferRequests(request, randomArbitrator.getNodeAddress(), new HashSet(), resultHandler, errorMessageHandler); } private void sendSignOfferRequests(SignOfferRequest request, NodeAddress arbitratorNodeAddress, Set excludedArbitrators, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) { @@ -135,7 +135,7 @@ public class MakerSendSignOfferRequest extends Task { public void onFault(String errorMessage) { log.warn("Arbitrator unavailable: {}", errorMessage); excludedArbitrators.add(arbitratorNodeAddress); - Arbitrator altArbitrator = DisputeAgentSelection.getLeastUsedArbitrator(model.getTradeStatisticsManager(), model.getArbitratorManager(), excludedArbitrators); + Arbitrator altArbitrator = DisputeAgentSelection.getRandomArbitrator(model.getArbitratorManager(), excludedArbitrators); if (altArbitrator == null) { errorMessageHandler.handleErrorMessage("Offer " + request.getOfferId() + " could not be signed by any arbitrator"); return; diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java index df962beb..20135315 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/TakerSendInitTradeRequestToArbitrator.java @@ -45,8 +45,15 @@ public class TakerSendInitTradeRequestToArbitrator extends TradeTask { try { runInterceptHook(); - // send request to signing arbitrator then least used arbitrators until success - sendInitTradeRequests(trade.getOffer().getOfferPayload().getArbitratorSigner(), new HashSet(), () -> { + // get least used arbitrator + Arbitrator leastUsedArbitrator = DisputeAgentSelection.getLeastUsedArbitrator(processModel.getTradeStatisticsManager(), processModel.getArbitratorManager()); + if (leastUsedArbitrator == null) { + failed("Could not get least used arbitrator to send " + InitTradeRequest.class.getSimpleName() + " for offer " + trade.getId()); + return; + } + + // send request to least used arbitrators until success + sendInitTradeRequests(leastUsedArbitrator.getNodeAddress(), new HashSet(), () -> { trade.addInitProgressStep(); complete(); }, (errorMessage) -> {