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