mirror of https://github.com/hyperledger/besu
Remove DefaultPeerSelector, make EthPeers implement PeerSelector interface, and add PeerTask.getPeerRequirementFilter Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>pull/7628/head
parent
8718102277
commit
e63f4730c6
@ -1,102 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright contributors to Hyperledger Besu. |
|
||||||
* |
|
||||||
* 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. |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: Apache-2.0 |
|
||||||
*/ |
|
||||||
package org.hyperledger.besu.ethereum.eth.manager.peertask; |
|
||||||
|
|
||||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; |
|
||||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; |
|
||||||
import org.hyperledger.besu.ethereum.p2p.peers.PeerId; |
|
||||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; |
|
||||||
|
|
||||||
import java.util.Collection; |
|
||||||
import java.util.Comparator; |
|
||||||
import java.util.Map; |
|
||||||
import java.util.Optional; |
|
||||||
import java.util.concurrent.ConcurrentHashMap; |
|
||||||
import java.util.function.Predicate; |
|
||||||
import java.util.function.Supplier; |
|
||||||
|
|
||||||
import org.slf4j.Logger; |
|
||||||
import org.slf4j.LoggerFactory; |
|
||||||
|
|
||||||
/** |
|
||||||
* This is a simple PeerSelector implementation that can be used the default implementation in most |
|
||||||
* situations |
|
||||||
*/ |
|
||||||
public class DefaultPeerSelector implements PeerSelector { |
|
||||||
private static final Logger LOG = LoggerFactory.getLogger(DefaultPeerSelector.class); |
|
||||||
|
|
||||||
private final Supplier<ProtocolSpec> protocolSpecSupplier; |
|
||||||
private final Map<PeerId, EthPeer> ethPeersByPeerId = new ConcurrentHashMap<>(); |
|
||||||
|
|
||||||
public DefaultPeerSelector(final Supplier<ProtocolSpec> protocolSpecSupplier) { |
|
||||||
this.protocolSpecSupplier = protocolSpecSupplier; |
|
||||||
} |
|
||||||
|
|
||||||
/** |
|
||||||
* Gets the highest reputation peer matching the supplied filter |
|
||||||
* |
|
||||||
* @param filter a filter to match prospective peers with |
|
||||||
* @return the highest reputation peer matching the supplies filter |
|
||||||
* @throws NoAvailablePeerException If there are no suitable peers |
|
||||||
*/ |
|
||||||
private EthPeer getPeer(final Predicate<EthPeer> filter) throws NoAvailablePeerException { |
|
||||||
LOG.trace("Finding peer from pool of {} peers", ethPeersByPeerId.size()); |
|
||||||
return ethPeersByPeerId.values().stream() |
|
||||||
.filter(filter) |
|
||||||
.max(Comparator.naturalOrder()) |
|
||||||
.orElseThrow(NoAvailablePeerException::new); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public EthPeer getPeer( |
|
||||||
final Collection<EthPeer> usedEthPeers, |
|
||||||
final long requiredPeerHeight, |
|
||||||
final SubProtocol requiredSubProtocol) |
|
||||||
throws NoAvailablePeerException { |
|
||||||
return getPeer( |
|
||||||
(candidatePeer) -> |
|
||||||
isPeerUnused(candidatePeer, usedEthPeers) |
|
||||||
&& (protocolSpecSupplier.get().isPoS() |
|
||||||
|| isPeerHeightHighEnough(candidatePeer, requiredPeerHeight)) |
|
||||||
&& isPeerProtocolSuitable(candidatePeer, requiredSubProtocol)); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public Optional<EthPeer> getPeerByPeerId(final PeerId peerId) { |
|
||||||
return Optional.ofNullable(ethPeersByPeerId.get(peerId)); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void addPeer(final EthPeer ethPeer) { |
|
||||||
ethPeersByPeerId.put(ethPeer.getConnection().getPeer(), ethPeer); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public void removePeer(final PeerId peerId) { |
|
||||||
ethPeersByPeerId.remove(peerId); |
|
||||||
} |
|
||||||
|
|
||||||
private boolean isPeerUnused(final EthPeer ethPeer, final Collection<EthPeer> usedEthPeers) { |
|
||||||
return !usedEthPeers.contains(ethPeer); |
|
||||||
} |
|
||||||
|
|
||||||
private boolean isPeerHeightHighEnough(final EthPeer ethPeer, final long requiredHeight) { |
|
||||||
return ethPeer.chainState().getEstimatedHeight() >= requiredHeight; |
|
||||||
} |
|
||||||
|
|
||||||
private boolean isPeerProtocolSuitable(final EthPeer ethPeer, final SubProtocol protocol) { |
|
||||||
return ethPeer.getProtocolName().equals(protocol.getName()); |
|
||||||
} |
|
||||||
} |
|
@ -1,135 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright contributors to Hyperledger Besu. |
|
||||||
* |
|
||||||
* 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. |
|
||||||
* |
|
||||||
* SPDX-License-Identifier: Apache-2.0 |
|
||||||
*/ |
|
||||||
package org.hyperledger.besu.ethereum.eth.manager.peertask; |
|
||||||
|
|
||||||
import org.hyperledger.besu.ethereum.eth.EthProtocol; |
|
||||||
import org.hyperledger.besu.ethereum.eth.SnapProtocol; |
|
||||||
import org.hyperledger.besu.ethereum.eth.manager.ChainState; |
|
||||||
import org.hyperledger.besu.ethereum.eth.manager.EthPeer; |
|
||||||
import org.hyperledger.besu.ethereum.eth.manager.PeerReputation; |
|
||||||
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; |
|
||||||
import org.hyperledger.besu.ethereum.p2p.peers.Peer; |
|
||||||
import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; |
|
||||||
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol; |
|
||||||
|
|
||||||
import java.util.HashSet; |
|
||||||
import java.util.Set; |
|
||||||
|
|
||||||
import org.junit.jupiter.api.Assertions; |
|
||||||
import org.junit.jupiter.api.BeforeEach; |
|
||||||
import org.junit.jupiter.api.Test; |
|
||||||
import org.mockito.Mockito; |
|
||||||
|
|
||||||
public class DefaultPeerSelectorTest { |
|
||||||
|
|
||||||
public DefaultPeerSelector peerSelector; |
|
||||||
|
|
||||||
@BeforeEach |
|
||||||
public void beforeTest() { |
|
||||||
ProtocolSpec protocolSpec = Mockito.mock(ProtocolSpec.class); |
|
||||||
Mockito.when(protocolSpec.isPoS()).thenReturn(false); |
|
||||||
peerSelector = new DefaultPeerSelector(() -> protocolSpec); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testGetPeer() throws NoAvailablePeerException { |
|
||||||
EthPeer expectedPeer = createTestPeer(10, EthProtocol.get(), 5); |
|
||||||
peerSelector.addPeer(expectedPeer); |
|
||||||
EthPeer excludedForLowChainHeightPeer = createTestPeer(5, EthProtocol.get(), 50); |
|
||||||
peerSelector.addPeer(excludedForLowChainHeightPeer); |
|
||||||
EthPeer excludedForWrongProtocolPeer = createTestPeer(10, SnapProtocol.get(), 50); |
|
||||||
peerSelector.addPeer(excludedForWrongProtocolPeer); |
|
||||||
EthPeer excludedForLowReputationPeer = createTestPeer(10, EthProtocol.get(), 1); |
|
||||||
peerSelector.addPeer(excludedForLowReputationPeer); |
|
||||||
EthPeer excludedForBeingAlreadyUsedPeer = createTestPeer(10, EthProtocol.get(), 50); |
|
||||||
peerSelector.addPeer(excludedForBeingAlreadyUsedPeer); |
|
||||||
|
|
||||||
Set<EthPeer> usedEthPeers = new HashSet<>(); |
|
||||||
usedEthPeers.add(excludedForBeingAlreadyUsedPeer); |
|
||||||
|
|
||||||
EthPeer result = peerSelector.getPeer(usedEthPeers, 10, EthProtocol.get()); |
|
||||||
|
|
||||||
Assertions.assertSame(expectedPeer, result); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void testGetPeerButNoPeerMatchesFilter() { |
|
||||||
EthPeer expectedPeer = createTestPeer(10, EthProtocol.get(), 5); |
|
||||||
peerSelector.addPeer(expectedPeer); |
|
||||||
EthPeer excludedForLowChainHeightPeer = createTestPeer(5, EthProtocol.get(), 50); |
|
||||||
peerSelector.addPeer(excludedForLowChainHeightPeer); |
|
||||||
EthPeer excludedForWrongProtocolPeer = createTestPeer(10, SnapProtocol.get(), 50); |
|
||||||
peerSelector.addPeer(excludedForWrongProtocolPeer); |
|
||||||
EthPeer excludedForLowReputationPeer = createTestPeer(10, EthProtocol.get(), 1); |
|
||||||
peerSelector.addPeer(excludedForLowReputationPeer); |
|
||||||
EthPeer excludedForBeingAlreadyUsedPeer = createTestPeer(10, EthProtocol.get(), 50); |
|
||||||
peerSelector.addPeer(excludedForBeingAlreadyUsedPeer); |
|
||||||
|
|
||||||
Set<EthPeer> usedEthPeers = new HashSet<>(); |
|
||||||
usedEthPeers.add(excludedForBeingAlreadyUsedPeer); |
|
||||||
|
|
||||||
Assertions.assertThrows( |
|
||||||
NoAvailablePeerException.class, |
|
||||||
() -> peerSelector.getPeer(usedEthPeers, 10, new MockSubProtocol())); |
|
||||||
} |
|
||||||
|
|
||||||
private EthPeer createTestPeer( |
|
||||||
final long chainHeight, final SubProtocol protocol, final int reputation) { |
|
||||||
EthPeer ethPeer = Mockito.mock(EthPeer.class); |
|
||||||
PeerConnection peerConnection = Mockito.mock(PeerConnection.class); |
|
||||||
Peer peer = Mockito.mock(Peer.class); |
|
||||||
ChainState chainState = Mockito.mock(ChainState.class); |
|
||||||
PeerReputation peerReputation = Mockito.mock(PeerReputation.class); |
|
||||||
|
|
||||||
Mockito.when(ethPeer.getConnection()).thenReturn(peerConnection); |
|
||||||
Mockito.when(peerConnection.getPeer()).thenReturn(peer); |
|
||||||
Mockito.when(ethPeer.getProtocolName()).thenReturn(protocol.getName()); |
|
||||||
Mockito.when(ethPeer.chainState()).thenReturn(chainState); |
|
||||||
Mockito.when(chainState.getEstimatedHeight()).thenReturn(chainHeight); |
|
||||||
Mockito.when(ethPeer.getReputation()).thenReturn(peerReputation); |
|
||||||
Mockito.when(peerReputation.getScore()).thenReturn(reputation); |
|
||||||
|
|
||||||
Mockito.when(ethPeer.compareTo(Mockito.any(EthPeer.class))) |
|
||||||
.thenAnswer( |
|
||||||
(invocationOnMock) -> { |
|
||||||
EthPeer otherPeer = invocationOnMock.getArgument(0, EthPeer.class); |
|
||||||
return Integer.compare(reputation, otherPeer.getReputation().getScore()); |
|
||||||
}); |
|
||||||
return ethPeer; |
|
||||||
} |
|
||||||
|
|
||||||
private static class MockSubProtocol implements SubProtocol { |
|
||||||
|
|
||||||
@Override |
|
||||||
public String getName() { |
|
||||||
return "Mock"; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public int messageSpace(final int protocolVersion) { |
|
||||||
throw new UnsupportedOperationException(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public boolean isValidMessageCode(final int protocolVersion, final int code) { |
|
||||||
throw new UnsupportedOperationException(); |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String messageName(final int protocolVersion, final int code) { |
|
||||||
throw new UnsupportedOperationException(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue