|
|
@ -26,16 +26,17 @@ import org.hyperledger.besu.ethereum.p2p.peers.Peer; |
|
|
|
import org.hyperledger.besu.ethereum.p2p.peers.PeerId; |
|
|
|
import org.hyperledger.besu.ethereum.p2p.peers.PeerId; |
|
|
|
|
|
|
|
|
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.Arrays; |
|
|
|
import java.util.LinkedHashMap; |
|
|
|
|
|
|
|
import java.util.List; |
|
|
|
import java.util.List; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Map; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.Optional; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ConcurrentHashMap; |
|
|
|
import java.util.concurrent.ForkJoinPool; |
|
|
|
import java.util.concurrent.ForkJoinPool; |
|
|
|
|
|
|
|
import java.util.concurrent.TimeUnit; |
|
|
|
import java.util.stream.Stream; |
|
|
|
import java.util.stream.Stream; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import com.google.common.cache.Cache; |
|
|
|
|
|
|
|
import com.google.common.cache.CacheBuilder; |
|
|
|
import com.google.common.hash.BloomFilter; |
|
|
|
import com.google.common.hash.BloomFilter; |
|
|
|
import org.apache.commons.collections4.queue.CircularFifoQueue; |
|
|
|
|
|
|
|
import org.apache.tuweni.bytes.Bytes; |
|
|
|
import org.apache.tuweni.bytes.Bytes; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -54,10 +55,7 @@ public class PeerTable { |
|
|
|
private final Map<Bytes, Integer> distanceCache; |
|
|
|
private final Map<Bytes, Integer> distanceCache; |
|
|
|
private BloomFilter<Bytes> idBloom; |
|
|
|
private BloomFilter<Bytes> idBloom; |
|
|
|
private int evictionCnt = 0; |
|
|
|
private int evictionCnt = 0; |
|
|
|
private final LinkedHashMapWithMaximumSize<String, Integer> ipAddressCheckMap = |
|
|
|
private final Cache<String, Integer> unresponsiveIPs; |
|
|
|
new LinkedHashMapWithMaximumSize<>(DEFAULT_BUCKET_SIZE * N_BUCKETS); |
|
|
|
|
|
|
|
private final CircularFifoQueue<String> invalidIPs = |
|
|
|
|
|
|
|
new CircularFifoQueue<>(DEFAULT_BUCKET_SIZE * N_BUCKETS); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Builds a new peer table, where distance is calculated using the provided nodeId as a baseline. |
|
|
|
* Builds a new peer table, where distance is calculated using the provided nodeId as a baseline. |
|
|
@ -72,6 +70,11 @@ public class PeerTable { |
|
|
|
.toArray(Bucket[]::new); |
|
|
|
.toArray(Bucket[]::new); |
|
|
|
this.distanceCache = new ConcurrentHashMap<>(); |
|
|
|
this.distanceCache = new ConcurrentHashMap<>(); |
|
|
|
this.maxEntriesCnt = N_BUCKETS * DEFAULT_BUCKET_SIZE; |
|
|
|
this.maxEntriesCnt = N_BUCKETS * DEFAULT_BUCKET_SIZE; |
|
|
|
|
|
|
|
this.unresponsiveIPs = |
|
|
|
|
|
|
|
CacheBuilder.newBuilder() |
|
|
|
|
|
|
|
.maximumSize(maxEntriesCnt) |
|
|
|
|
|
|
|
.expireAfterWrite(15L, TimeUnit.MINUTES) |
|
|
|
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
|
|
// A bloom filter with 4096 expected insertions of 64-byte keys with a 0.1% false positive
|
|
|
|
// A bloom filter with 4096 expected insertions of 64-byte keys with a 0.1% false positive
|
|
|
|
// probability yields a memory footprint of ~7.5kb.
|
|
|
|
// probability yields a memory footprint of ~7.5kb.
|
|
|
@ -140,7 +143,6 @@ public class PeerTable { |
|
|
|
if (!res.isPresent()) { |
|
|
|
if (!res.isPresent()) { |
|
|
|
idBloom.put(id); |
|
|
|
idBloom.put(id); |
|
|
|
distanceCache.put(id, distance); |
|
|
|
distanceCache.put(id, distance); |
|
|
|
ipAddressCheckMap.put(getKey(peer.getEndpoint()), peer.getEndpoint().getUdpPort()); |
|
|
|
|
|
|
|
return AddResult.added(); |
|
|
|
return AddResult.added(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -214,26 +216,12 @@ public class PeerTable { |
|
|
|
|
|
|
|
|
|
|
|
public boolean isIpAddressInvalid(final Endpoint endpoint) { |
|
|
|
public boolean isIpAddressInvalid(final Endpoint endpoint) { |
|
|
|
final String key = getKey(endpoint); |
|
|
|
final String key = getKey(endpoint); |
|
|
|
if (invalidIPs.contains(key)) { |
|
|
|
return unresponsiveIPs.getIfPresent(key) != null; |
|
|
|
return true; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
if (ipAddressCheckMap.containsKey(key) && ipAddressCheckMap.get(key) != endpoint.getUdpPort()) { |
|
|
|
|
|
|
|
// This peer has multiple discovery services on the same IP address + TCP port.
|
|
|
|
|
|
|
|
invalidIPs.add(key); |
|
|
|
|
|
|
|
for (final Bucket bucket : table) { |
|
|
|
|
|
|
|
bucket.getPeers().stream() |
|
|
|
|
|
|
|
.filter(p -> p.getEndpoint().getHost().equals(endpoint.getHost())) |
|
|
|
|
|
|
|
.forEach(bucket::evict); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
return true; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
return false; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
public void invalidateIP(final Endpoint endpoint) { |
|
|
|
public void invalidateIP(final Endpoint endpoint) { |
|
|
|
final String key = getKey(endpoint); |
|
|
|
final String key = getKey(endpoint); |
|
|
|
invalidIPs.add(key); |
|
|
|
unresponsiveIPs.put(key, Integer.MAX_VALUE); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static String getKey(final Endpoint endpoint) { |
|
|
|
private static String getKey(final Endpoint endpoint) { |
|
|
@ -313,20 +301,6 @@ public class PeerTable { |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
private static class LinkedHashMapWithMaximumSize<K, V> extends LinkedHashMap<K, V> { |
|
|
|
|
|
|
|
private final int maxSize; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public LinkedHashMapWithMaximumSize(final int maxSize) { |
|
|
|
|
|
|
|
super(maxSize, 0.75f, false); |
|
|
|
|
|
|
|
this.maxSize = maxSize; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@Override |
|
|
|
|
|
|
|
protected boolean removeEldestEntry(final Map.Entry<K, V> eldest) { |
|
|
|
|
|
|
|
return size() > maxSize; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static class EvictResult { |
|
|
|
static class EvictResult { |
|
|
|
public enum EvictOutcome { |
|
|
|
public enum EvictOutcome { |
|
|
|
EVICTED, |
|
|
|
EVICTED, |
|
|
|