mirror of https://github.com/hyperledger/besu
[NC-875] Implement iterative peer search (#268)
* basic updatdes * spotless inter alia * building successfully * funtioning * minor update to docs * rebased in previous commit, attempt to pass build server * eliminate distanceSortedPeers * spotless update * revamp outstanding requests * implementation of timeoutTask and corresponding test * use setPeriodic * testing with DiscoveryPeer * remove commenceTimeoutTask from constructor * isolate clock functionality out of recursive state * update to docs * validate size of outstandingrequestlist * improve sanity check test * remove extraneous copy * add accurate interface parameters * finalize
parent
e0d4f21e73
commit
df88b69b2b
@ -0,0 +1,50 @@ |
||||
/* |
||||
* Copyright 2018 ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.p2p.discovery.internal; |
||||
|
||||
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||
|
||||
import java.util.Arrays; |
||||
|
||||
public class PeerDistanceCalculator { |
||||
|
||||
/** |
||||
* Calculates the XOR distance between two values. |
||||
* |
||||
* @param v1 the first value |
||||
* @param v2 the second value |
||||
* @return the distance |
||||
*/ |
||||
static int distance(final BytesValue v1, final BytesValue v2) { |
||||
assert (v1.size() == v2.size()); |
||||
final byte[] v1b = v1.extractArray(); |
||||
final byte[] v2b = v2.extractArray(); |
||||
if (Arrays.equals(v1b, v2b)) { |
||||
return 0; |
||||
} |
||||
int distance = v1b.length * 8; |
||||
for (int i = 0; i < v1b.length; i++) { |
||||
final byte xor = (byte) (0xff & (v1b[i] ^ v2b[i])); |
||||
if (xor == 0) { |
||||
distance -= 8; |
||||
} else { |
||||
int p = 7; |
||||
while (((xor >> p--) & 0x01) == 0) { |
||||
distance--; |
||||
} |
||||
break; |
||||
} |
||||
} |
||||
return distance; |
||||
} |
||||
} |
@ -0,0 +1,223 @@ |
||||
/* |
||||
* Copyright 2018 ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.p2p.discovery.internal; |
||||
|
||||
import static java.util.stream.Collectors.toList; |
||||
import static tech.pegasys.pantheon.ethereum.p2p.discovery.internal.PeerDistanceCalculator.distance; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.p2p.discovery.DiscoveryPeer; |
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.Peer; |
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; |
||||
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.Objects; |
||||
|
||||
import com.google.common.annotations.VisibleForTesting; |
||||
|
||||
class RecursivePeerRefreshState { |
||||
private final int CONCURRENT_REQUEST_LIMIT = 3; |
||||
private final BytesValue target; |
||||
private final PeerBlacklist peerBlacklist; |
||||
private final BondingAgent bondingAgent; |
||||
private final NeighborFinder neighborFinder; |
||||
private final List<PeerDistance> anteList; |
||||
private final List<OutstandingRequest> outstandingRequestList; |
||||
private final List<BytesValue> contactedInCurrentExecution; |
||||
|
||||
RecursivePeerRefreshState( |
||||
final BytesValue target, |
||||
final PeerBlacklist peerBlacklist, |
||||
final BondingAgent bondingAgent, |
||||
final NeighborFinder neighborFinder) { |
||||
this.target = target; |
||||
this.peerBlacklist = peerBlacklist; |
||||
this.bondingAgent = bondingAgent; |
||||
this.neighborFinder = neighborFinder; |
||||
this.anteList = new ArrayList<>(); |
||||
this.outstandingRequestList = new ArrayList<>(); |
||||
this.contactedInCurrentExecution = new ArrayList<>(); |
||||
} |
||||
|
||||
void kickstartBootstrapPeers(final List<Peer> bootstrapPeers) { |
||||
for (Peer bootstrapPeer : bootstrapPeers) { |
||||
final BytesValue peerId = bootstrapPeer.getId(); |
||||
outstandingRequestList.add(new OutstandingRequest(bootstrapPeer)); |
||||
contactedInCurrentExecution.add(peerId); |
||||
bondingAgent.performBonding(bootstrapPeer, true); |
||||
neighborFinder.issueFindNodeRequest(bootstrapPeer, target); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* This method is intended to be called periodically by the {@link PeerDiscoveryController}, which |
||||
* will maintain a timer for purposes of effecting expiration of requests outstanding. Requests |
||||
* once encountered are deemed eligible for eviction if they have not been dispatched before the |
||||
* next invocation of the method. |
||||
*/ |
||||
public void executeTimeoutEvaluation() { |
||||
for (int i = 0; i < outstandingRequestList.size(); i++) { |
||||
if (outstandingRequestList.get(i).getEvaluation()) { |
||||
final List<DiscoveryPeer> queryCandidates = determineFindNodeCandidates(anteList.size()); |
||||
for (DiscoveryPeer candidate : queryCandidates) { |
||||
if (!contactedInCurrentExecution.contains(candidate.getId()) |
||||
&& !outstandingRequestList.contains(new OutstandingRequest(candidate))) { |
||||
outstandingRequestList.remove(i); |
||||
executeFindNodeRequest(candidate); |
||||
} |
||||
} |
||||
} |
||||
outstandingRequestList.get(i).setEvaluation(); |
||||
} |
||||
} |
||||
|
||||
private void executeFindNodeRequest(final DiscoveryPeer peer) { |
||||
final BytesValue peerId = peer.getId(); |
||||
outstandingRequestList.add(new OutstandingRequest(peer)); |
||||
contactedInCurrentExecution.add(peerId); |
||||
neighborFinder.issueFindNodeRequest(peer, target); |
||||
} |
||||
|
||||
/** |
||||
* The lookup initiator starts by picking CONCURRENT_REQUEST_LIMIT closest nodes to the target it |
||||
* knows of. The initiator then issues concurrent FindNode packets to those nodes. |
||||
*/ |
||||
private void initiatePeerRefreshCycle(final List<DiscoveryPeer> peers) { |
||||
for (DiscoveryPeer peer : peers) { |
||||
if (!contactedInCurrentExecution.contains(peer.getId())) { |
||||
executeFindNodeRequest(peer); |
||||
} |
||||
} |
||||
} |
||||
|
||||
void onNeighboursPacketReceived(final NeighborsPacketData neighboursPacket, final Peer peer) { |
||||
if (outstandingRequestList.contains(new OutstandingRequest(peer))) { |
||||
final List<DiscoveryPeer> receivedPeerList = neighboursPacket.getNodes(); |
||||
for (DiscoveryPeer receivedPeer : receivedPeerList) { |
||||
if (!peerBlacklist.contains(receivedPeer)) { |
||||
bondingAgent.performBonding(receivedPeer, false); |
||||
anteList.add(new PeerDistance(receivedPeer, distance(target, receivedPeer.getId()))); |
||||
} |
||||
} |
||||
outstandingRequestList.remove(new OutstandingRequest(peer)); |
||||
queryNearestNodes(); |
||||
} |
||||
} |
||||
|
||||
private List<DiscoveryPeer> determineFindNodeCandidates(final int threshold) { |
||||
anteList.sort( |
||||
(peer1, peer2) -> { |
||||
if (peer1.getDistance() > peer2.getDistance()) return 1; |
||||
if (peer1.getDistance() < peer2.getDistance()) return -1; |
||||
return 0; |
||||
}); |
||||
return anteList.subList(0, threshold).stream().map(PeerDistance::getPeer).collect(toList()); |
||||
} |
||||
|
||||
private void queryNearestNodes() { |
||||
if (outstandingRequestList.isEmpty()) { |
||||
final List<DiscoveryPeer> queryCandidates = |
||||
determineFindNodeCandidates(CONCURRENT_REQUEST_LIMIT); |
||||
initiatePeerRefreshCycle(queryCandidates); |
||||
} |
||||
} |
||||
|
||||
@VisibleForTesting |
||||
List<OutstandingRequest> getOutstandingRequestList() { |
||||
return outstandingRequestList; |
||||
} |
||||
|
||||
static class PeerDistance { |
||||
DiscoveryPeer peer; |
||||
Integer distance; |
||||
|
||||
PeerDistance(final DiscoveryPeer peer, final Integer distance) { |
||||
this.peer = peer; |
||||
this.distance = distance; |
||||
} |
||||
|
||||
DiscoveryPeer getPeer() { |
||||
return peer; |
||||
} |
||||
|
||||
Integer getDistance() { |
||||
return distance; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return peer + ": " + distance; |
||||
} |
||||
} |
||||
|
||||
static class OutstandingRequest { |
||||
boolean evaluation; |
||||
Peer peer; |
||||
|
||||
OutstandingRequest(final Peer peer) { |
||||
this.evaluation = false; |
||||
this.peer = peer; |
||||
} |
||||
|
||||
boolean getEvaluation() { |
||||
return evaluation; |
||||
} |
||||
|
||||
Peer getPeer() { |
||||
return peer; |
||||
} |
||||
|
||||
void setEvaluation() { |
||||
this.evaluation = true; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(final Object o) { |
||||
if (this == o) return true; |
||||
if (o == null || getClass() != o.getClass()) return false; |
||||
final OutstandingRequest that = (OutstandingRequest) o; |
||||
return Objects.equals(peer.getId(), that.peer.getId()); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(peer.getId()); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return peer.toString(); |
||||
} |
||||
} |
||||
|
||||
public interface NeighborFinder { |
||||
/** |
||||
* Sends a FIND_NEIGHBORS message to a {@link DiscoveryPeer}, in search of a target value. |
||||
* |
||||
* @param peer the peer to interrogate |
||||
* @param target the target node ID to find |
||||
*/ |
||||
void issueFindNodeRequest(final Peer peer, final BytesValue target); |
||||
} |
||||
|
||||
public interface BondingAgent { |
||||
/** |
||||
* Initiates a bonding PING-PONG cycle with a peer. |
||||
* |
||||
* @param peer The targeted peer. |
||||
* @param bootstrap Whether this is a bootstrap interaction. |
||||
*/ |
||||
void performBonding(final Peer peer, final boolean bootstrap); |
||||
} |
||||
} |
@ -0,0 +1,331 @@ |
||||
/* |
||||
* Copyright 2018 ConsenSys AG. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with |
||||
* the License. You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on |
||||
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the |
||||
* specific language governing permissions and limitations under the License. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.p2p.discovery.internal; |
||||
|
||||
import static java.util.stream.Collectors.toList; |
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.mockito.Mockito.mock; |
||||
import static org.mockito.Mockito.never; |
||||
import static org.mockito.Mockito.verify; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.p2p.discovery.DiscoveryPeer; |
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.Endpoint; |
||||
import tech.pegasys.pantheon.ethereum.p2p.peers.PeerBlacklist; |
||||
import tech.pegasys.pantheon.util.bytes.Bytes32; |
||||
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode; |
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
|
||||
public class RecursivePeerRefreshStateTest { |
||||
private static final ObjectMapper MAPPER = new ObjectMapper(); |
||||
|
||||
private RecursivePeerRefreshState recursivePeerRefreshState; |
||||
|
||||
private final BytesValue target = |
||||
BytesValue.fromHexString( |
||||
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); |
||||
|
||||
private final RecursivePeerRefreshState.BondingAgent bondingAgent = |
||||
mock(RecursivePeerRefreshState.BondingAgent.class); |
||||
private final RecursivePeerRefreshState.NeighborFinder neighborFinder = |
||||
mock(RecursivePeerRefreshState.NeighborFinder.class); |
||||
|
||||
private final List<TestPeer> aggregatePeerList = new ArrayList<>(); |
||||
|
||||
private NeighborsPacketData neighborsPacketData_000; |
||||
private NeighborsPacketData neighborsPacketData_010; |
||||
private NeighborsPacketData neighborsPacketData_011; |
||||
private NeighborsPacketData neighborsPacketData_012; |
||||
private NeighborsPacketData neighborsPacketData_013; |
||||
|
||||
private TestPeer peer_000; |
||||
private TestPeer peer_010; |
||||
private TestPeer peer_020; |
||||
private TestPeer peer_021; |
||||
private TestPeer peer_022; |
||||
private TestPeer peer_023; |
||||
private TestPeer peer_011; |
||||
private TestPeer peer_120; |
||||
private TestPeer peer_121; |
||||
private TestPeer peer_122; |
||||
private TestPeer peer_123; |
||||
private TestPeer peer_012; |
||||
private TestPeer peer_220; |
||||
private TestPeer peer_221; |
||||
private TestPeer peer_222; |
||||
private TestPeer peer_223; |
||||
private TestPeer peer_013; |
||||
private TestPeer peer_320; |
||||
private TestPeer peer_321; |
||||
private TestPeer peer_322; |
||||
private TestPeer peer_323; |
||||
|
||||
@Before |
||||
public void setup() throws Exception { |
||||
JsonNode peers = |
||||
MAPPER.readTree(RecursivePeerRefreshStateTest.class.getResource("/peers.json")); |
||||
recursivePeerRefreshState = |
||||
new RecursivePeerRefreshState(target, new PeerBlacklist(), bondingAgent, neighborFinder); |
||||
|
||||
peer_000 = (TestPeer) generatePeer(peers); |
||||
|
||||
peer_010 = (TestPeer) peer_000.getPeerTable().get(0); |
||||
|
||||
peer_020 = (TestPeer) peer_010.getPeerTable().get(0); |
||||
peer_021 = (TestPeer) peer_010.getPeerTable().get(1); |
||||
peer_022 = (TestPeer) peer_010.getPeerTable().get(2); |
||||
peer_023 = (TestPeer) peer_010.getPeerTable().get(3); |
||||
|
||||
peer_011 = (TestPeer) peer_000.getPeerTable().get(1); |
||||
|
||||
peer_120 = (TestPeer) peer_011.getPeerTable().get(0); |
||||
peer_121 = (TestPeer) peer_011.getPeerTable().get(1); |
||||
peer_122 = (TestPeer) peer_011.getPeerTable().get(2); |
||||
peer_123 = (TestPeer) peer_011.getPeerTable().get(3); |
||||
|
||||
peer_012 = (TestPeer) peer_000.getPeerTable().get(2); |
||||
|
||||
peer_220 = (TestPeer) peer_012.getPeerTable().get(0); |
||||
peer_221 = (TestPeer) peer_012.getPeerTable().get(1); |
||||
peer_222 = (TestPeer) peer_012.getPeerTable().get(2); |
||||
peer_223 = (TestPeer) peer_012.getPeerTable().get(3); |
||||
|
||||
peer_013 = (TestPeer) peer_000.getPeerTable().get(3); |
||||
|
||||
peer_320 = (TestPeer) peer_013.getPeerTable().get(0); |
||||
peer_321 = (TestPeer) peer_013.getPeerTable().get(1); |
||||
peer_322 = (TestPeer) peer_013.getPeerTable().get(2); |
||||
peer_323 = (TestPeer) peer_013.getPeerTable().get(3); |
||||
|
||||
neighborsPacketData_000 = NeighborsPacketData.create(peer_000.getPeerTable()); |
||||
neighborsPacketData_010 = NeighborsPacketData.create(peer_010.getPeerTable()); |
||||
neighborsPacketData_011 = NeighborsPacketData.create(peer_011.getPeerTable()); |
||||
neighborsPacketData_012 = NeighborsPacketData.create(peer_012.getPeerTable()); |
||||
neighborsPacketData_013 = NeighborsPacketData.create(peer_013.getPeerTable()); |
||||
|
||||
addPeersToAggregateListByOrdinalRank(); |
||||
} |
||||
|
||||
private void addPeersToAggregateListByOrdinalRank() { |
||||
aggregatePeerList.add(peer_323); // 1
|
||||
aggregatePeerList.add(peer_011); // 2
|
||||
aggregatePeerList.add(peer_012); // 3
|
||||
aggregatePeerList.add(peer_013); // 4
|
||||
aggregatePeerList.add(peer_020); // 5
|
||||
aggregatePeerList.add(peer_021); // 6
|
||||
aggregatePeerList.add(peer_022); // 7
|
||||
aggregatePeerList.add(peer_023); // 8
|
||||
aggregatePeerList.add(peer_120); // 9
|
||||
aggregatePeerList.add(peer_121); // 10
|
||||
aggregatePeerList.add(peer_122); // 11
|
||||
aggregatePeerList.add(peer_123); // 12
|
||||
aggregatePeerList.add(peer_220); // 13
|
||||
aggregatePeerList.add(peer_221); // 14
|
||||
aggregatePeerList.add(peer_222); // 15
|
||||
aggregatePeerList.add(peer_223); // 16
|
||||
aggregatePeerList.add(peer_320); // 17
|
||||
aggregatePeerList.add(peer_321); // 18
|
||||
aggregatePeerList.add(peer_322); // 19
|
||||
aggregatePeerList.add(peer_010); // 20
|
||||
aggregatePeerList.add(peer_000); // 21
|
||||
} |
||||
|
||||
@Test |
||||
public void shouldEstablishRelativeDistanceValues() { |
||||
for (int i = 0; i < aggregatePeerList.size() - 1; i++) { |
||||
int nodeOrdinalRank = aggregatePeerList.get(i).getOrdinalRank(); |
||||
int neighborOrdinalRank = aggregatePeerList.get(i + 1).getOrdinalRank(); |
||||
assertThat(nodeOrdinalRank).isLessThan(neighborOrdinalRank); |
||||
} |
||||
} |
||||
|
||||
@Test |
||||
public void shouldConfirmPeersMatchCorrespondingPackets() { |
||||
assertThat(matchPeerToCorrespondingPacketData(peer_000, neighborsPacketData_000)).isTrue(); |
||||
assertThat(matchPeerToCorrespondingPacketData(peer_010, neighborsPacketData_010)).isTrue(); |
||||
assertThat(matchPeerToCorrespondingPacketData(peer_011, neighborsPacketData_011)).isTrue(); |
||||
assertThat(matchPeerToCorrespondingPacketData(peer_012, neighborsPacketData_012)).isTrue(); |
||||
assertThat(matchPeerToCorrespondingPacketData(peer_013, neighborsPacketData_013)).isTrue(); |
||||
} |
||||
|
||||
private boolean matchPeerToCorrespondingPacketData( |
||||
final TestPeer peer, final NeighborsPacketData neighborsPacketData) { |
||||
for (TestPeer neighbour : |
||||
neighborsPacketData.getNodes().stream().map(p -> (TestPeer) p).collect(toList())) { |
||||
if (neighbour.getParent() != peer.getIdentifier()) { |
||||
return false; |
||||
} |
||||
if (neighbour.getTier() != peer.getTier() + 1) { |
||||
return false; |
||||
} |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
@Test |
||||
public void shouldIssueRequestToPeerWithLesserDistanceGreaterHops() { |
||||
recursivePeerRefreshState.kickstartBootstrapPeers(Collections.singletonList(peer_000)); |
||||
|
||||
verify(bondingAgent).performBonding(peer_000, true); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_000, target); |
||||
|
||||
recursivePeerRefreshState.onNeighboursPacketReceived(neighborsPacketData_000, peer_000); |
||||
assertThat(recursivePeerRefreshState.getOutstandingRequestList().size()).isLessThanOrEqualTo(3); |
||||
|
||||
verify(bondingAgent).performBonding(peer_010, false); |
||||
verify(bondingAgent).performBonding(peer_011, false); |
||||
verify(bondingAgent).performBonding(peer_012, false); |
||||
verify(bondingAgent).performBonding(peer_013, false); |
||||
|
||||
verify(neighborFinder, never()).issueFindNodeRequest(peer_010, target); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_011, target); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_012, target); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_013, target); |
||||
|
||||
recursivePeerRefreshState.onNeighboursPacketReceived(neighborsPacketData_011, peer_011); |
||||
assertThat(recursivePeerRefreshState.getOutstandingRequestList().size()).isLessThanOrEqualTo(3); |
||||
|
||||
verify(bondingAgent).performBonding(peer_120, false); |
||||
verify(bondingAgent).performBonding(peer_121, false); |
||||
verify(bondingAgent).performBonding(peer_122, false); |
||||
verify(bondingAgent).performBonding(peer_123, false); |
||||
|
||||
recursivePeerRefreshState.onNeighboursPacketReceived(neighborsPacketData_012, peer_012); |
||||
assertThat(recursivePeerRefreshState.getOutstandingRequestList().size()).isLessThanOrEqualTo(3); |
||||
|
||||
verify(bondingAgent).performBonding(peer_220, false); |
||||
verify(bondingAgent).performBonding(peer_221, false); |
||||
verify(bondingAgent).performBonding(peer_222, false); |
||||
verify(bondingAgent).performBonding(peer_223, false); |
||||
|
||||
recursivePeerRefreshState.onNeighboursPacketReceived(neighborsPacketData_013, peer_013); |
||||
assertThat(recursivePeerRefreshState.getOutstandingRequestList().size()).isLessThanOrEqualTo(3); |
||||
|
||||
verify(bondingAgent).performBonding(peer_320, false); |
||||
verify(bondingAgent).performBonding(peer_321, false); |
||||
verify(bondingAgent).performBonding(peer_322, false); |
||||
verify(bondingAgent).performBonding(peer_323, false); |
||||
|
||||
verify(neighborFinder, never()).issueFindNodeRequest(peer_320, target); |
||||
verify(neighborFinder, never()).issueFindNodeRequest(peer_321, target); |
||||
verify(neighborFinder, never()).issueFindNodeRequest(peer_322, target); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_323, target); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldIssueRequestToPeerWithGreaterDistanceOnExpirationOfLowerDistancePeerRequest() { |
||||
recursivePeerRefreshState.kickstartBootstrapPeers(Collections.singletonList(peer_000)); |
||||
recursivePeerRefreshState.executeTimeoutEvaluation(); |
||||
|
||||
verify(bondingAgent).performBonding(peer_000, true); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_000, target); |
||||
|
||||
recursivePeerRefreshState.onNeighboursPacketReceived(neighborsPacketData_000, peer_000); |
||||
assertThat(recursivePeerRefreshState.getOutstandingRequestList().size()).isLessThanOrEqualTo(3); |
||||
|
||||
recursivePeerRefreshState.executeTimeoutEvaluation(); |
||||
|
||||
verify(neighborFinder, never()).issueFindNodeRequest(peer_010, target); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_011, target); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_012, target); |
||||
verify(neighborFinder).issueFindNodeRequest(peer_013, target); |
||||
|
||||
recursivePeerRefreshState.executeTimeoutEvaluation(); |
||||
assertThat(recursivePeerRefreshState.getOutstandingRequestList().size()).isLessThanOrEqualTo(3); |
||||
|
||||
verify(neighborFinder).issueFindNodeRequest(peer_010, target); |
||||
} |
||||
|
||||
private DiscoveryPeer generatePeer(final JsonNode peer) { |
||||
int parent = peer.get("parent").asInt(); |
||||
int tier = peer.get("tier").asInt(); |
||||
int identifier = peer.get("identifier").asInt(); |
||||
int ordinalRank = peer.get("ordinalRank").asInt(); |
||||
BytesValue id = BytesValue.fromHexString(peer.get("id").asText()); |
||||
List<DiscoveryPeer> peerTable = new ArrayList<>(); |
||||
if (peer.get("peerTable") != null) { |
||||
JsonNode peers = peer.get("peerTable"); |
||||
for (JsonNode element : peers) { |
||||
peerTable.add(generatePeer(element)); |
||||
} |
||||
} else { |
||||
peerTable = Collections.emptyList(); |
||||
} |
||||
return new TestPeer(parent, tier, identifier, ordinalRank, id, peerTable); |
||||
} |
||||
|
||||
static class TestPeer extends DiscoveryPeer { |
||||
int parent; |
||||
int tier; |
||||
int identifier; |
||||
int ordinalRank; |
||||
List<DiscoveryPeer> peerTable; |
||||
|
||||
TestPeer( |
||||
final int parent, |
||||
final int tier, |
||||
final int identifier, |
||||
final int ordinalRank, |
||||
final BytesValue id, |
||||
final List<DiscoveryPeer> peerTable) { |
||||
super(id, "0.0.0.0", 1, 1); |
||||
this.parent = parent; |
||||
this.tier = tier; |
||||
this.identifier = identifier; |
||||
this.ordinalRank = ordinalRank; |
||||
this.peerTable = peerTable; |
||||
} |
||||
|
||||
int getParent() { |
||||
return parent; |
||||
} |
||||
|
||||
int getTier() { |
||||
return tier; |
||||
} |
||||
|
||||
int getIdentifier() { |
||||
return identifier; |
||||
} |
||||
|
||||
int getOrdinalRank() { |
||||
return ordinalRank; |
||||
} |
||||
|
||||
List<DiscoveryPeer> getPeerTable() { |
||||
return peerTable; |
||||
} |
||||
|
||||
@Override |
||||
public Bytes32 keccak256() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public Endpoint getEndpoint() { |
||||
return null; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return parent + "." + tier + "." + identifier; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,173 @@ |
||||
{ |
||||
"parent": 0, |
||||
"tier": 0, |
||||
"identifier": 0, |
||||
"ordinalRank": 21, |
||||
"id": "0x11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111", |
||||
"peerTable": [ |
||||
{ |
||||
"parent": 0, |
||||
"tier": 1, |
||||
"identifier": 0, |
||||
"ordinalRank": 20, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000000", |
||||
"peerTable": [ |
||||
{ |
||||
"parent": 0, |
||||
"tier": 2, |
||||
"identifier": 0, |
||||
"ordinalRank": 5, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 0, |
||||
"tier": 2, |
||||
"identifier": 1, |
||||
"ordinalRank": 6, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 0, |
||||
"tier": 2, |
||||
"identifier": 2, |
||||
"ordinalRank": 7, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 0, |
||||
"tier": 2, |
||||
"identifier": 3, |
||||
"ordinalRank": 8, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000", |
||||
"peerTable": null |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"parent": 0, |
||||
"tier": 1, |
||||
"identifier": 1, |
||||
"ordinalRank": 2, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010", |
||||
"peerTable": [ |
||||
{ |
||||
"parent": 1, |
||||
"tier": 2, |
||||
"identifier": 0, |
||||
"ordinalRank": 9, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 1, |
||||
"tier": 2, |
||||
"identifier": 1, |
||||
"ordinalRank": 10, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 1, |
||||
"tier": 2, |
||||
"identifier": 2, |
||||
"ordinalRank": 11, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 1, |
||||
"tier": 2, |
||||
"identifier": 3, |
||||
"ordinalRank": 12, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000", |
||||
"peerTable": null |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"parent": 0, |
||||
"tier": 1, |
||||
"identifier": 2, |
||||
"ordinalRank": 3, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100", |
||||
"peerTable": [ |
||||
{ |
||||
"parent": 2, |
||||
"tier": 2, |
||||
"identifier": 0, |
||||
"ordinalRank": 13, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 2, |
||||
"tier": 2, |
||||
"identifier": 1, |
||||
"ordinalRank": 14, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 2, |
||||
"tier": 2, |
||||
"identifier": 2, |
||||
"ordinalRank": 15, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 2, |
||||
"tier": 2, |
||||
"identifier": 3, |
||||
"ordinalRank": 16, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000", |
||||
"peerTable": null |
||||
} |
||||
] |
||||
}, |
||||
{ |
||||
"parent": 0, |
||||
"tier": 1, |
||||
"identifier": 3, |
||||
"ordinalRank": 4, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000", |
||||
"peerTable": [ |
||||
{ |
||||
"parent": 3, |
||||
"tier": 2, |
||||
"identifier": 0, |
||||
"ordinalRank": 17, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010000000000000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 3, |
||||
"tier": 2, |
||||
"identifier": 1, |
||||
"ordinalRank": 18, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000000000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 3, |
||||
"tier": 2, |
||||
"identifier": 2, |
||||
"ordinalRank": 19, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000", |
||||
"peerTable": null |
||||
}, |
||||
{ |
||||
"parent": 3, |
||||
"tier": 2, |
||||
"identifier": 3, |
||||
"ordinalRank": 1, |
||||
"id": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001", |
||||
"peerTable": null |
||||
} |
||||
] |
||||
} |
||||
] |
||||
} |
Loading…
Reference in new issue