mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
Refactor dispute agent selection.
This commit is contained in:
parent
638848de24
commit
2d340432eb
@ -777,10 +777,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
||||
if (openOffer.getState() == OpenOffer.State.AVAILABLE) {
|
||||
Offer offer = openOffer.getOffer();
|
||||
if (preferences.getIgnoreTradersList().stream().noneMatch(fullAddress -> fullAddress.equals(peer.getFullAddress()))) {
|
||||
mediatorNodeAddress = DisputeAgentSelection.getLeastUsedMediator(tradeStatisticsManager, mediatorManager).getNodeAddress();
|
||||
mediatorNodeAddress = DisputeAgentSelection.getRandomMediator(mediatorManager).getNodeAddress();
|
||||
openOffer.setMediatorNodeAddress(mediatorNodeAddress);
|
||||
|
||||
refundAgentNodeAddress = DisputeAgentSelection.getLeastUsedRefundAgent(tradeStatisticsManager, refundAgentManager).getNodeAddress();
|
||||
refundAgentNodeAddress = DisputeAgentSelection.getRandomRefundAgent(refundAgentManager).getNodeAddress();
|
||||
openOffer.setRefundAgentNodeAddress(refundAgentNodeAddress);
|
||||
|
||||
try {
|
||||
|
@ -19,21 +19,13 @@ package bisq.core.offer.availability;
|
||||
|
||||
import bisq.core.support.dispute.agent.DisputeAgent;
|
||||
import bisq.core.support.dispute.agent.DisputeAgentManager;
|
||||
import bisq.core.trade.statistics.TradeStatistics3;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
|
||||
import bisq.common.util.Tuple2;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
|
||||
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;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -43,38 +35,20 @@ import static com.google.common.base.Preconditions.checkArgument;
|
||||
public class DisputeAgentSelection {
|
||||
public static final int LOOK_BACK_RANGE = 100;
|
||||
|
||||
public static <T extends DisputeAgent> T getLeastUsedMediator(TradeStatisticsManager tradeStatisticsManager,
|
||||
DisputeAgentManager<T> disputeAgentManager) {
|
||||
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
||||
disputeAgentManager,
|
||||
true);
|
||||
public static <T extends DisputeAgent> T getRandomMediator(DisputeAgentManager<T> disputeAgentManager) {
|
||||
return getRandomDisputeAgent(disputeAgentManager);
|
||||
}
|
||||
|
||||
public static <T extends DisputeAgent> T getLeastUsedRefundAgent(TradeStatisticsManager tradeStatisticsManager,
|
||||
DisputeAgentManager<T> disputeAgentManager) {
|
||||
return getLeastUsedDisputeAgent(tradeStatisticsManager,
|
||||
disputeAgentManager,
|
||||
false);
|
||||
public static <T extends DisputeAgent> T getRandomRefundAgent(DisputeAgentManager<T> disputeAgentManager) {
|
||||
return getRandomDisputeAgent(disputeAgentManager);
|
||||
}
|
||||
|
||||
private static <T extends DisputeAgent> T getLeastUsedDisputeAgent(TradeStatisticsManager tradeStatisticsManager,
|
||||
DisputeAgentManager<T> disputeAgentManager,
|
||||
boolean isMediator) {
|
||||
// We take last 100 entries from trade statistics
|
||||
Stream<TradeStatistics3> stream = tradeStatisticsManager.getNavigableTradeStatisticsSet().descendingSet().stream()
|
||||
.limit(LOOK_BACK_RANGE);
|
||||
|
||||
// We stored only first 4 chars of disputeAgents onion address
|
||||
List<String> lastAddressesUsedInTrades = stream
|
||||
.map(tradeStatistics3 -> isMediator ? tradeStatistics3.getMediator() : tradeStatistics3.getRefundAgent())
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
private static <T extends DisputeAgent> T getRandomDisputeAgent(DisputeAgentManager<T> disputeAgentManager) {
|
||||
Set<String> disputeAgents = disputeAgentManager.getObservableMap().values().stream()
|
||||
.map(disputeAgent -> disputeAgent.getNodeAddress().getFullAddress())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
String result = getLeastUsedDisputeAgent(lastAddressesUsedInTrades, disputeAgents);
|
||||
String result = getRandomDisputeAgent(disputeAgents);
|
||||
|
||||
Optional<T> optionalDisputeAgent = disputeAgentManager.getObservableMap().values().stream()
|
||||
.filter(e -> e.getNodeAddress().getFullAddress().equals(result))
|
||||
@ -84,21 +58,10 @@ public class DisputeAgentSelection {
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
static String getLeastUsedDisputeAgent(List<String> lastAddressesUsedInTrades, Set<String> disputeAgents) {
|
||||
checkArgument(!disputeAgents.isEmpty(), "disputeAgents must not be empty");
|
||||
List<Tuple2<String, AtomicInteger>> disputeAgentTuples = disputeAgents.stream()
|
||||
.map(e -> new Tuple2<>(e, new AtomicInteger(0)))
|
||||
.collect(Collectors.toList());
|
||||
disputeAgentTuples.forEach(tuple -> {
|
||||
int count = (int) lastAddressesUsedInTrades.stream()
|
||||
.filter(tuple.first::startsWith) // we use only first 4 chars for comparing
|
||||
.mapToInt(e -> 1)
|
||||
.count();
|
||||
tuple.second.set(count);
|
||||
});
|
||||
|
||||
disputeAgentTuples.sort(Comparator.comparing(e -> e.first));
|
||||
disputeAgentTuples.sort(Comparator.comparingInt(e -> e.second.get()));
|
||||
return disputeAgentTuples.get(0).first;
|
||||
static String getRandomDisputeAgent(Set<String> disputeAgents) {
|
||||
if (disputeAgents.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
return (String) disputeAgents.toArray()[new Random().nextInt(disputeAgents.size())];
|
||||
}
|
||||
}
|
||||
|
@ -62,7 +62,7 @@ public class ProcessOfferAvailabilityResponse extends Task<OfferAvailabilityMode
|
||||
NodeAddress mediator = offerAvailabilityResponse.getMediator();
|
||||
if (mediator == null) {
|
||||
// We do not get a mediator from old clients so we need to handle the null case.
|
||||
mediator = DisputeAgentSelection.getLeastUsedMediator(model.getTradeStatisticsManager(), model.getMediatorManager()).getNodeAddress();
|
||||
mediator = DisputeAgentSelection.getRandomMediator(model.getMediatorManager()).getNodeAddress();
|
||||
}
|
||||
model.setSelectedMediator(mediator);
|
||||
|
||||
|
@ -18,8 +18,9 @@
|
||||
package bisq.core.offer.availability;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -28,58 +29,39 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ArbitratorSelectionTest {
|
||||
@Test
|
||||
public void testGetLeastUsedArbitrator() {
|
||||
// We get least used selected
|
||||
List<String> lastAddressesUsedInTrades;
|
||||
Set<String> arbitrators;
|
||||
String result;
|
||||
public void testGetRandomArbitratorFromZero() {
|
||||
testArbitratorSelection(10, new HashSet<>());
|
||||
}
|
||||
@Test
|
||||
public void testGetRandomArbitratorFromOne() {
|
||||
testArbitratorSelection(10, new HashSet<>(Arrays.asList("arb1")));
|
||||
}
|
||||
@Test
|
||||
public void testGetRandomArbitratorFromTwo() {
|
||||
testArbitratorSelection(10000, new HashSet<>(Arrays.asList("arb1", "arb2")));
|
||||
}
|
||||
@Test
|
||||
public void testGetRandomArbitratorFromThree() {
|
||||
testArbitratorSelection(10000, new HashSet<>(Arrays.asList("arb1", "arb2", "arb3")));
|
||||
}
|
||||
@Test
|
||||
public void testGetRandomArbitratorFromFour() {
|
||||
testArbitratorSelection(1000, new HashSet<>(Arrays.asList("arb1", "arb2", "arb3", "arb4")));
|
||||
}
|
||||
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb1");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb2", result);
|
||||
|
||||
// if all are same we use first according to alphanumeric sorting
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb3");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2", "arb3"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb1", result);
|
||||
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb3", "arb1");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2", "arb3"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb2", result);
|
||||
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb3", "arb1", "arb2");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2", "arb3"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb3", result);
|
||||
|
||||
lastAddressesUsedInTrades = Arrays.asList("xxx", "ccc", "aaa");
|
||||
arbitrators = new HashSet<>(Arrays.asList("aaa", "ccc", "xxx"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("aaa", result);
|
||||
lastAddressesUsedInTrades = Arrays.asList("333", "000", "111");
|
||||
arbitrators = new HashSet<>(Arrays.asList("111", "333", "000"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("000", result);
|
||||
|
||||
// if winner is not in our arb list we use our arb from arbitrators even if never used in trades
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb3");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb4"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb4", result);
|
||||
|
||||
// if winner (arb2) is not in our arb list we use our arb from arbitrators
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb1", "arb1", "arb2");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb1", result);
|
||||
|
||||
// arb1 is used least
|
||||
lastAddressesUsedInTrades = Arrays.asList("arb1", "arb2", "arb2", "arb2", "arb1", "arb1", "arb2");
|
||||
arbitrators = new HashSet<>(Arrays.asList("arb1", "arb2"));
|
||||
result = DisputeAgentSelection.getLeastUsedDisputeAgent(lastAddressesUsedInTrades, arbitrators);
|
||||
assertEquals("arb1", result);
|
||||
private void testArbitratorSelection(int iterations, Set<String> arbitrators) {
|
||||
double expectedPercentage = 1.00 / arbitrators.size();
|
||||
System.out.printf("%ntestArbitratorSelection with %d arbitrators %d iterations, expected percentage=%f%n",
|
||||
arbitrators.size(), iterations, expectedPercentage);
|
||||
Map<String, Integer> results = new HashMap<>();
|
||||
for (int i=0; i < iterations; i++) {
|
||||
String selectedArb = DisputeAgentSelection.getRandomDisputeAgent(arbitrators);
|
||||
if (selectedArb != null) {
|
||||
results.put(selectedArb, 1 + results.getOrDefault(selectedArb, 0));
|
||||
}
|
||||
}
|
||||
assertEquals(results.size(), arbitrators.size());
|
||||
results.forEach((k, v) -> System.out.printf("arb=%s result=%d percentage=%f%n", k, v, (double)v / iterations));
|
||||
results.forEach((k, v) -> assertEquals(expectedPercentage, (double)v / iterations, 0.1));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user