diff --git a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java index 6bb3fb117c1..21408302b30 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java @@ -55,6 +55,9 @@ import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.MergePeerFilter; import org.hyperledger.besu.ethereum.eth.manager.MonitoredExecutors; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.RequestSender; import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager; import org.hyperledger.besu.ethereum.eth.peervalidation.CheckpointBlocksPeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.ClassicForkPeerValidator; @@ -655,6 +658,13 @@ public BesuController build() { final boolean fullSyncDisabled = !SyncMode.isFullSync(syncConfig.getSyncMode()); final SyncState syncState = new SyncState(blockchain, ethPeers, fullSyncDisabled, checkpoint); + final PeerManager peerManager = new PeerManager(); + ethPeers.streamAllPeers().forEach(peerManager::addPeer); + + final PeerTaskExecutor peerTaskExecutor = + new PeerTaskExecutor( + peerManager, new RequestSender(), currentProtocolSpecSupplier, metricsSystem); + if (chainPrunerConfiguration.getChainPruningEnabled()) { final ChainDataPruner chainDataPruner = createChainPruner(blockchainStorage); blockchain.observeBlockAdded(chainDataPruner); @@ -677,7 +687,8 @@ public BesuController build() { besuComponent.map(BesuComponent::getBlobCache).orElse(new BlobCache()), miningParameters); - final List peerValidators = createPeerValidators(protocolSchedule); + final List peerValidators = + createPeerValidators(protocolSchedule, peerTaskExecutor); final EthProtocolManager ethProtocolManager = createEthProtocolManager( @@ -689,6 +700,7 @@ public BesuController build() { ethContext, ethMessages, scheduler, + peerManager, peerValidators, Optional.empty(), forkIdManager); @@ -705,7 +717,8 @@ public BesuController build() { ethContext, syncState, ethProtocolManager, - pivotBlockSelector); + pivotBlockSelector, + peerTaskExecutor); ethPeers.setTrailingPeerRequirementsSupplier(synchronizer::calculateTrailingPeerRequirements); @@ -828,6 +841,7 @@ private TrieLogPruner createTrieLogPruner( * @param syncState the sync state * @param ethProtocolManager the eth protocol manager * @param pivotBlockSelector the pivot block selector + * @param peerTaskExecutor the peer task executor * @return the synchronizer */ protected DefaultSynchronizer createSynchronizer( @@ -837,7 +851,8 @@ protected DefaultSynchronizer createSynchronizer( final EthContext ethContext, final SyncState syncState, final EthProtocolManager ethProtocolManager, - final PivotBlockSelector pivotBlockSelector) { + final PivotBlockSelector pivotBlockSelector, + final PeerTaskExecutor peerTaskExecutor) { return new DefaultSynchronizer( syncConfig, @@ -852,7 +867,8 @@ protected DefaultSynchronizer createSynchronizer( clock, metricsSystem, getFullSyncTerminationCondition(protocolContext.getBlockchain()), - pivotBlockSelector); + pivotBlockSelector, + peerTaskExecutor); } private PivotBlockSelector createPivotSelector( @@ -1019,6 +1035,7 @@ protected String getSupportedProtocol() { * @param ethContext the eth context * @param ethMessages the eth messages * @param scheduler the scheduler + * @param peerManager the peer manager * @param peerValidators the peer validators * @param mergePeerFilter the merge peer filter * @param forkIdManager the fork id manager @@ -1033,6 +1050,7 @@ protected EthProtocolManager createEthProtocolManager( final EthContext ethContext, final EthMessages ethMessages, final EthScheduler scheduler, + final PeerManager peerManager, final List peerValidators, final Optional mergePeerFilter, final ForkIdManager forkIdManager) { @@ -1049,6 +1067,7 @@ protected EthProtocolManager createEthProtocolManager( mergePeerFilter, synchronizerConfiguration, scheduler, + peerManager, forkIdManager); } @@ -1134,27 +1153,32 @@ private ChainDataPruner createChainPruner(final BlockchainStorage blockchainStor * @param protocolSchedule the protocol schedule * @return the list */ - protected List createPeerValidators(final ProtocolSchedule protocolSchedule) { + protected List createPeerValidators( + final ProtocolSchedule protocolSchedule, final PeerTaskExecutor peerTaskExecutor) { final List validators = new ArrayList<>(); final OptionalLong daoBlock = genesisConfigOptions.getDaoForkBlock(); if (daoBlock.isPresent()) { // Setup dao validator validators.add( - new DaoForkPeerValidator(protocolSchedule, metricsSystem, daoBlock.getAsLong())); + new DaoForkPeerValidator(protocolSchedule, peerTaskExecutor, daoBlock.getAsLong())); } final OptionalLong classicBlock = genesisConfigOptions.getClassicForkBlock(); // setup classic validator if (classicBlock.isPresent()) { validators.add( - new ClassicForkPeerValidator(protocolSchedule, metricsSystem, classicBlock.getAsLong())); + new ClassicForkPeerValidator( + protocolSchedule, peerTaskExecutor, classicBlock.getAsLong())); } for (final Map.Entry requiredBlock : requiredBlocks.entrySet()) { validators.add( new RequiredBlocksPeerValidator( - protocolSchedule, metricsSystem, requiredBlock.getKey(), requiredBlock.getValue())); + protocolSchedule, + peerTaskExecutor, + requiredBlock.getKey(), + requiredBlock.getValue())); } final CheckpointConfigOptions checkpointConfigOptions = @@ -1163,7 +1187,7 @@ protected List createPeerValidators(final ProtocolSchedule protoc validators.add( new CheckpointBlocksPeerValidator( protocolSchedule, - metricsSystem, + peerTaskExecutor, checkpointConfigOptions.getNumber().orElseThrow(), checkpointConfigOptions.getHash().map(Hash::fromHexString).orElseThrow())); } diff --git a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java index 0d0f8d2fd87..eeaad2ea857 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/ConsensusScheduleBesuControllerBuilder.java @@ -42,6 +42,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.MergePeerFilter; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -241,6 +242,7 @@ protected EthProtocolManager createEthProtocolManager( final EthContext ethContext, final EthMessages ethMessages, final EthScheduler scheduler, + final PeerManager peerManager, final List peerValidators, final Optional mergePeerFilter, final ForkIdManager forkIdManager) { @@ -255,6 +257,7 @@ protected EthProtocolManager createEthProtocolManager( ethContext, ethMessages, scheduler, + peerManager, peerValidators, mergePeerFilter, forkIdManager); diff --git a/besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java index f5fc75959e1..cafec279a15 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/MergeBesuControllerBuilder.java @@ -34,6 +34,8 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.MergePeerFilter; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.peervalidation.RequiredBlocksPeerValidator; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -97,6 +99,7 @@ protected EthProtocolManager createEthProtocolManager( final EthContext ethContext, final EthMessages ethMessages, final EthScheduler scheduler, + final PeerManager peerManager, final List peerValidators, final Optional mergePeerFilter, final ForkIdManager forkIdManager) { @@ -127,6 +130,7 @@ protected EthProtocolManager createEthProtocolManager( ethContext, ethMessages, scheduler, + peerManager, peerValidators, filterToUse, forkIdManager); @@ -235,15 +239,16 @@ protected PluginServiceFactory createAdditionalPluginServices( } @Override - protected List createPeerValidators(final ProtocolSchedule protocolSchedule) { - List retval = super.createPeerValidators(protocolSchedule); + protected List createPeerValidators( + final ProtocolSchedule protocolSchedule, final PeerTaskExecutor peerTaskExecutor) { + List retval = super.createPeerValidators(protocolSchedule, peerTaskExecutor); final OptionalLong powTerminalBlockNumber = genesisConfigOptions.getTerminalBlockNumber(); final Optional powTerminalBlockHash = genesisConfigOptions.getTerminalBlockHash(); if (powTerminalBlockHash.isPresent() && powTerminalBlockNumber.isPresent()) { retval.add( new RequiredBlocksPeerValidator( protocolSchedule, - metricsSystem, + peerTaskExecutor, powTerminalBlockNumber.getAsLong(), powTerminalBlockHash.get(), 0)); diff --git a/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java index ee2611d9c87..c5258d1bfb3 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java @@ -40,6 +40,8 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.MergePeerFilter; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.sync.DefaultSynchronizer; import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; @@ -161,6 +163,7 @@ protected EthProtocolManager createEthProtocolManager( final EthContext ethContext, final EthMessages ethMessages, final EthScheduler scheduler, + final PeerManager peerManager, final List peerValidators, final Optional mergePeerFilter, final ForkIdManager forkIdManager) { @@ -173,6 +176,7 @@ protected EthProtocolManager createEthProtocolManager( ethContext, ethMessages, scheduler, + peerManager, peerValidators, mergePeerFilter, forkIdManager); @@ -227,7 +231,8 @@ protected DefaultSynchronizer createSynchronizer( final EthContext ethContext, final SyncState syncState, final EthProtocolManager ethProtocolManager, - final PivotBlockSelector pivotBlockSelector) { + final PivotBlockSelector pivotBlockSelector, + final PeerTaskExecutor peerTaskExecutor) { DefaultSynchronizer sync = super.createSynchronizer( @@ -237,7 +242,8 @@ protected DefaultSynchronizer createSynchronizer( ethContext, syncState, ethProtocolManager, - pivotBlockSelector); + pivotBlockSelector, + peerTaskExecutor); if (genesisConfigOptions.getTerminalTotalDifficulty().isPresent()) { LOG.info( diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java index 58318f9611e..beac8a0c045 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.core.Difficulty; import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.StatusMessage; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; @@ -73,6 +74,7 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver { private final EthPeers ethPeers; private final EthMessages ethMessages; private final EthContext ethContext; + private final PeerManager peerManager; private final List supportedCapabilities; private final Blockchain blockchain; private final BlockBroadcaster blockBroadcaster; @@ -92,6 +94,7 @@ public EthProtocolManager( final Optional mergePeerFilter, final SynchronizerConfiguration synchronizerConfiguration, final EthScheduler scheduler, + final PeerManager peerManager, final ForkIdManager forkIdManager) { this.networkId = networkId; this.peerValidators = peerValidators; @@ -100,6 +103,7 @@ public EthProtocolManager( this.mergePeerFilter = mergePeerFilter; this.shutdown = new CountDownLatch(1); this.genesisHash = blockchain.getBlockHashByNumber(0L).orElse(Hash.ZERO); + this.peerManager = peerManager; this.forkIdManager = forkIdManager; @@ -140,7 +144,8 @@ public EthProtocolManager( final List peerValidators, final Optional mergePeerFilter, final SynchronizerConfiguration synchronizerConfiguration, - final EthScheduler scheduler) { + final EthScheduler scheduler, + final PeerManager peerManager) { this( blockchain, networkId, @@ -154,6 +159,7 @@ public EthProtocolManager( mergePeerFilter, synchronizerConfiguration, scheduler, + peerManager, new ForkIdManager( blockchain, Collections.emptyList(), @@ -337,6 +343,7 @@ public void processMessage(final Capability cap, final Message message) { public void handleNewConnection(final PeerConnection connection) { ethPeers.registerNewConnection(connection, peerValidators); final EthPeer peer = ethPeers.peer(connection); + peerManager.addPeer(peer); final Capability cap = connection.capability(getSupportedProtocol()); final ForkId latestForkId = @@ -369,6 +376,7 @@ public void handleDisconnect( final DisconnectReason reason, final boolean initiatedByPeer) { final boolean wasActiveConnection = ethPeers.registerDisconnect(connection); + peerManager.removePeer(connection.getPeer()); LOG.atDebug() .setMessage("Disconnect - active Connection? {} - {} - {} - {} {} - {} peers left") .addArgument(wasActiveConnection) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/NoAvailablePeerException.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/NoAvailablePeerException.java new file mode 100644 index 00000000000..40c600d6fb3 --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/NoAvailablePeerException.java @@ -0,0 +1,17 @@ +/* + * 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; + +public class NoAvailablePeerException extends Exception {} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerManager.java new file mode 100644 index 00000000000..bf334877800 --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerManager.java @@ -0,0 +1,64 @@ +/* + * 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.p2p.peers.PeerId; + +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.function.Predicate; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** "Manages" the EthPeers for the PeerTaskExecutor */ +public class PeerManager { + private static final Logger LOG = LoggerFactory.getLogger(PeerManager.class); + + // use a synchronized map to ensure the map is never modified by multiple threads at once + private final Map ethPeersByPeerId = + Collections.synchronizedMap(new HashMap<>()); + + /** + * Gets the highest reputation peer matching the supplies 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 + */ + public EthPeer getPeer(final Predicate filter) throws NoAvailablePeerException { + LOG.trace("Getting peer from pool of {} peers", ethPeersByPeerId.size()); + return ethPeersByPeerId.values().stream() + .filter(filter) + .max(Comparator.comparing(EthPeer::getReputation)) + .orElseThrow(NoAvailablePeerException::new); + } + + public Optional getPeerByPeerId(final PeerId peerId) { + return Optional.ofNullable(ethPeersByPeerId.get(peerId)); + } + + public void addPeer(final EthPeer ethPeer) { + ethPeersByPeerId.put(ethPeer.getConnection().getPeer(), ethPeer); + } + + public void removePeer(final PeerId peerId) { + ethPeersByPeerId.remove(peerId); + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTask.java new file mode 100644 index 00000000000..bd4991aa7b0 --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTask.java @@ -0,0 +1,64 @@ +/* + * 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.peertask.task.InvalidPeerTaskResponseException; +import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; + +import java.util.Collection; + +/** + * Represents a task to be executed on an EthPeer by the PeerTaskExecutor + * + * @param The type of the result of this PeerTask + */ +public interface PeerTask { + /** + * Returns the SubProtocol used for this PeerTask + * + * @return the SubProtocol used for this PeerTask + */ + String getSubProtocol(); + + /** + * Gets the minimum required block number for a peer to have to successfully execute this task + * + * @return the minimum required block number for a peer to have to successfully execute this task + */ + long getRequiredBlockNumber(); + + /** + * Gets the request data to send to the EthPeer + * + * @return the request data to send to the EthPeer + */ + MessageData getRequestMessage(); + + /** + * Parses the MessageData response from the EthPeer + * + * @param messageData the response MessageData to be parsed + * @return a T built from the response MessageData + * @throws InvalidPeerTaskResponseException if the response messageData is invalid + */ + T parseResponse(MessageData messageData) throws InvalidPeerTaskResponseException; + + /** + * Gets the Collection of behaviors this task is expected to exhibit in the PeetTaskExecutor + * + * @return the Collection of behaviors this task is expected to exhibit in the PeetTaskExecutor + */ + Collection getPeerTaskBehaviors(); +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskBehavior.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskBehavior.java new file mode 100644 index 00000000000..fba9000a741 --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskBehavior.java @@ -0,0 +1,20 @@ +/* + * 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; + +public enum PeerTaskBehavior { + RETRY_WITH_SAME_PEER, + RETRY_WITH_OTHER_PEERS +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutor.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutor.java new file mode 100644 index 00000000000..423ba1f7c8e --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutor.java @@ -0,0 +1,158 @@ +/* + * 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.eth.manager.peertask.task.InvalidPeerTaskResponseException; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; +import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; +import org.hyperledger.besu.metrics.BesuMetricCategory; +import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.plugin.services.metrics.LabelledMetric; +import org.hyperledger.besu.plugin.services.metrics.OperationTimer; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.function.Supplier; + +/** Manages the execution of PeerTasks, respecting their PeerTaskBehavior */ +public class PeerTaskExecutor { + private static final long[] WAIT_TIME_BEFORE_RETRY = {0, 20000, 5000}; + + private final PeerManager peerManager; + private final RequestSender requestSender; + private final Supplier protocolSpecSupplier; + private final LabelledMetric requestTimer; + + public PeerTaskExecutor( + final PeerManager peerManager, + final RequestSender requestSender, + final Supplier protocolSpecSupplier, + final MetricsSystem metricsSystem) { + this.peerManager = peerManager; + this.requestSender = requestSender; + this.protocolSpecSupplier = protocolSpecSupplier; + requestTimer = + metricsSystem.createLabelledTimer( + BesuMetricCategory.PEERS, "Peer Task Executor Request Time", "", "Task Class Name"); + } + + public PeerTaskExecutorResult execute(final PeerTask peerTask) { + PeerTaskExecutorResult executorResult; + int triesRemaining = + peerTask.getPeerTaskBehaviors().contains(PeerTaskBehavior.RETRY_WITH_OTHER_PEERS) ? 3 : 1; + final Collection usedEthPeers = new ArrayList<>(); + do { + EthPeer peer; + try { + peer = + peerManager.getPeer( + (candidatePeer) -> + isPeerUnused(candidatePeer, usedEthPeers) + && (protocolSpecSupplier.get().isPoS() + || isPeerHeightHighEnough( + candidatePeer, peerTask.getRequiredBlockNumber())) + && isPeerProtocolSuitable(candidatePeer, peerTask.getSubProtocol())); + usedEthPeers.add(peer); + executorResult = executeAgainstPeer(peerTask, peer); + } catch (NoAvailablePeerException e) { + executorResult = + new PeerTaskExecutorResult<>(null, PeerTaskExecutorResponseCode.NO_PEER_AVAILABLE); + } + } while (--triesRemaining > 0 + && executorResult.getResponseCode() != PeerTaskExecutorResponseCode.SUCCESS); + + return executorResult; + } + + public CompletableFuture> executeAsync(final PeerTask peerTask) { + return CompletableFuture.supplyAsync(() -> execute(peerTask)); + } + + public PeerTaskExecutorResult executeAgainstPeer( + final PeerTask peerTask, final EthPeer peer) { + MessageData requestMessageData = peerTask.getRequestMessage(); + PeerTaskExecutorResult executorResult; + int triesRemaining = + peerTask.getPeerTaskBehaviors().contains(PeerTaskBehavior.RETRY_WITH_SAME_PEER) ? 3 : 1; + do { + try { + + MessageData responseMessageData; + try (final OperationTimer.TimingContext timingContext = + requestTimer.labels(peerTask.getClass().getSimpleName()).startTimer()) { + responseMessageData = + requestSender.sendRequest(peerTask.getSubProtocol(), requestMessageData, peer); + } + T result = peerTask.parseResponse(responseMessageData); + peer.recordUsefulResponse(); + executorResult = new PeerTaskExecutorResult<>(result, PeerTaskExecutorResponseCode.SUCCESS); + + } catch (PeerConnection.PeerNotConnected e) { + executorResult = + new PeerTaskExecutorResult<>(null, PeerTaskExecutorResponseCode.PEER_DISCONNECTED); + + } catch (InterruptedException | TimeoutException e) { + peer.recordRequestTimeout(requestMessageData.getCode()); + executorResult = new PeerTaskExecutorResult<>(null, PeerTaskExecutorResponseCode.TIMEOUT); + + } catch (InvalidPeerTaskResponseException e) { + peer.recordUselessResponse(e.getMessage()); + executorResult = + new PeerTaskExecutorResult<>(null, PeerTaskExecutorResponseCode.INVALID_RESPONSE); + + } catch (ExecutionException e) { + executorResult = + new PeerTaskExecutorResult<>(null, PeerTaskExecutorResponseCode.INTERNAL_SERVER_ERROR); + } + } while (--triesRemaining > 0 + && executorResult.getResponseCode() != PeerTaskExecutorResponseCode.SUCCESS + && executorResult.getResponseCode() != PeerTaskExecutorResponseCode.PEER_DISCONNECTED + && sleepBetweenRetries(WAIT_TIME_BEFORE_RETRY[triesRemaining])); + + return executorResult; + } + + public CompletableFuture> executeAgainstPeerAsync( + final PeerTask peerTask, final EthPeer peer) { + return CompletableFuture.supplyAsync(() -> executeAgainstPeer(peerTask, peer)); + } + + private boolean sleepBetweenRetries(final long sleepTime) { + try { + Thread.sleep(sleepTime); + return true; + } catch (InterruptedException e) { + return false; + } + } + + private static boolean isPeerUnused( + final EthPeer ethPeer, final Collection usedEthPeers) { + return !usedEthPeers.contains(ethPeer); + } + + private static boolean isPeerHeightHighEnough(final EthPeer ethPeer, final long requiredHeight) { + return ethPeer.chainState().getEstimatedHeight() >= requiredHeight; + } + + private static boolean isPeerProtocolSuitable(final EthPeer ethPeer, final String protocol) { + return ethPeer.getProtocolName().equals(protocol); + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorResponseCode.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorResponseCode.java new file mode 100644 index 00000000000..327461de15a --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorResponseCode.java @@ -0,0 +1,24 @@ +/* + * 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; + +public enum PeerTaskExecutorResponseCode { + SUCCESS, + NO_PEER_AVAILABLE, + PEER_DISCONNECTED, + INTERNAL_SERVER_ERROR, + TIMEOUT, + INVALID_RESPONSE +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorResult.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorResult.java new file mode 100644 index 00000000000..f89bc67f61f --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorResult.java @@ -0,0 +1,35 @@ +/* + * 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 java.util.Optional; + +public class PeerTaskExecutorResult { + private final Optional result; + private final PeerTaskExecutorResponseCode responseCode; + + public PeerTaskExecutorResult(final T result, final PeerTaskExecutorResponseCode responseCode) { + this.result = Optional.ofNullable(result); + this.responseCode = responseCode; + } + + public Optional getResult() { + return result; + } + + public PeerTaskExecutorResponseCode getResponseCode() { + return responseCode; + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/RequestSender.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/RequestSender.java new file mode 100644 index 00000000000..5cf3383823d --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/RequestSender.java @@ -0,0 +1,55 @@ +/* + * 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.eth.manager.RequestManager.ResponseStream; +import org.hyperledger.besu.ethereum.p2p.rlpx.connections.PeerConnection; +import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class RequestSender { + private static final long DEFAULT_TIMEOUT_MS = 20_000; + + private final long timeoutMs; + + public RequestSender() { + this.timeoutMs = DEFAULT_TIMEOUT_MS; + } + + public RequestSender(final long timeoutMs) { + this.timeoutMs = timeoutMs; + } + + public MessageData sendRequest( + final String subProtocol, final MessageData requestMessageData, final EthPeer ethPeer) + throws PeerConnection.PeerNotConnected, + ExecutionException, + InterruptedException, + TimeoutException { + ResponseStream responseStream = + ethPeer.send(requestMessageData, subProtocol, ethPeer.getConnection()); + final CompletableFuture responseMessageDataFuture = new CompletableFuture<>(); + responseStream.then( + (boolean streamClosed, MessageData message, EthPeer peer) -> { + responseMessageDataFuture.complete(message); + }); + return responseMessageDataFuture.get(timeoutMs, TimeUnit.MILLISECONDS); + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/GetHeadersFromPeerByNumberPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/GetHeadersFromPeerByNumberPeerTask.java new file mode 100644 index 00000000000..a2261da0c02 --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/GetHeadersFromPeerByNumberPeerTask.java @@ -0,0 +1,108 @@ +/* + * 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.task; + +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.eth.EthProtocol; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTask; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskBehavior; +import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage; +import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; + +import java.util.Collection; +import java.util.List; + +public class GetHeadersFromPeerByNumberPeerTask implements PeerTask> { + private final long blockNumber; + private final int count; + private final int skip; + private final Direction direction; + private final ProtocolSchedule protocolSchedule; + + public GetHeadersFromPeerByNumberPeerTask( + final long blockNumber, + final int count, + final int skip, + final Direction direction, + final ProtocolSchedule protocolSchedule) { + this.blockNumber = blockNumber; + this.count = count; + this.skip = skip; + this.direction = direction; + this.protocolSchedule = protocolSchedule; + } + + @Override + public String getSubProtocol() { + return EthProtocol.NAME; + } + + @Override + public long getRequiredBlockNumber() { + return direction == Direction.FORWARD ? blockNumber + (long) count * (skip + 1) : blockNumber; + } + + @Override + public MessageData getRequestMessage() { + return GetBlockHeadersMessage.create(blockNumber, count, skip, direction.isReverse()); + } + + @Override + public List parseResponse(final MessageData messageData) + throws InvalidPeerTaskResponseException { + try { + return BlockHeadersMessage.readFrom(messageData).getHeaders(protocolSchedule); + } catch (IllegalArgumentException e) { + throw new InvalidPeerTaskResponseException(e); + } + } + + @Override + public Collection getPeerTaskBehaviors() { + return List.of(PeerTaskBehavior.RETRY_WITH_OTHER_PEERS, PeerTaskBehavior.RETRY_WITH_SAME_PEER); + } + + public enum Direction { + FORWARD(false), + BACKWARD(true); + private final boolean reverse; + + Direction(final boolean reverse) { + this.reverse = reverse; + } + + public boolean isReverse() { + return reverse; + } + } + + public long getBlockNumber() { + return blockNumber; + } + + public int getCount() { + return count; + } + + public int getSkip() { + return skip; + } + + public Direction getDirection() { + return direction; + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/GetReceiptsFromPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/GetReceiptsFromPeerTask.java new file mode 100644 index 00000000000..3f90024c63a --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/GetReceiptsFromPeerTask.java @@ -0,0 +1,102 @@ +/* + * 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.task; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.eth.EthProtocol; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTask; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskBehavior; +import org.hyperledger.besu.ethereum.eth.messages.GetReceiptsMessage; +import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage; +import org.hyperledger.besu.ethereum.mainnet.BodyValidation; +import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GetReceiptsFromPeerTask + implements PeerTask>> { + + private final Collection blockHeaders; + private final Map> headersByReceiptsRoot = new HashMap<>(); + + public GetReceiptsFromPeerTask(final Collection blockHeaders) { + this.blockHeaders = blockHeaders; + blockHeaders.forEach( + header -> + headersByReceiptsRoot + .computeIfAbsent(header.getReceiptsRoot(), key -> new ArrayList<>()) + .add(header)); + } + + @Override + public String getSubProtocol() { + return EthProtocol.NAME; + } + + @Override + public long getRequiredBlockNumber() { + return blockHeaders.stream() + .mapToLong(BlockHeader::getNumber) + .max() + .orElse(BlockHeader.GENESIS_BLOCK_NUMBER); + } + + @Override + public MessageData getRequestMessage() { + // Since we have to match up the data by receipt root, we only need to request receipts + // for one of the headers with each unique receipt root. + final List blockHashes = + headersByReceiptsRoot.values().stream() + .map(headers -> headers.getFirst().getHash()) + .toList(); + return GetReceiptsMessage.create(blockHashes); + } + + @Override + public Map> parseResponse(final MessageData messageData) + throws InvalidPeerTaskResponseException { + if (messageData == null) { + throw new InvalidPeerTaskResponseException(); + } + final ReceiptsMessage receiptsMessage = ReceiptsMessage.readFrom(messageData); + final List> receiptsByBlock = receiptsMessage.receipts(); + if (receiptsByBlock.isEmpty() || receiptsByBlock.size() > blockHeaders.size()) { + throw new InvalidPeerTaskResponseException(); + } + + final Map> receiptsByHeader = new HashMap<>(); + for (final List receiptsInBlock : receiptsByBlock) { + final List blockHeaders = + headersByReceiptsRoot.get(BodyValidation.receiptsRoot(receiptsInBlock)); + if (blockHeaders == null) { + // Contains receipts that we didn't request, so mustn't be the response we're looking for. + throw new InvalidPeerTaskResponseException(); + } + blockHeaders.forEach(header -> receiptsByHeader.put(header, receiptsInBlock)); + } + return receiptsByHeader; + } + + @Override + public Collection getPeerTaskBehaviors() { + return List.of(PeerTaskBehavior.RETRY_WITH_OTHER_PEERS, PeerTaskBehavior.RETRY_WITH_SAME_PEER); + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/InvalidPeerTaskResponseException.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/InvalidPeerTaskResponseException.java new file mode 100644 index 00000000000..271bfc2e35a --- /dev/null +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/InvalidPeerTaskResponseException.java @@ -0,0 +1,26 @@ +/* + * 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.task; + +public class InvalidPeerTaskResponseException extends Exception { + + public InvalidPeerTaskResponseException() { + super(); + } + + public InvalidPeerTaskResponseException(final Throwable cause) { + super(cause); + } +} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetBlockFromPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetBlockFromPeerTask.java index c352fbc3009..518b5726a37 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetBlockFromPeerTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetBlockFromPeerTask.java @@ -30,6 +30,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +// TODO: refactor this to use all PeerTask tasks once GetHeadersFromPeerByNumberPeerTask, +// GetHeadersFromPeerByHashPeerTask, and GetBlockFromPeerTask have all been implemented /** Downloads a block from a peer. Will complete exceptionally if block cannot be downloaded. */ public class GetBlockFromPeerTask extends AbstractPeerTask { private static final Logger LOG = LoggerFactory.getLogger(GetBlockFromPeerTask.class); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetHeadersFromPeerByNumberTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetHeadersFromPeerByNumberTask.java index b4f5aa4f487..39b24f99923 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetHeadersFromPeerByNumberTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetHeadersFromPeerByNumberTask.java @@ -43,27 +43,6 @@ public class GetHeadersFromPeerByNumberTask extends AbstractGetHeadersFromPeerTa this.blockNumber = blockNumber; } - public static AbstractGetHeadersFromPeerTask startingAtNumber( - final ProtocolSchedule protocolSchedule, - final EthContext ethContext, - final long firstBlockNumber, - final int segmentLength, - final MetricsSystem metricsSystem) { - return new GetHeadersFromPeerByNumberTask( - protocolSchedule, ethContext, firstBlockNumber, segmentLength, 0, false, metricsSystem); - } - - public static AbstractGetHeadersFromPeerTask endingAtNumber( - final ProtocolSchedule protocolSchedule, - final EthContext ethContext, - final long lastlockNumber, - final int segmentLength, - final int skip, - final MetricsSystem metricsSystem) { - return new GetHeadersFromPeerByNumberTask( - protocolSchedule, ethContext, lastlockNumber, segmentLength, skip, true, metricsSystem); - } - public static AbstractGetHeadersFromPeerTask forSingleNumber( final ProtocolSchedule protocolSchedule, final EthContext ethContext, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTask.java deleted file mode 100644 index 5b52078b23e..00000000000 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTask.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright 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. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.eth.manager.task; - -import static java.util.Collections.emptyMap; -import static java.util.stream.Collectors.toList; -import static org.hyperledger.besu.ethereum.mainnet.BodyValidation.receiptsRoot; - -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.TransactionReceipt; -import org.hyperledger.besu.ethereum.eth.manager.EthContext; -import org.hyperledger.besu.ethereum.eth.manager.EthPeer; -import org.hyperledger.besu.ethereum.eth.manager.PendingPeerRequest; -import org.hyperledger.besu.ethereum.eth.messages.EthPV63; -import org.hyperledger.besu.ethereum.eth.messages.ReceiptsMessage; -import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData; -import org.hyperledger.besu.plugin.services.MetricsSystem; - -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class GetReceiptsFromPeerTask - extends AbstractPeerRequestTask>> { - private static final Logger LOG = LoggerFactory.getLogger(GetReceiptsFromPeerTask.class); - - private final Collection blockHeaders; - private final Map> headersByReceiptsRoot = new HashMap<>(); - - private GetReceiptsFromPeerTask( - final EthContext ethContext, - final Collection blockHeaders, - final MetricsSystem metricsSystem) { - super(ethContext, EthPV63.GET_RECEIPTS, metricsSystem); - this.blockHeaders = blockHeaders; - blockHeaders.forEach( - header -> - headersByReceiptsRoot - .computeIfAbsent(header.getReceiptsRoot(), key -> new ArrayList<>()) - .add(header)); - } - - public static GetReceiptsFromPeerTask forHeaders( - final EthContext ethContext, - final Collection blockHeaders, - final MetricsSystem metricsSystem) { - - return new GetReceiptsFromPeerTask(ethContext, blockHeaders, metricsSystem); - } - - @Override - protected PendingPeerRequest sendRequest() { - final long maximumRequiredBlockNumber = - blockHeaders.stream() - .mapToLong(BlockHeader::getNumber) - .max() - .orElse(BlockHeader.GENESIS_BLOCK_NUMBER); - - // Since we have to match up the data by receipt root, we only need to request receipts - // for one of the headers with each unique receipt root. - final List blockHashes = - headersByReceiptsRoot.values().stream() - .map(headers -> headers.get(0).getHash()) - .collect(toList()); - return sendRequestToPeer( - peer -> { - LOG.atTrace() - .setMessage("Requesting {} receipts from peer {}") - .addArgument(blockHeaders::size) - .addArgument(peer::getLoggableId) - .log(); - return peer.getReceipts(blockHashes); - }, - maximumRequiredBlockNumber); - } - - @Override - protected Optional>> processResponse( - final boolean streamClosed, final MessageData message, final EthPeer peer) { - if (streamClosed) { - // All outstanding requests have been responded to, and we still haven't found the response - // we wanted. It must have been empty or contain data that didn't match. - peer.recordUselessResponse("receipts"); - return Optional.of(emptyMap()); - } - - final ReceiptsMessage receiptsMessage = ReceiptsMessage.readFrom(message); - final List> receiptsByBlock = receiptsMessage.receipts(); - if (receiptsByBlock.isEmpty()) { - return Optional.empty(); - } else if (receiptsByBlock.size() > blockHeaders.size()) { - return Optional.empty(); - } - - final Map> receiptsByHeader = new HashMap<>(); - for (final List receiptsInBlock : receiptsByBlock) { - final List blockHeaders = - headersByReceiptsRoot.get(receiptsRoot(receiptsInBlock)); - if (blockHeaders == null) { - // Contains receipts that we didn't request, so mustn't be the response we're looking for. - return Optional.empty(); - } - blockHeaders.forEach(header -> receiptsByHeader.put(header, receiptsInBlock)); - } - return Optional.of(receiptsByHeader); - } -} diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java index 9de0a77144b..d7ee289e969 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidator.java @@ -19,10 +19,11 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; -import org.hyperledger.besu.ethereum.eth.manager.task.AbstractPeerTask; -import org.hyperledger.besu.ethereum.eth.manager.task.GetHeadersFromPeerByNumberTask; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetHeadersFromPeerByNumberPeerTask; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.plugin.services.MetricsSystem; import java.time.Duration; import java.util.List; @@ -36,7 +37,7 @@ abstract class AbstractPeerBlockValidator implements PeerValidator { static long DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER = 10L; private final ProtocolSchedule protocolSchedule; - private final MetricsSystem metricsSystem; + private final PeerTaskExecutor peerTaskExecutor; final long blockNumber; // Wait for peer's chainhead to advance some distance beyond blockNumber before validating @@ -44,64 +45,69 @@ abstract class AbstractPeerBlockValidator implements PeerValidator { AbstractPeerBlockValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long blockNumber, final long chainHeightEstimationBuffer) { checkArgument(chainHeightEstimationBuffer >= 0); this.protocolSchedule = protocolSchedule; - this.metricsSystem = metricsSystem; + this.peerTaskExecutor = peerTaskExecutor; this.blockNumber = blockNumber; this.chainHeightEstimationBuffer = chainHeightEstimationBuffer; } protected AbstractPeerBlockValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long blockNumber) { - this(protocolSchedule, metricsSystem, blockNumber, DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); + this(protocolSchedule, peerTaskExecutor, blockNumber, DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); } @Override public CompletableFuture validatePeer( final EthContext ethContext, final EthPeer ethPeer) { - final AbstractPeerTask> getHeaderTask = - GetHeadersFromPeerByNumberTask.forSingleNumber( - protocolSchedule, ethContext, blockNumber, metricsSystem) - .setTimeout(Duration.ofSeconds(20)) - .assignPeer(ethPeer); - return getHeaderTask - .run() - .handle( - (res, err) -> { - if (err != null) { - // Mark peer as invalid on error - LOG.debug( - "Peer {} is invalid because required block ({}) is unavailable: {}", - ethPeer, - blockNumber, - err.toString()); - return false; - } - final List headers = res.getResult(); - if (headers.size() == 0) { - if (blockIsRequired()) { - // If no headers are returned, fail - LOG.debug( - "Peer {} is invalid because required block ({}) is unavailable.", - ethPeer, - blockNumber); - return false; - } else { - LOG.debug( - "Peer {} deemed valid because unavailable block ({}) is not required.", - ethPeer, - blockNumber); - return true; - } - } - final BlockHeader header = headers.get(0); - return validateBlockHeader(ethPeer, header); - }); + final GetHeadersFromPeerByNumberPeerTask getHeadersPeerTask = + new GetHeadersFromPeerByNumberPeerTask( + blockNumber, + 1, + 0, + GetHeadersFromPeerByNumberPeerTask.Direction.FORWARD, + protocolSchedule); + PeerTaskExecutorResult> getHeadersResult = + peerTaskExecutor.executeAgainstPeer(getHeadersPeerTask, ethPeer); + + CompletableFuture isPeerValid = new CompletableFuture<>(); + if (getHeadersResult.getResponseCode() != PeerTaskExecutorResponseCode.SUCCESS + || getHeadersResult.getResult().isEmpty()) { + // Mark peer as invalid on error + LOG.debug( + "Peer {} is invalid because required block ({}) is unavailable: {}", + ethPeer, + blockNumber, + getHeadersResult.getResponseCode()); + isPeerValid.complete(false); + } else { + final List headers = getHeadersResult.getResult().get(); + if (headers.isEmpty()) { + if (blockIsRequired()) { + // If no headers are returned, fail + LOG.debug( + "Peer {} is invalid because required block ({}) is unavailable.", + ethPeer, + blockNumber); + isPeerValid.complete(false); + } else { + LOG.debug( + "Peer {} deemed valid because unavailable block ({}) is not required.", + ethPeer, + blockNumber); + isPeerValid.complete(true); + } + } else { + final BlockHeader header = headers.getFirst(); + isPeerValid.complete(validateBlockHeader(ethPeer, header)); + } + } + return isPeerValid; } abstract boolean validateBlockHeader(EthPeer ethPeer, BlockHeader header); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/CheckpointBlocksPeerValidator.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/CheckpointBlocksPeerValidator.java index a66cc16785d..62d42c1fcad 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/CheckpointBlocksPeerValidator.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/CheckpointBlocksPeerValidator.java @@ -17,27 +17,31 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.plugin.services.MetricsSystem; public class CheckpointBlocksPeerValidator extends RequiredBlocksPeerValidator { public CheckpointBlocksPeerValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long blockNumber, final Hash hash, final long chainHeightEstimationBuffer) { - super(protocolSchedule, metricsSystem, blockNumber, hash, chainHeightEstimationBuffer); + super(protocolSchedule, peerTaskExecutor, blockNumber, hash, chainHeightEstimationBuffer); } public CheckpointBlocksPeerValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long blockNumber, final Hash hash) { this( - protocolSchedule, metricsSystem, blockNumber, hash, DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); + protocolSchedule, + peerTaskExecutor, + blockNumber, + hash, + DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); } @Override diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/ClassicForkPeerValidator.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/ClassicForkPeerValidator.java index 2958ec8e551..ca2c6ea377f 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/ClassicForkPeerValidator.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/ClassicForkPeerValidator.java @@ -16,9 +16,9 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderValidator; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.plugin.services.MetricsSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,17 +28,18 @@ public class ClassicForkPeerValidator extends AbstractPeerBlockValidator { ClassicForkPeerValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long daoBlockNumber, final long chainHeightEstimationBuffer) { - super(protocolSchedule, metricsSystem, daoBlockNumber, chainHeightEstimationBuffer); + super(protocolSchedule, peerTaskExecutor, daoBlockNumber, chainHeightEstimationBuffer); } public ClassicForkPeerValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long daoBlockNumber) { - this(protocolSchedule, metricsSystem, daoBlockNumber, DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); + this( + protocolSchedule, peerTaskExecutor, daoBlockNumber, DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); } @Override diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidator.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidator.java index 01ce6144ee0..e85ec59009c 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidator.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidator.java @@ -16,9 +16,9 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderValidator; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.plugin.services.MetricsSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,17 +28,18 @@ public class DaoForkPeerValidator extends AbstractPeerBlockValidator { DaoForkPeerValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long daoBlockNumber, final long chainHeightEstimationBuffer) { - super(protocolSchedule, metricsSystem, daoBlockNumber, chainHeightEstimationBuffer); + super(protocolSchedule, peerTaskExecutor, daoBlockNumber, chainHeightEstimationBuffer); } public DaoForkPeerValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long daoBlockNumber) { - this(protocolSchedule, metricsSystem, daoBlockNumber, DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); + this( + protocolSchedule, peerTaskExecutor, daoBlockNumber, DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); } @Override diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidator.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidator.java index bf8716c328b..b5cdd25e16a 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidator.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidator.java @@ -17,8 +17,8 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.plugin.services.MetricsSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,21 +30,25 @@ public class RequiredBlocksPeerValidator extends AbstractPeerBlockValidator { public RequiredBlocksPeerValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long blockNumber, final Hash hash, final long chainHeightEstimationBuffer) { - super(protocolSchedule, metricsSystem, blockNumber, chainHeightEstimationBuffer); + super(protocolSchedule, peerTaskExecutor, blockNumber, chainHeightEstimationBuffer); this.hash = hash; } public RequiredBlocksPeerValidator( final ProtocolSchedule protocolSchedule, - final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor, final long blockNumber, final Hash hash) { this( - protocolSchedule, metricsSystem, blockNumber, hash, DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); + protocolSchedule, + peerTaskExecutor, + blockNumber, + hash, + DEFAULT_CHAIN_HEIGHT_ESTIMATION_BUFFER); } @Override diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/AbstractSyncTargetManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/AbstractSyncTargetManager.java index f5b5f978cb3..e77bb7b57c9 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/AbstractSyncTargetManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/AbstractSyncTargetManager.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.manager.task.WaitForPeerTask; import org.hyperledger.besu.ethereum.eth.sync.state.SyncTarget; import org.hyperledger.besu.ethereum.eth.sync.tasks.DetermineCommonAncestorTask; @@ -43,6 +44,7 @@ public abstract class AbstractSyncTargetManager { private final ProtocolSchedule protocolSchedule; private final ProtocolContext protocolContext; private final EthContext ethContext; + private final PeerTaskExecutor peerTaskExecutor; private final MetricsSystem metricsSystem; protected AbstractSyncTargetManager( @@ -50,11 +52,13 @@ protected AbstractSyncTargetManager( final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, final EthContext ethContext, + final PeerTaskExecutor peerTaskExecutor, final MetricsSystem metricsSystem) { this.config = config; this.protocolSchedule = protocolSchedule; this.protocolContext = protocolContext; this.ethContext = ethContext; + this.peerTaskExecutor = peerTaskExecutor; this.metricsSystem = metricsSystem; } @@ -70,7 +74,7 @@ public CompletableFuture findSyncTarget() { return DetermineCommonAncestorTask.create( protocolSchedule, protocolContext, - ethContext, + peerTaskExecutor, bestPeer, config.getDownloaderHeaderRequestSize(), metricsSystem) diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java index b7dc2adb160..cec675d489e 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java @@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.core.Synchronizer; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.checkpointsync.CheckpointDownloaderFactory; import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncDownloader; import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncState; @@ -88,7 +89,8 @@ public DefaultSynchronizer( final Clock clock, final MetricsSystem metricsSystem, final SyncTerminationCondition terminationCondition, - final PivotBlockSelector pivotBlockSelector) { + final PivotBlockSelector pivotBlockSelector, + final PeerTaskExecutor peerTaskExecutor) { this.syncState = syncState; this.pivotBlockSelector = pivotBlockSelector; this.protocolContext = protocolContext; @@ -131,6 +133,7 @@ public DefaultSynchronizer( protocolSchedule, protocolContext, ethContext, + peerTaskExecutor, syncState, metricsSystem, terminationCondition, @@ -150,7 +153,8 @@ public DefaultSynchronizer( worldStateStorageCoordinator, syncState, clock, - syncDurationMetrics); + syncDurationMetrics, + peerTaskExecutor); } else if (syncConfig.getSyncMode() == SyncMode.CHECKPOINT) { this.fastSyncFactory = () -> @@ -166,7 +170,8 @@ public DefaultSynchronizer( worldStateStorageCoordinator, syncState, clock, - syncDurationMetrics); + syncDurationMetrics, + peerTaskExecutor); } else { this.fastSyncFactory = () -> @@ -182,7 +187,8 @@ public DefaultSynchronizer( worldStateStorageCoordinator, syncState, clock, - syncDurationMetrics); + syncDurationMetrics, + peerTaskExecutor); } // create a non-resync fast sync downloader: diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloadBlockStep.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloadBlockStep.java index b4bdf585410..344cb4167f9 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloadBlockStep.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloadBlockStep.java @@ -16,17 +16,22 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetReceiptsFromPeerTask; import org.hyperledger.besu.ethereum.eth.manager.task.AbstractPeerTask.PeerTaskResult; import org.hyperledger.besu.ethereum.eth.manager.task.GetBlockFromPeerTask; -import org.hyperledger.besu.ethereum.eth.manager.task.GetReceiptsFromPeerTask; import org.hyperledger.besu.ethereum.eth.sync.fastsync.checkpoint.Checkpoint; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.plugin.services.MetricsSystem; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -36,16 +41,19 @@ public class CheckpointDownloadBlockStep { private final EthContext ethContext; private final Checkpoint checkpoint; private final MetricsSystem metricsSystem; + private final PeerTaskExecutor peerTaskExecutor; public CheckpointDownloadBlockStep( final ProtocolSchedule protocolSchedule, final EthContext ethContext, final Checkpoint checkpoint, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor) { this.protocolSchedule = protocolSchedule; this.ethContext = ethContext; this.checkpoint = checkpoint; this.metricsSystem = metricsSystem; + this.peerTaskExecutor = peerTaskExecutor; } public CompletableFuture> downloadBlock(final Hash hash) { @@ -65,17 +73,24 @@ public CompletableFuture> downloadBlock(final Hash h private CompletableFuture> downloadReceipts( final PeerTaskResult peerTaskResult) { final Block block = peerTaskResult.getResult(); - final GetReceiptsFromPeerTask getReceiptsFromPeerTask = - GetReceiptsFromPeerTask.forHeaders(ethContext, List.of(block.getHeader()), metricsSystem); - return getReceiptsFromPeerTask - .run() - .thenCompose( - receiptTaskResult -> { - final Optional> transactionReceipts = - Optional.ofNullable(receiptTaskResult.getResult().get(block.getHeader())); - return CompletableFuture.completedFuture( - transactionReceipts.map(receipts -> new BlockWithReceipts(block, receipts))); - }) - .exceptionally(throwable -> Optional.empty()); + GetReceiptsFromPeerTask getReceiptsFromPeerTask = + new GetReceiptsFromPeerTask(List.of(block.getHeader())); + PeerTaskExecutorResult>> getReceiptsFromPeerResult = + peerTaskExecutor.execute(getReceiptsFromPeerTask); + + CompletableFuture> result = new CompletableFuture<>(); + if (getReceiptsFromPeerResult.getResponseCode() == PeerTaskExecutorResponseCode.SUCCESS) { + List transactionReceipts = + getReceiptsFromPeerResult + .getResult() + .map((map) -> map.get(block.getHeader())) + .orElseThrow( + () -> new IllegalStateException("PeerTask response code was success, but empty")); + BlockWithReceipts blockWithReceipts = new BlockWithReceipts(block, transactionReceipts); + result.complete(Optional.of(blockWithReceipts)); + } else { + result.complete(Optional.empty()); + } + return result; } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloaderFactory.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloaderFactory.java index 03df47e4407..886e4c8f31f 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloaderFactory.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloaderFactory.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -64,7 +65,8 @@ public static Optional> createCheckpointDownloader( final WorldStateStorageCoordinator worldStateStorageCoordinator, final SyncState syncState, final Clock clock, - final SyncDurationMetrics syncDurationMetrics) { + final SyncDurationMetrics syncDurationMetrics, + final PeerTaskExecutor peerTaskExecutor) { final Path fastSyncDataDirectory = dataDirectory.resolve(FAST_SYNC_FOLDER); final FastSyncStateStorage fastSyncStateStorage = @@ -112,7 +114,8 @@ public static Optional> createCheckpointDownloader( ethContext, syncState, pivotBlockSelector, - metricsSystem); + metricsSystem, + peerTaskExecutor); } else { if (!syncState.isResyncNeeded()) { LOG.info( @@ -129,7 +132,8 @@ public static Optional> createCheckpointDownloader( ethContext, syncState, pivotBlockSelector, - metricsSystem); + metricsSystem, + peerTaskExecutor); } final SnapSyncProcessState snapSyncState = diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncActions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncActions.java index 5096b74e24f..a9ec567f5db 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncActions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncActions.java @@ -16,6 +16,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -36,7 +37,8 @@ public CheckpointSyncActions( final EthContext ethContext, final SyncState syncState, final PivotBlockSelector pivotBlockSelector, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor) { super( syncConfig, worldStateStorageCoordinator, @@ -45,7 +47,8 @@ public CheckpointSyncActions( ethContext, syncState, pivotBlockSelector, - metricsSystem); + metricsSystem, + peerTaskExecutor); } @Override @@ -60,6 +63,7 @@ public ChainDownloader createChainDownloader( syncState, metricsSystem, currentState, - syncDurationMetrics); + syncDurationMetrics, + peerTaskExecutor); } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncChainDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncChainDownloader.java index 5450b9e5a49..3d7d71b001e 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncChainDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncChainDownloader.java @@ -16,6 +16,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.PipelineChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -39,7 +40,8 @@ public static ChainDownloader create( final SyncState syncState, final MetricsSystem metricsSystem, final FastSyncState fastSyncState, - final SyncDurationMetrics syncDurationMetrics) { + final SyncDurationMetrics syncDurationMetrics, + final PeerTaskExecutor peerTaskExecutor) { final SyncTargetManager syncTargetManager = new SyncTargetManager( @@ -48,6 +50,7 @@ public static ChainDownloader create( protocolSchedule, protocolContext, ethContext, + peerTaskExecutor, metricsSystem, fastSyncState); @@ -55,7 +58,13 @@ public static ChainDownloader create( syncState, syncTargetManager, new CheckpointSyncDownloadPipelineFactory( - config, protocolSchedule, protocolContext, ethContext, fastSyncState, metricsSystem), + config, + protocolSchedule, + protocolContext, + ethContext, + fastSyncState, + metricsSystem, + peerTaskExecutor), ethContext.getScheduler(), metricsSystem, syncDurationMetrics); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncDownloadPipelineFactory.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncDownloadPipelineFactory.java index 45f3f243d8c..d29079a9564 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncDownloadPipelineFactory.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncDownloadPipelineFactory.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncDownloadPipelineFactory; import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncState; @@ -41,8 +42,16 @@ public CheckpointSyncDownloadPipelineFactory( final ProtocolContext protocolContext, final EthContext ethContext, final FastSyncState fastSyncState, - final MetricsSystem metricsSystem) { - super(syncConfig, protocolSchedule, protocolContext, ethContext, fastSyncState, metricsSystem); + final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor) { + super( + syncConfig, + protocolSchedule, + protocolContext, + ethContext, + fastSyncState, + metricsSystem, + peerTaskExecutor); } @Override @@ -76,7 +85,8 @@ protected Pipeline createDownloadCheckPointPipeline( checkPointSource, checkpoint, protocolContext.getBlockchain()); final CheckpointDownloadBlockStep checkPointDownloadBlockStep = - new CheckpointDownloadBlockStep(protocolSchedule, ethContext, checkpoint, metricsSystem); + new CheckpointDownloadBlockStep( + protocolSchedule, ethContext, checkpoint, metricsSystem, peerTaskExecutor); return PipelineBuilder.createPipelineFrom( "fetchCheckpoints", diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStep.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStep.java index cd57de371dd..0de907141d1 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStep.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStep.java @@ -21,9 +21,10 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockWithReceipts; import org.hyperledger.besu.ethereum.core.TransactionReceipt; -import org.hyperledger.besu.ethereum.eth.manager.EthContext; -import org.hyperledger.besu.ethereum.eth.sync.tasks.GetReceiptsForHeadersTask; -import org.hyperledger.besu.plugin.services.MetricsSystem; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetReceiptsFromPeerTask; import org.hyperledger.besu.util.FutureUtils; import java.util.List; @@ -33,22 +34,27 @@ public class DownloadReceiptsStep implements Function, CompletableFuture>> { - private final EthContext ethContext; - private final MetricsSystem metricsSystem; + private final PeerTaskExecutor peerTaskExecutor; - public DownloadReceiptsStep(final EthContext ethContext, final MetricsSystem metricsSystem) { - this.ethContext = ethContext; - this.metricsSystem = metricsSystem; + public DownloadReceiptsStep(final PeerTaskExecutor peerTaskExecutor) { + this.peerTaskExecutor = peerTaskExecutor; } @Override public CompletableFuture> apply(final List blocks) { final List headers = blocks.stream().map(Block::getHeader).collect(toList()); - final CompletableFuture>> getReceipts = - GetReceiptsForHeadersTask.forHeaders(ethContext, headers, metricsSystem).run(); + final CompletableFuture>>> + getReceipts = peerTaskExecutor.executeAsync(new GetReceiptsFromPeerTask(headers)); final CompletableFuture> combineWithBlocks = getReceipts.thenApply( - receiptsByHeader -> combineBlocksAndReceipts(blocks, receiptsByHeader)); + receiptsByHeader -> { + if (receiptsByHeader.getResponseCode() == PeerTaskExecutorResponseCode.SUCCESS + && receiptsByHeader.getResult().isPresent()) { + return combineBlocksAndReceipts(blocks, receiptsByHeader.getResult().get()); + } else { + throw new RuntimeException("Unable to get receipts for blocks"); + } + }); FutureUtils.propagateCancellation(combineWithBlocks, getReceipts); return combineWithBlocks; } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java index 7f6bbae3f31..b4e1955b522 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.manager.task.WaitForPeersTask; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; @@ -51,6 +52,7 @@ public class FastSyncActions { protected final SyncState syncState; protected final PivotBlockSelector pivotBlockSelector; protected final MetricsSystem metricsSystem; + protected final PeerTaskExecutor peerTaskExecutor; protected final Counter pivotBlockSelectionCounter; protected final AtomicLong pivotBlockGauge = new AtomicLong(0); @@ -62,7 +64,8 @@ public FastSyncActions( final EthContext ethContext, final SyncState syncState, final PivotBlockSelector pivotBlockSelector, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor) { this.syncConfig = syncConfig; this.worldStateStorageCoordinator = worldStateStorageCoordinator; this.protocolSchedule = protocolSchedule; @@ -71,6 +74,7 @@ public FastSyncActions( this.syncState = syncState; this.pivotBlockSelector = pivotBlockSelector; this.metricsSystem = metricsSystem; + this.peerTaskExecutor = peerTaskExecutor; pivotBlockSelectionCounter = metricsSystem.createCounter( @@ -167,7 +171,8 @@ public ChainDownloader createChainDownloader( syncState, metricsSystem, currentState, - syncDurationMetrics); + syncDurationMetrics, + peerTaskExecutor); } private CompletableFuture downloadPivotBlockHeader(final Hash hash) { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloader.java index c36ff7cb482..5028b085a0d 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloader.java @@ -16,6 +16,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.PipelineChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -38,7 +39,8 @@ public static ChainDownloader create( final SyncState syncState, final MetricsSystem metricsSystem, final FastSyncState fastSyncState, - final SyncDurationMetrics syncDurationMetrics) { + final SyncDurationMetrics syncDurationMetrics, + final PeerTaskExecutor peerTaskExecutor) { final SyncTargetManager syncTargetManager = new SyncTargetManager( @@ -47,13 +49,20 @@ public static ChainDownloader create( protocolSchedule, protocolContext, ethContext, + peerTaskExecutor, metricsSystem, fastSyncState); return new PipelineChainDownloader( syncState, syncTargetManager, new FastSyncDownloadPipelineFactory( - config, protocolSchedule, protocolContext, ethContext, fastSyncState, metricsSystem), + config, + protocolSchedule, + protocolContext, + ethContext, + fastSyncState, + metricsSystem, + peerTaskExecutor), ethContext.getScheduler(), metricsSystem, syncDurationMetrics); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloadPipelineFactory.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloadPipelineFactory.java index 07e964426cc..6269aafb7b8 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloadPipelineFactory.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloadPipelineFactory.java @@ -26,6 +26,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.DownloadBodiesStep; import org.hyperledger.besu.ethereum.eth.sync.DownloadHeadersStep; import org.hyperledger.besu.ethereum.eth.sync.DownloadPipelineFactory; @@ -62,6 +63,7 @@ public class FastSyncDownloadPipelineFactory implements DownloadPipelineFactory protected final FastSyncValidationPolicy attachedValidationPolicy; protected final FastSyncValidationPolicy detachedValidationPolicy; protected final FastSyncValidationPolicy ommerValidationPolicy; + protected final PeerTaskExecutor peerTaskExecutor; public FastSyncDownloadPipelineFactory( final SynchronizerConfiguration syncConfig, @@ -69,13 +71,15 @@ public FastSyncDownloadPipelineFactory( final ProtocolContext protocolContext, final EthContext ethContext, final FastSyncState fastSyncState, - final MetricsSystem metricsSystem) { + final MetricsSystem metricsSystem, + final PeerTaskExecutor peerTaskExecutor) { this.syncConfig = syncConfig; this.protocolSchedule = protocolSchedule; this.protocolContext = protocolContext; this.ethContext = ethContext; this.fastSyncState = fastSyncState; this.metricsSystem = metricsSystem; + this.peerTaskExecutor = peerTaskExecutor; final LabelledMetric fastSyncValidationCounter = metricsSystem.createLabelledCounter( BesuMetricCategory.SYNCHRONIZER, @@ -139,8 +143,7 @@ public Pipeline createDownloadPipelineForSyncTarget(final SyncT new RangeHeadersValidationStep(protocolSchedule, protocolContext, detachedValidationPolicy); final DownloadBodiesStep downloadBodiesStep = new DownloadBodiesStep(protocolSchedule, ethContext, metricsSystem); - final DownloadReceiptsStep downloadReceiptsStep = - new DownloadReceiptsStep(ethContext, metricsSystem); + final DownloadReceiptsStep downloadReceiptsStep = new DownloadReceiptsStep(peerTaskExecutor); final ImportBlocksStep importBlockStep = new ImportBlocksStep( protocolSchedule, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/SyncTargetManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/SyncTargetManager.java index 2acfa54d04d..210bd127b7f 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/SyncTargetManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/SyncTargetManager.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthPeers; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.AbstractSyncTargetManager; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.tasks.RetryingGetHeaderFromPeerByNumberTask; @@ -62,9 +63,10 @@ public SyncTargetManager( final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, final EthContext ethContext, + final PeerTaskExecutor peerTaskExecutor, final MetricsSystem metricsSystem, final FastSyncState fastSyncState) { - super(config, protocolSchedule, protocolContext, ethContext, metricsSystem); + super(config, protocolSchedule, protocolContext, ethContext, peerTaskExecutor, metricsSystem); this.worldStateStorageCoordinator = worldStateStorageCoordinator; this.protocolSchedule = protocolSchedule; this.protocolContext = protocolContext; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastDownloaderFactory.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastDownloaderFactory.java index 8b71a57885d..e529f0d8bfe 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastDownloaderFactory.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastDownloaderFactory.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -62,7 +63,8 @@ public static Optional> create( final WorldStateStorageCoordinator worldStateStorageCoordinator, final SyncState syncState, final Clock clock, - final SyncDurationMetrics syncDurationMetrics) { + final SyncDurationMetrics syncDurationMetrics, + final PeerTaskExecutor peerTaskExecutor) { final Path fastSyncDataDirectory = dataDirectory.resolve(FAST_SYNC_FOLDER); final FastSyncStateStorage fastSyncStateStorage = @@ -128,7 +130,8 @@ public static Optional> create( ethContext, syncState, pivotBlockSelector, - metricsSystem), + metricsSystem, + peerTaskExecutor), worldStateStorageCoordinator, worldStateDownloader, fastSyncStateStorage, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloader.java index 3a0f6edb086..fb8bd45846c 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloader.java @@ -16,6 +16,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.PipelineChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -32,6 +33,7 @@ public static ChainDownloader create( final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, final EthContext ethContext, + final PeerTaskExecutor peerTaskExecutor, final SyncState syncState, final MetricsSystem metricsSystem, final SyncTerminationCondition terminationCondition, @@ -43,6 +45,7 @@ public static ChainDownloader create( protocolSchedule, protocolContext, ethContext, + peerTaskExecutor, metricsSystem, terminationCondition); diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloader.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloader.java index 8f1aca792c3..0366d295494 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloader.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloader.java @@ -16,6 +16,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.TrailingPeerRequirements; @@ -42,6 +43,7 @@ public FullSyncDownloader( final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, final EthContext ethContext, + final PeerTaskExecutor peerTaskExecutor, final SyncState syncState, final MetricsSystem metricsSystem, final SyncTerminationCondition terminationCondition, @@ -56,6 +58,7 @@ public FullSyncDownloader( protocolSchedule, protocolContext, ethContext, + peerTaskExecutor, syncState, metricsSystem, terminationCondition, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManager.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManager.java index 6bb9e3b7adc..cd2781a2742 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManager.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManager.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.AbstractSyncTargetManager; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.state.SyncTarget; @@ -46,9 +47,10 @@ class FullSyncTargetManager extends AbstractSyncTargetManager { final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, final EthContext ethContext, + final PeerTaskExecutor peerTaskExecutor, final MetricsSystem metricsSystem, final SyncTerminationCondition terminationCondition) { - super(config, protocolSchedule, protocolContext, ethContext, metricsSystem); + super(config, protocolSchedule, protocolContext, ethContext, peerTaskExecutor, metricsSystem); this.protocolContext = protocolContext; this.ethContext = ethContext; this.terminationCondition = terminationCondition; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java index 5de8ceb9843..bae741b2e91 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -60,7 +61,8 @@ public static Optional> createSnapDownloader( final WorldStateStorageCoordinator worldStateStorageCoordinator, final SyncState syncState, final Clock clock, - final SyncDurationMetrics syncDurationMetrics) { + final SyncDurationMetrics syncDurationMetrics, + final PeerTaskExecutor peerTaskExecutor) { final Path fastSyncDataDirectory = dataDirectory.resolve(FAST_SYNC_FOLDER); final FastSyncStateStorage fastSyncStateStorage = @@ -123,7 +125,8 @@ public static Optional> createSnapDownloader( ethContext, syncState, pivotBlockSelector, - metricsSystem), + metricsSystem, + peerTaskExecutor), worldStateStorageCoordinator, snapWorldStateDownloader, fastSyncStateStorage, diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTask.java index 91aa6e5ff8d..6d936662200 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTask.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTask.java @@ -16,11 +16,13 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; +import org.hyperledger.besu.ethereum.eth.manager.exceptions.PeerDisconnectedException; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetHeadersFromPeerByNumberPeerTask; import org.hyperledger.besu.ethereum.eth.manager.task.AbstractEthTask; -import org.hyperledger.besu.ethereum.eth.manager.task.AbstractPeerTask; -import org.hyperledger.besu.ethereum.eth.manager.task.GetHeadersFromPeerByNumberTask; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.util.BlockchainUtil; import org.hyperledger.besu.plugin.services.MetricsSystem; @@ -41,12 +43,11 @@ */ public class DetermineCommonAncestorTask extends AbstractEthTask { private static final Logger LOG = LoggerFactory.getLogger(DetermineCommonAncestorTask.class); - private final EthContext ethContext; private final ProtocolSchedule protocolSchedule; private final ProtocolContext protocolContext; + private final PeerTaskExecutor peerTaskExecutor; private final EthPeer peer; private final int headerRequestSize; - private final MetricsSystem metricsSystem; private long maximumPossibleCommonAncestorNumber; private long minimumPossibleCommonAncestorNumber; @@ -56,17 +57,16 @@ public class DetermineCommonAncestorTask extends AbstractEthTask { private DetermineCommonAncestorTask( final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, - final EthContext ethContext, + final PeerTaskExecutor peerTaskExecutor, final EthPeer peer, final int headerRequestSize, final MetricsSystem metricsSystem) { super(metricsSystem); this.protocolSchedule = protocolSchedule; - this.ethContext = ethContext; this.protocolContext = protocolContext; + this.peerTaskExecutor = peerTaskExecutor; this.peer = peer; this.headerRequestSize = headerRequestSize; - this.metricsSystem = metricsSystem; maximumPossibleCommonAncestorNumber = Math.min( @@ -80,40 +80,34 @@ private DetermineCommonAncestorTask( public static DetermineCommonAncestorTask create( final ProtocolSchedule protocolSchedule, final ProtocolContext protocolContext, - final EthContext ethContext, + final PeerTaskExecutor peerTaskExecutor, final EthPeer peer, final int headerRequestSize, final MetricsSystem metricsSystem) { return new DetermineCommonAncestorTask( - protocolSchedule, protocolContext, ethContext, peer, headerRequestSize, metricsSystem); + protocolSchedule, + protocolContext, + peerTaskExecutor, + peer, + headerRequestSize, + metricsSystem); } @Override protected void executeTask() { - if (maximumPossibleCommonAncestorNumber == minimumPossibleCommonAncestorNumber) { - // Bingo, we found our common ancestor. - result.complete(commonAncestorCandidate); - return; - } - if (maximumPossibleCommonAncestorNumber < BlockHeader.GENESIS_BLOCK_NUMBER - && !result.isDone()) { - result.completeExceptionally(new IllegalStateException("No common ancestor.")); - return; + while (!isTaskComplete()) { + try { + PeerTaskExecutorResult> requestHeadersResult = requestHeaders().get(); + processHeaders(requestHeadersResult); + } catch (Exception e) { + result.completeExceptionally(e); + return; + } } - requestHeaders() - .thenCompose(this::processHeaders) - .whenComplete( - (peerResult, error) -> { - if (error != null) { - result.completeExceptionally(error); - } else if (!result.isDone()) { - executeTaskTimed(); - } - }); } @VisibleForTesting - CompletableFuture>> requestHeaders() { + CompletableFuture>> requestHeaders() { final long range = maximumPossibleCommonAncestorNumber - minimumPossibleCommonAncestorNumber; final int skipInterval = initialQuery ? 0 : calculateSkipInterval(range, headerRequestSize); final int count = @@ -123,17 +117,14 @@ CompletableFuture>> requestHea peer, minimumPossibleCommonAncestorNumber, maximumPossibleCommonAncestorNumber); - return executeSubTask( - () -> - GetHeadersFromPeerByNumberTask.endingAtNumber( - protocolSchedule, - ethContext, - maximumPossibleCommonAncestorNumber, - count, - skipInterval, - metricsSystem) - .assignPeer(peer) - .run()); + GetHeadersFromPeerByNumberPeerTask getHeadersFromPeerByNumberPeerTask = + new GetHeadersFromPeerByNumberPeerTask( + maximumPossibleCommonAncestorNumber, + count, + skipInterval, + GetHeadersFromPeerByNumberPeerTask.Direction.BACKWARD, + protocolSchedule); + return peerTaskExecutor.executeAgainstPeerAsync(getHeadersFromPeerByNumberPeerTask, peer); } /** @@ -151,22 +142,26 @@ static int calculateCount(final double range, final int skipInterval) { return Math.toIntExact((long) Math.ceil(range / (skipInterval + 1)) + 1); } - private CompletableFuture processHeaders( - final AbstractPeerTask.PeerTaskResult> headersResult) { + private void processHeaders(final PeerTaskExecutorResult> headersResult) { initialQuery = false; - final List headers = headersResult.getResult(); - if (headers.isEmpty()) { + if (headersResult.getResponseCode() == PeerTaskExecutorResponseCode.PEER_DISCONNECTED) { + throw new PeerDisconnectedException(peer); + } + + if (headersResult.getResponseCode() != PeerTaskExecutorResponseCode.SUCCESS + || headersResult.getResult().isEmpty()) { // Nothing to do - return CompletableFuture.completedFuture(null); + return; } + final List headers = headersResult.getResult().get(); final OptionalInt maybeAncestorNumber = BlockchainUtil.findHighestKnownBlockIndex(protocolContext.getBlockchain(), headers, false); // Means the insertion point is in the next header request. - if (!maybeAncestorNumber.isPresent()) { - maximumPossibleCommonAncestorNumber = headers.get(headers.size() - 1).getNumber() - 1L; - return CompletableFuture.completedFuture(null); + if (maybeAncestorNumber.isEmpty()) { + maximumPossibleCommonAncestorNumber = headers.getLast().getNumber() - 1L; + return; } final int ancestorNumber = maybeAncestorNumber.getAsInt(); commonAncestorCandidate = headers.get(ancestorNumber); @@ -175,7 +170,19 @@ private CompletableFuture processHeaders( maximumPossibleCommonAncestorNumber = headers.get(ancestorNumber - 1).getNumber() - 1L; } minimumPossibleCommonAncestorNumber = headers.get(ancestorNumber).getNumber(); + } - return CompletableFuture.completedFuture(null); + private boolean isTaskComplete() { + if (maximumPossibleCommonAncestorNumber == minimumPossibleCommonAncestorNumber) { + // Bingo, we found our common ancestor. + result.complete(commonAncestorCandidate); + return true; + } + if (maximumPossibleCommonAncestorNumber < BlockHeader.GENESIS_BLOCK_NUMBER + && !result.isDone()) { + result.completeExceptionally(new IllegalStateException("No common ancestor.")); + return true; + } + return false; } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/tasks/GetReceiptsForHeadersTask.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/tasks/GetReceiptsForHeadersTask.java deleted file mode 100644 index 58c4d3a7afa..00000000000 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/tasks/GetReceiptsForHeadersTask.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 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. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.eth.sync.tasks; - -import static com.google.common.base.Preconditions.checkArgument; -import static java.util.Collections.emptyList; -import static java.util.Collections.emptyMap; - -import org.hyperledger.besu.datatypes.Hash; -import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.TransactionReceipt; -import org.hyperledger.besu.ethereum.eth.manager.EthContext; -import org.hyperledger.besu.ethereum.eth.manager.EthPeer; -import org.hyperledger.besu.ethereum.eth.manager.task.AbstractPeerTask.PeerTaskResult; -import org.hyperledger.besu.ethereum.eth.manager.task.AbstractRetryingPeerTask; -import org.hyperledger.besu.ethereum.eth.manager.task.GetReceiptsFromPeerTask; -import org.hyperledger.besu.plugin.services.MetricsSystem; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.stream.Collectors; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -/** Given a set of headers, repeatedly requests the receipts for those blocks. */ -public class GetReceiptsForHeadersTask - extends AbstractRetryingPeerTask>> { - private static final Logger LOG = LoggerFactory.getLogger(GetReceiptsForHeadersTask.class); - private static final int DEFAULT_RETRIES = 5; - - private final EthContext ethContext; - - private final List headers; - private final Map> receipts; - private final MetricsSystem metricsSystem; - - private GetReceiptsForHeadersTask( - final EthContext ethContext, - final List headers, - final int maxRetries, - final MetricsSystem metricsSystem) { - super(ethContext, maxRetries, Map::isEmpty, metricsSystem); - checkArgument(headers.size() > 0, "Must supply a non-empty headers list"); - this.ethContext = ethContext; - this.metricsSystem = metricsSystem; - - this.headers = headers; - this.receipts = new HashMap<>(); - completeEmptyReceipts(headers); - } - - public static GetReceiptsForHeadersTask forHeaders( - final EthContext ethContext, - final List headers, - final int maxRetries, - final MetricsSystem metricsSystem) { - return new GetReceiptsForHeadersTask(ethContext, headers, maxRetries, metricsSystem); - } - - public static GetReceiptsForHeadersTask forHeaders( - final EthContext ethContext, - final List headers, - final MetricsSystem metricsSystem) { - return new GetReceiptsForHeadersTask(ethContext, headers, DEFAULT_RETRIES, metricsSystem); - } - - private void completeEmptyReceipts(final List headers) { - headers.stream() - .filter(header -> header.getReceiptsRoot().equals(Hash.EMPTY_TRIE_HASH)) - .forEach(header -> receipts.put(header, emptyList())); - } - - @Override - protected CompletableFuture>> executePeerTask( - final Optional assignedPeer) { - return requestReceipts(assignedPeer).thenCompose(this::processResponse); - } - - private CompletableFuture>> requestReceipts( - final Optional assignedPeer) { - final List incompleteHeaders = incompleteHeaders(); - if (incompleteHeaders.isEmpty()) { - return CompletableFuture.completedFuture(emptyMap()); - } - LOG.debug( - "Requesting bodies to complete {} blocks, starting with {}.", - incompleteHeaders.size(), - incompleteHeaders.get(0).getNumber()); - return executeSubTask( - () -> { - final GetReceiptsFromPeerTask task = - GetReceiptsFromPeerTask.forHeaders(ethContext, incompleteHeaders, metricsSystem); - assignedPeer.ifPresent(task::assignPeer); - return task.run().thenApply(PeerTaskResult::getResult); - }); - } - - private CompletableFuture>> processResponse( - final Map> responseData) { - receipts.putAll(responseData); - - if (isComplete()) { - result.complete(receipts); - } - - return CompletableFuture.completedFuture(responseData); - } - - private List incompleteHeaders() { - return headers.stream().filter(h -> receipts.get(h) == null).collect(Collectors.toList()); - } - - private boolean isComplete() { - return headers.stream().allMatch(header -> receipts.get(header) != null); - } -} diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java index 3a3331b568f..524f1123585 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTest.java @@ -44,6 +44,7 @@ import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; import org.hyperledger.besu.ethereum.eth.EthProtocolVersion; import org.hyperledger.besu.ethereum.eth.manager.MockPeerConnection.PeerSendHandler; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; import org.hyperledger.besu.ethereum.eth.messages.BlockBodiesMessage; import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.EthPV62; @@ -1243,6 +1244,7 @@ private EthProtocolManager createEthManager( Optional.empty(), syncConfig, mock(EthScheduler.class), + mock(PeerManager.class), mock(ForkIdManager.class))) { return ethManager; diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java index 0b0bd1e3eb7..e568fb80740 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManagerTestUtil.java @@ -30,6 +30,7 @@ import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.eth.EthProtocol; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; import org.hyperledger.besu.ethereum.eth.manager.snap.SnapProtocolManager; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.sync.ChainHeadTracker; @@ -103,6 +104,7 @@ public static EthProtocolManager create( final EthMessages messages = new EthMessages(); final EthScheduler ethScheduler = new DeterministicEthScheduler(TimeoutPolicy.NEVER_TIMEOUT); final EthContext ethContext = new EthContext(peers, messages, ethScheduler); + final PeerManager peerManager = new PeerManager(); return new EthProtocolManager( blockchain, @@ -117,6 +119,7 @@ public static EthProtocolManager create( mergePeerFilter, mock(SynchronizerConfiguration.class), ethScheduler, + peerManager, new ForkIdManager(blockchain, Collections.emptyList(), Collections.emptyList(), false)); } @@ -128,7 +131,8 @@ public static EthProtocolManager create( final EthProtocolConfiguration ethereumWireProtocolConfiguration, final EthPeers ethPeers, final EthMessages ethMessages, - final EthContext ethContext) { + final EthContext ethContext, + final PeerManager peerManager) { return create( blockchain, ethScheduler, @@ -138,6 +142,7 @@ public static EthProtocolManager create( ethPeers, ethMessages, ethContext, + peerManager, new ForkIdManager(blockchain, Collections.emptyList(), Collections.emptyList(), false)); } @@ -150,6 +155,7 @@ public static EthProtocolManager create( final EthPeers ethPeers, final EthMessages ethMessages, final EthContext ethContext, + final PeerManager peerManager, final ForkIdManager forkIdManager) { ethPeers.setChainHeadTracker(getChainHeadTrackerMock()); @@ -168,6 +174,7 @@ public static EthProtocolManager create( Optional.empty(), mock(SynchronizerConfiguration.class), ethScheduler, + peerManager, forkIdManager); } @@ -237,7 +244,8 @@ public static EthProtocolManager create( configuration, peers, messages, - new EthContext(peers, messages, ethScheduler)); + new EthContext(peers, messages, ethScheduler), + new PeerManager()); } public static ChainHeadTracker getChainHeadTrackerMock() { @@ -285,6 +293,7 @@ public static EthProtocolManager create( peers, messages, new EthContext(peers, messages, ethScheduler), + new PeerManager(), forkIdManager); } @@ -320,7 +329,8 @@ public static EthProtocolManager create( EthProtocolConfiguration.defaultConfig(), ethPeers, messages, - new EthContext(ethPeers, messages, ethScheduler)); + new EthContext(ethPeers, messages, ethScheduler), + new PeerManager()); } public static EthProtocolManager create() { diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java index 6dbea259cc2..b8ab059d45c 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/ethtaskutils/AbstractMessageTaskTest.java @@ -37,6 +37,8 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; @@ -64,6 +66,7 @@ import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; /** * @param The type of data being requested from the network @@ -90,7 +93,9 @@ public abstract class AbstractMessageTaskTest { protected static MetricsSystem metricsSystem = new NoOpMetricsSystem(); protected EthProtocolManager ethProtocolManager; protected EthContext ethContext; + protected PeerTaskExecutor peerTaskExecutor; protected EthPeers ethPeers; + protected PeerManager peerManager; protected TransactionPool transactionPool; protected AtomicBoolean peersDoTimeout; protected AtomicInteger peerCountToTimeout; @@ -133,6 +138,8 @@ public void setupTest() { new DeterministicEthScheduler( () -> peerCountToTimeout.getAndDecrement() > 0 || peersDoTimeout.get()); ethContext = new EthContext(ethPeers, ethMessages, ethScheduler); + peerManager = new PeerManager(); + peerTaskExecutor = Mockito.mock(PeerTaskExecutor.class); final SyncState syncState = new SyncState(blockchain, ethContext.getEthPeers()); transactionPool = TransactionPoolFactory.createTransactionPool( @@ -156,7 +163,8 @@ public void setupTest() { EthProtocolConfiguration.defaultConfig(), ethPeers, ethMessages, - ethContext); + ethContext, + peerManager); } protected abstract T generateDataToBeRequested(); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/GetHeadersFromPeerByNumberTaskTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/GetHeadersFromPeerByNumberTaskTest.java deleted file mode 100644 index 5e0c5dea25b..00000000000 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/GetHeadersFromPeerByNumberTaskTest.java +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 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. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.eth.manager.task; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.collection.IsCollectionWithSize.hasSize; -import static org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain.generateTestBlockHash; - -import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; -import org.hyperledger.besu.ethereum.eth.manager.EthPeer; -import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; -import org.hyperledger.besu.ethereum.eth.manager.MockPeerConnection; -import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; -import org.hyperledger.besu.ethereum.eth.manager.ethtaskutils.PeerMessageTaskTest; -import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage; -import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - -import org.hamcrest.MatcherAssert; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.junit.jupiter.MockitoExtension; - -@ExtendWith(MockitoExtension.class) -public class GetHeadersFromPeerByNumberTaskTest extends PeerMessageTaskTest> { - @Override - protected void assertPartialResultMatchesExpectation( - final List requestedData, final List partialResponse) { - assertThat(partialResponse.size()).isLessThanOrEqualTo(requestedData.size()); - assertThat(partialResponse.size()).isGreaterThan(0); - for (final BlockHeader header : partialResponse) { - assertThat(requestedData).contains(header); - } - } - - @Override - protected List generateDataToBeRequested() { - final int count = 3; - final List requestedHeaders = new ArrayList<>(count); - for (long i = 0; i < count; i++) { - requestedHeaders.add(blockchain.getBlockHeader(5 + i).get()); - } - return requestedHeaders; - } - - @Override - protected EthTask>> createTask( - final List requestedData) { - final BlockHeader firstHeader = requestedData.get(0); - return GetHeadersFromPeerByNumberTask.startingAtNumber( - protocolSchedule, ethContext, firstHeader.getNumber(), requestedData.size(), metricsSystem); - } - - @Test - public void getHeadersFromHashNoSkip() { - getHeadersFromHash(0, false); - } - - @Test - public void getHeadersFromHashNoSkipReversed() { - getHeadersFromHash(0, true); - } - - @Test - public void getHeadersFromHashWithSkip() { - getHeadersFromHash(2, false); - } - - @Test - public void getHeadersFromHashWithSkipReversed() { - getHeadersFromHash(2, true); - } - - private void getHeadersFromHash(final int skip, final boolean reverse) { - // Setup a responsive peer - final RespondingEthPeer.Responder responder = RespondingEthPeer.blockchainResponder(blockchain); - final RespondingEthPeer respondingPeer = - EthProtocolManagerTestUtil.createPeer(ethProtocolManager); - - // Set up parameters and calculated expected response - final long startNumber = reverse ? blockchain.getChainHeadBlockNumber() - 2 : 2; - final int delta = (skip + 1) * (reverse ? -1 : 1); - final int count = 4; - final List expectedHeaders = new ArrayList<>(count); - for (long i = 0; i < count; i++) { - expectedHeaders.add(blockchain.getBlockHeader(startNumber + delta * i).get()); - } - - // Execute task and wait for response - final AbstractGetHeadersFromPeerTask task = - new GetHeadersFromPeerByNumberTask( - protocolSchedule, ethContext, startNumber, count, skip, reverse, metricsSystem); - final AtomicReference>> actualResult = - new AtomicReference<>(); - final AtomicBoolean done = new AtomicBoolean(false); - final CompletableFuture>> future = task.run(); - respondingPeer.respondWhile(responder, () -> !future.isDone()); - future.whenComplete( - (result, error) -> { - actualResult.set(result); - done.compareAndSet(false, true); - }); - - assertThat(done).isTrue(); - assertThat(actualResult.get().getPeer()).isEqualTo(respondingPeer.getEthPeer()); - assertThat(actualResult.get().getResult()).isEqualTo(expectedHeaders); - } - - @Test - public void checkThatSequentialHeadersFormingAChainWorks() { - final BlockHeader block1 = - new BlockHeaderTestFixture().number(1).parentHash(generateTestBlockHash(0)).buildHeader(); - final BlockHeader block2 = - new BlockHeaderTestFixture().number(2).parentHash(block1.getHash()).buildHeader(); - final List headers = Arrays.asList(block1, block2); - - final EthPeer peer = createPeer(); - - final AbstractGetHeadersFromPeerTask task = - new GetHeadersFromPeerByNumberTask( - protocolSchedule, ethContext, block1.getNumber(), 2, 0, false, metricsSystem); - final Optional> optionalBlockHeaders = - task.processResponse(false, BlockHeadersMessage.create(headers), peer); - assertThat(optionalBlockHeaders).isNotNull(); - assertThat(optionalBlockHeaders).isPresent(); - final List blockHeaders = optionalBlockHeaders.get(); - MatcherAssert.assertThat(blockHeaders, hasSize(2)); - assertThat(peer.isDisconnected()).isFalse(); - assertThat(peer.chainState().getEstimatedHeight()).isEqualTo(2); - } - - @Test - public void checkThatSequentialHeadersNotFormingAChainFails() { - - final BlockHeader block1 = - new BlockHeaderTestFixture().number(1).parentHash(generateTestBlockHash(0)).buildHeader(); - final BlockHeader block2 = - new BlockHeaderTestFixture().number(2).parentHash(generateTestBlockHash(1)).buildHeader(); - final List headers = Arrays.asList(block1, block2); - - final EthPeer peer = createPeer(); - - final AbstractGetHeadersFromPeerTask task = - new GetHeadersFromPeerByNumberTask( - protocolSchedule, ethContext, block1.getNumber(), 2, 0, false, metricsSystem); - final Optional> optionalBlockHeaders = - task.processResponse(false, BlockHeadersMessage.create(headers), peer); - assertThat(optionalBlockHeaders).isNotNull(); - assertThat(optionalBlockHeaders).isEmpty(); - assertThat(peer.isDisconnected()).isTrue(); - assertThat(((MockPeerConnection) peer.getConnection()).getDisconnectReason().get()) - .isEqualTo(DisconnectMessage.DisconnectReason.BREACH_OF_PROTOCOL_NON_SEQUENTIAL_HEADERS); - } -} diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTaskTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTaskTest.java deleted file mode 100644 index e037dc6d45b..00000000000 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/task/GetReceiptsFromPeerTaskTest.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 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. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.eth.manager.task; - -import static org.assertj.core.api.Assertions.assertThat; - -import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.TransactionReceipt; -import org.hyperledger.besu.ethereum.eth.manager.ethtaskutils.PeerMessageTaskTest; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class GetReceiptsFromPeerTaskTest - extends PeerMessageTaskTest>> { - - @Override - protected Map> generateDataToBeRequested() { - final Map> expectedData = new HashMap<>(); - for (long i = 0; i < 3; i++) { - final BlockHeader header = blockchain.getBlockHeader(10 + i).get(); - final List transactionReceipts = - blockchain.getTxReceipts(header.getHash()).get(); - expectedData.put(header, transactionReceipts); - } - return expectedData; - } - - @Override - protected EthTask>>> - createTask(final Map> requestedData) { - return GetReceiptsFromPeerTask.forHeaders(ethContext, requestedData.keySet(), metricsSystem); - } - - @Override - protected void assertPartialResultMatchesExpectation( - final Map> requestedData, - final Map> partialResponse) { - - assertThat(partialResponse.size()).isLessThanOrEqualTo(requestedData.size()); - assertThat(partialResponse.size()).isGreaterThan(0); - partialResponse.forEach( - (blockHeader, transactionReceipts) -> { - assertThat(requestedData).containsKey(blockHeader); - assertThat(requestedData.get(blockHeader)).isEqualTo(transactionReceipts); - }); - } -} diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidatorTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidatorTest.java index b7258de5b7e..c6e9730e6cf 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidatorTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/AbstractPeerBlockValidatorTest.java @@ -23,22 +23,35 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetHeadersFromPeerByNumberPeerTask; import org.hyperledger.besu.ethereum.eth.messages.BlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage; import org.hyperledger.besu.testutil.DeterministicEthScheduler; +import java.util.Collections; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; -import java.util.stream.Collectors; -import java.util.stream.Stream; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public abstract class AbstractPeerBlockValidatorTest { - abstract AbstractPeerBlockValidator createValidator(long blockNumber, long buffer); + protected PeerTaskExecutor peerTaskExecutor; + + @BeforeEach + public void beforeEachTest() { + peerTaskExecutor = Mockito.mock(PeerTaskExecutor.class); + } + + abstract AbstractPeerBlockValidator createValidator( + PeerTaskExecutor peerTaskExecutor, long blockNumber, long buffer); @Test public void validatePeer_unresponsivePeer() { @@ -46,11 +59,19 @@ public void validatePeer_unresponsivePeer() { EthProtocolManagerTestUtil.create(DeterministicEthScheduler.TimeoutPolicy.ALWAYS_TIMEOUT); final long blockNumber = 500; - final PeerValidator validator = createValidator(blockNumber, 0); + final PeerValidator validator = createValidator(peerTaskExecutor, blockNumber, 0); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, blockNumber); + Mockito.when( + peerTaskExecutor.executeAgainstPeer( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), + Mockito.eq(peer.getEthPeer()))) + .thenReturn( + new PeerTaskExecutorResult<>( + Collections.emptyList(), PeerTaskExecutorResponseCode.TIMEOUT)); + final CompletableFuture result = validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); @@ -66,31 +87,23 @@ public void validatePeer_requestBlockFromPeerBeingTested() { final long blockNumber = 500; final Block block = gen.block(BlockOptions.create().setBlockNumber(blockNumber)); - final PeerValidator validator = createValidator(blockNumber, 0); + final PeerValidator validator = createValidator(peerTaskExecutor, blockNumber, 0); - final int peerCount = 24; - final List otherPeers = - Stream.generate( - () -> EthProtocolManagerTestUtil.createPeer(ethProtocolManager, blockNumber)) - .limit(peerCount) - .collect(Collectors.toList()); final RespondingEthPeer targetPeer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, blockNumber); + Mockito.when( + peerTaskExecutor.executeAgainstPeer( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), + Mockito.eq(targetPeer.getEthPeer()))) + .thenReturn( + new PeerTaskExecutorResult<>( + List.of(block.getHeader()), PeerTaskExecutorResponseCode.SUCCESS)); + final CompletableFuture result = validator.validatePeer(ethProtocolManager.ethContext(), targetPeer.getEthPeer()); - assertThat(result).isNotDone(); - - // Other peers should not receive request for target block - for (final RespondingEthPeer otherPeer : otherPeers) { - final AtomicBoolean blockRequestedForOtherPeer = respondToBlockRequest(otherPeer, block); - assertThat(blockRequestedForOtherPeer).isFalse(); - } - - // Target peer should receive request for target block - final AtomicBoolean blockRequested = respondToBlockRequest(targetPeer, block); - assertThat(blockRequested).isTrue(); + assertThat(result).isDone(); } @Test @@ -101,7 +114,7 @@ public void canBeValidated() { final long blockNumber = 500; final long buffer = 10; - final PeerValidator validator = createValidator(blockNumber, buffer); + final PeerValidator validator = createValidator(peerTaskExecutor, blockNumber, buffer); final EthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 0).getEthPeer(); peer.chainState().update(gen.hash(), blockNumber - 10); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidatorTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidatorTest.java index 042ca8f9605..c2aef9538da 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidatorTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/DaoForkPeerValidatorTest.java @@ -23,21 +23,27 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetHeadersFromPeerByNumberPeerTask; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderValidator; -import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; import org.apache.tuweni.bytes.Bytes; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class DaoForkPeerValidatorTest extends AbstractPeerBlockValidatorTest { @Override - AbstractPeerBlockValidator createValidator(final long blockNumber, final long buffer) { + AbstractPeerBlockValidator createValidator( + final PeerTaskExecutor peerTaskExecutor, final long blockNumber, final long buffer) { return new DaoForkPeerValidator( - ProtocolScheduleFixture.MAINNET, new NoOpMetricsSystem(), blockNumber, buffer); + ProtocolScheduleFixture.MAINNET, peerTaskExecutor, blockNumber, buffer); } @Test @@ -51,22 +57,22 @@ public void validatePeer_responsivePeerOnRightSideOfFork() { .setBlockNumber(daoBlockNumber) .setExtraData(MainnetBlockHeaderValidator.DAO_EXTRA_DATA)); - final PeerValidator validator = - new DaoForkPeerValidator( - ProtocolScheduleFixture.MAINNET, new NoOpMetricsSystem(), daoBlockNumber, 0); + final PeerValidator validator = createValidator(peerTaskExecutor, daoBlockNumber, 0); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, daoBlockNumber); + Mockito.when( + peerTaskExecutor.executeAgainstPeer( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), + Mockito.eq(peer.getEthPeer()))) + .thenReturn( + new PeerTaskExecutorResult<>( + List.of(daoBlock.getHeader()), PeerTaskExecutorResponseCode.SUCCESS)); + final CompletableFuture result = validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); - assertThat(result).isNotDone(); - - // Send response for dao block - final AtomicBoolean daoBlockRequested = respondToBlockRequest(peer, daoBlock); - - assertThat(daoBlockRequested).isTrue(); assertThat(result).isDone(); assertThat(result).isCompletedWithValue(true); } @@ -79,22 +85,22 @@ public void validatePeer_responsivePeerOnWrongSideOfFork() { final Block daoBlock = gen.block(BlockOptions.create().setBlockNumber(daoBlockNumber).setExtraData(Bytes.EMPTY)); - final PeerValidator validator = - new DaoForkPeerValidator( - ProtocolScheduleFixture.MAINNET, new NoOpMetricsSystem(), daoBlockNumber, 0); + final PeerValidator validator = createValidator(peerTaskExecutor, daoBlockNumber, 0); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, daoBlockNumber); + Mockito.when( + peerTaskExecutor.executeAgainstPeer( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), + Mockito.eq(peer.getEthPeer()))) + .thenReturn( + new PeerTaskExecutorResult<>( + List.of(daoBlock.getHeader()), PeerTaskExecutorResponseCode.SUCCESS)); + final CompletableFuture result = validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); - assertThat(result).isNotDone(); - - // Send response for dao block - final AtomicBoolean daoBlockRequested = respondToBlockRequest(peer, daoBlock); - - assertThat(daoBlockRequested).isTrue(); assertThat(result).isDone(); assertThat(result).isCompletedWithValue(false); } @@ -106,19 +112,22 @@ public void validatePeer_responsivePeerDoesNotHaveBlockWhenPastForkHeight() { final PeerValidator validator = new DaoForkPeerValidator( - ProtocolScheduleFixture.MAINNET, new NoOpMetricsSystem(), daoBlockNumber, 0); + ProtocolScheduleFixture.MAINNET, peerTaskExecutor, daoBlockNumber, 0); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, daoBlockNumber); + Mockito.when( + peerTaskExecutor.executeAgainstPeer( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), + Mockito.eq(peer.getEthPeer()))) + .thenReturn( + new PeerTaskExecutorResult<>( + Collections.emptyList(), PeerTaskExecutorResponseCode.SUCCESS)); + final CompletableFuture result = validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); - assertThat(result).isNotDone(); - - // Respond to block header request with empty - peer.respond(RespondingEthPeer.emptyResponder()); - assertThat(result).isDone(); assertThat(result).isCompletedWithValue(true); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidatorTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidatorTest.java index 4fdb679880b..f483e47d422 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidatorTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/peervalidation/RequiredBlocksPeerValidatorTest.java @@ -24,19 +24,25 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; -import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetHeadersFromPeerByNumberPeerTask; +import java.util.Collections; +import java.util.List; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; public class RequiredBlocksPeerValidatorTest extends AbstractPeerBlockValidatorTest { @Override - AbstractPeerBlockValidator createValidator(final long blockNumber, final long buffer) { + AbstractPeerBlockValidator createValidator( + final PeerTaskExecutor peerTaskExecutor, final long blockNumber, final long buffer) { return new RequiredBlocksPeerValidator( - ProtocolScheduleFixture.MAINNET, new NoOpMetricsSystem(), blockNumber, Hash.ZERO, buffer); + ProtocolScheduleFixture.MAINNET, peerTaskExecutor, blockNumber, Hash.ZERO, buffer); } @Test @@ -50,7 +56,7 @@ public void validatePeer_responsivePeerWithRequiredBlock() { final PeerValidator validator = new RequiredBlocksPeerValidator( ProtocolScheduleFixture.MAINNET, - new NoOpMetricsSystem(), + peerTaskExecutor, requiredBlockNumber, requiredBlock.getHash(), 0); @@ -58,15 +64,17 @@ public void validatePeer_responsivePeerWithRequiredBlock() { final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, requiredBlockNumber); + Mockito.when( + peerTaskExecutor.executeAgainstPeer( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), + Mockito.eq(peer.getEthPeer()))) + .thenReturn( + new PeerTaskExecutorResult<>( + List.of(requiredBlock.getHeader()), PeerTaskExecutorResponseCode.SUCCESS)); + final CompletableFuture result = validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); - assertThat(result).isNotDone(); - - // Send response for block - final AtomicBoolean requiredBlockRequested = respondToBlockRequest(peer, requiredBlock); - - assertThat(requiredBlockRequested).isTrue(); assertThat(result).isDone(); assertThat(result).isCompletedWithValue(true); } @@ -81,24 +89,22 @@ public void validatePeer_responsivePeerWithBadRequiredBlock() { final PeerValidator validator = new RequiredBlocksPeerValidator( - ProtocolScheduleFixture.MAINNET, - new NoOpMetricsSystem(), - requiredBlockNumber, - Hash.ZERO, - 0); + ProtocolScheduleFixture.MAINNET, peerTaskExecutor, requiredBlockNumber, Hash.ZERO, 0); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, requiredBlockNumber); + Mockito.when( + peerTaskExecutor.executeAgainstPeer( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), + Mockito.eq(peer.getEthPeer()))) + .thenReturn( + new PeerTaskExecutorResult<>( + List.of(requiredBlock.getHeader()), PeerTaskExecutorResponseCode.SUCCESS)); + final CompletableFuture result = validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); - assertThat(result).isNotDone(); - - // Send response for required block - final AtomicBoolean requiredBlockRequested = respondToBlockRequest(peer, requiredBlock); - - assertThat(requiredBlockRequested).isTrue(); assertThat(result).isDone(); assertThat(result).isCompletedWithValue(false); } @@ -109,18 +115,21 @@ public void validatePeer_responsivePeerDoesNotHaveBlockWhenPastForkHeight() { final PeerValidator validator = new RequiredBlocksPeerValidator( - ProtocolScheduleFixture.MAINNET, new NoOpMetricsSystem(), 1, Hash.ZERO); + ProtocolScheduleFixture.MAINNET, peerTaskExecutor, 1, Hash.ZERO); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1); + Mockito.when( + peerTaskExecutor.executeAgainstPeer( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), + Mockito.eq(peer.getEthPeer()))) + .thenReturn( + new PeerTaskExecutorResult<>( + Collections.emptyList(), PeerTaskExecutorResponseCode.SUCCESS)); + final CompletableFuture result = validator.validatePeer(ethProtocolManager.ethContext(), peer.getEthPeer()); - assertThat(result).isNotDone(); - - // Respond to block header request with empty - peer.respond(RespondingEthPeer.emptyResponder()); - assertThat(result).isDone(); assertThat(result).isCompletedWithValue(false); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckPointSyncChainDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckPointSyncChainDownloaderTest.java index 43f03100a75..1d25191ec7e 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckPointSyncChainDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckPointSyncChainDownloaderTest.java @@ -22,13 +22,19 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.Blockchain; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; +import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; import org.hyperledger.besu.ethereum.core.Difficulty; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetReceiptsFromPeerTask; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.fastsync.FastSyncState; @@ -44,6 +50,11 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.stream.Stream; @@ -55,6 +66,9 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; +import org.junit.platform.commons.util.ReflectionUtils; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; public class CheckPointSyncChainDownloaderTest { @@ -63,6 +77,7 @@ public class CheckPointSyncChainDownloaderTest { protected EthContext ethContext; protected ProtocolContext protocolContext; private SyncState syncState; + private PeerTaskExecutor peerTaskExecutor; protected MutableBlockchain localBlockchain; private BlockchainSetupUtil otherBlockchainSetup; @@ -100,6 +115,7 @@ public void setup(final DataStorageFormat dataStorageFormat) { localBlockchain = localBlockchainSetup.getBlockchain(); otherBlockchainSetup = BlockchainSetupUtil.forTesting(dataStorageFormat); otherBlockchain = otherBlockchainSetup.getBlockchain(); + otherBlockchainSetup.importFirstBlocks(30); protocolSchedule = localBlockchainSetup.getProtocolSchedule(); protocolContext = localBlockchainSetup.getProtocolContext(); ethProtocolManager = @@ -123,6 +139,57 @@ public void setup(final DataStorageFormat dataStorageFormat) { ethContext.getEthPeers(), true, Optional.of(checkpoint)); + + peerTaskExecutor = mock(PeerTaskExecutor.class); + + when(peerTaskExecutor.execute(any(GetReceiptsFromPeerTask.class))) + .thenAnswer( + new Answer>>>() { + @Override + public PeerTaskExecutorResult>> answer( + final InvocationOnMock invocationOnMock) throws Throwable { + GetReceiptsFromPeerTask task = + invocationOnMock.getArgument(0, GetReceiptsFromPeerTask.class); + + return processTask(task); + } + }); + + when(peerTaskExecutor.executeAsync(any(GetReceiptsFromPeerTask.class))) + .thenAnswer( + new Answer< + CompletableFuture< + PeerTaskExecutorResult>>>>() { + @Override + public CompletableFuture< + PeerTaskExecutorResult>>> + answer(final InvocationOnMock invocationOnMock) throws Throwable { + GetReceiptsFromPeerTask task = + invocationOnMock.getArgument(0, GetReceiptsFromPeerTask.class); + + return CompletableFuture.completedFuture(processTask(task)); + } + }); + } + + @SuppressWarnings("unchecked") + private PeerTaskExecutorResult>> processTask( + final GetReceiptsFromPeerTask task) throws IllegalAccessException { + Map> getReceiptsFromPeerTaskResult = new HashMap<>(); + List fields = + ReflectionUtils.findFields( + task.getClass(), + (field) -> field.getName().equals("blockHeaders"), + ReflectionUtils.HierarchyTraversalMode.TOP_DOWN); + fields.forEach((f) -> f.setAccessible(true)); + Collection blockHeaders = (Collection) fields.getFirst().get(task); + blockHeaders.forEach( + (bh) -> + getReceiptsFromPeerTaskResult.put( + bh, otherBlockchain.getTxReceipts(bh.getHash()).get())); + + return new PeerTaskExecutorResult<>( + getReceiptsFromPeerTaskResult, PeerTaskExecutorResponseCode.SUCCESS); } @AfterEach @@ -143,14 +210,14 @@ private ChainDownloader downloader( syncState, new NoOpMetricsSystem(), new FastSyncState(otherBlockchain.getBlockHeader(pivotBlockNumber).get()), - SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS); + SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS, + peerTaskExecutor); } @ParameterizedTest @ArgumentsSource(CheckPointSyncChainDownloaderTestArguments.class) public void shouldSyncToPivotBlockInMultipleSegments(final DataStorageFormat storageFormat) { setup(storageFormat); - otherBlockchainSetup.importFirstBlocks(30); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain); @@ -162,6 +229,7 @@ public void shouldSyncToPivotBlockInMultipleSegments(final DataStorageFormat sto .downloaderChainSegmentSize(5) .downloaderHeadersRequestSize(3) .build(); + final long pivotBlockNumber = 25; ethContext .getEthPeers() @@ -186,7 +254,6 @@ public void shouldSyncToPivotBlockInMultipleSegments(final DataStorageFormat sto @ArgumentsSource(CheckPointSyncChainDownloaderTestArguments.class) public void shouldSyncToPivotBlockInSingleSegment(final DataStorageFormat storageFormat) { setup(storageFormat); - otherBlockchainSetup.importFirstBlocks(30); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStepTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStepTest.java index c9cfeda1191..6f25be84a8a 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStepTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStepTest.java @@ -16,88 +16,74 @@ import static java.util.Arrays.asList; import static org.assertj.core.api.Assertions.assertThat; -import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; -import org.hyperledger.besu.ethereum.ProtocolContext; -import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockWithReceipts; -import org.hyperledger.besu.ethereum.core.BlockchainSetupUtil; -import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.core.TransactionReceipt; -import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; -import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; -import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; -import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; -import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; -import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; -import org.hyperledger.besu.plugin.services.storage.DataStorageFormat; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetReceiptsFromPeerTask; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; public class DownloadReceiptsStepTest { - - private static ProtocolContext protocolContext; - private static MutableBlockchain blockchain; - - private EthProtocolManager ethProtocolManager; + @Mock private PeerTaskExecutor peerTaskExecutor; + private AutoCloseable mockitoCloseable; private DownloadReceiptsStep downloadReceiptsStep; - @BeforeAll - public static void setUpClass() { - final BlockchainSetupUtil setupUtil = BlockchainSetupUtil.forTesting(DataStorageFormat.FOREST); - setupUtil.importFirstBlocks(20); - protocolContext = setupUtil.getProtocolContext(); - blockchain = setupUtil.getBlockchain(); - } - @BeforeEach public void setUp() { - TransactionPool transactionPool = mock(TransactionPool.class); - ethProtocolManager = - EthProtocolManagerTestUtil.create( - ProtocolScheduleFixture.MAINNET, - blockchain, - () -> false, - protocolContext.getWorldStateArchive(), - transactionPool, - EthProtocolConfiguration.defaultConfig()); - downloadReceiptsStep = - new DownloadReceiptsStep(ethProtocolManager.ethContext(), new NoOpMetricsSystem()); + mockitoCloseable = MockitoAnnotations.openMocks(this); + downloadReceiptsStep = new DownloadReceiptsStep(peerTaskExecutor); } - @Test - public void shouldDownloadReceiptsForBlocks() { - final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); + @AfterEach + public void afterEachTest() throws Exception { + mockitoCloseable.close(); + } - final List blocks = asList(block(1), block(2), block(3), block(4)); - final CompletableFuture> result = downloadReceiptsStep.apply(blocks); + @Test + public void shouldDownloadReceiptsForBlocks() throws ExecutionException, InterruptedException { + final List blocks = asList(block(), block(), block(), block()); - peer.respond(RespondingEthPeer.blockchainResponder(blockchain)); + Map> receiptsMap = new HashMap<>(); + blocks.forEach( + (b) -> receiptsMap.put(b.getHeader(), List.of(Mockito.mock(TransactionReceipt.class)))); + PeerTaskExecutorResult>> peerTaskResult = + new PeerTaskExecutorResult<>(receiptsMap, PeerTaskExecutorResponseCode.SUCCESS); + Mockito.when(peerTaskExecutor.executeAsync(Mockito.any(GetReceiptsFromPeerTask.class))) + .thenReturn(CompletableFuture.completedFuture(peerTaskResult)); - assertThat(result) - .isCompletedWithValue( - asList( - blockWithReceipts(1), - blockWithReceipts(2), - blockWithReceipts(3), - blockWithReceipts(4))); - } + final CompletableFuture> result = downloadReceiptsStep.apply(blocks); - private Block block(final long number) { - final BlockHeader header = blockchain.getBlockHeader(number).get(); - return new Block(header, blockchain.getBlockBody(header.getHash()).get()); + assertThat(result.get().get(0).getBlock()).isEqualTo(blocks.get(0)); + assertThat(result.get().get(0).getReceipts().size()).isEqualTo(1); + assertThat(result.get().get(1).getBlock()).isEqualTo(blocks.get(1)); + assertThat(result.get().get(1).getReceipts().size()).isEqualTo(1); + assertThat(result.get().get(2).getBlock()).isEqualTo(blocks.get(2)); + assertThat(result.get().get(2).getReceipts().size()).isEqualTo(1); + assertThat(result.get().get(3).getBlock()).isEqualTo(blocks.get(3)); + assertThat(result.get().get(3).getReceipts().size()).isEqualTo(1); } - private BlockWithReceipts blockWithReceipts(final long number) { - final Block block = block(number); - final List receipts = blockchain.getTxReceipts(block.getHash()).get(); - return new BlockWithReceipts(block, receipts); + private Block block() { + final Block block = Mockito.mock(Block.class); + final BlockHeader blockHeader = Mockito.mock(BlockHeader.class); + when(block.getHeader()).thenAnswer((invocationOnMock) -> blockHeader); + return block; } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastDownloaderFactoryTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastDownloaderFactoryTest.java index 37ca5be2e99..851721ca1fb 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastDownloaderFactoryTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastDownloaderFactoryTest.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.MutableBlockchain; import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -75,6 +76,7 @@ public class FastDownloaderFactoryTest { @Mock private Clock clock; @Mock private Path dataDirectory; @Mock private PivotBlockSelector pivotBlockSelector; + @Mock private PeerTaskExecutor peerTaskExecutor; private WorldStateKeyValueStorage worldStateKeyValueStorage; private WorldStateStorageCoordinator worldStateStorageCoordinator; @@ -117,7 +119,8 @@ public void shouldThrowIfSyncModeChangedWhileFastSyncIncomplete( worldStateStorageCoordinator, syncState, clock, - SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS)) + SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS, + peerTaskExecutor)) .isInstanceOf(IllegalStateException.class); } @@ -142,7 +145,8 @@ public void shouldNotThrowIfSyncModeChangedWhileFastSyncComplete( worldStateStorageCoordinator, syncState, clock, - SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS); + SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS, + peerTaskExecutor); assertThat(result).isEmpty(); } @@ -170,7 +174,8 @@ public void shouldNotThrowWhenFastSyncModeRequested(final DataStorageFormat data worldStateStorageCoordinator, syncState, clock, - SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS); + SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS, + peerTaskExecutor); verify(mutableBlockchain).getChainHeadBlockNumber(); } @@ -205,7 +210,8 @@ public void shouldClearWorldStateDuringFastSyncWhenStateQueDirectoryExists( worldStateStorageCoordinator, syncState, clock, - SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS); + SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS, + peerTaskExecutor); verify(worldStateKeyValueStorage).clear(); assertThat(Files.exists(stateQueueDir)).isFalse(); @@ -242,7 +248,8 @@ public void shouldCrashWhenStateQueueIsNotDirectory(final DataStorageFormat data worldStateStorageCoordinator, syncState, clock, - SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS)) + SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS, + peerTaskExecutor)) .isInstanceOf(IllegalStateException.class); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActionsTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActionsTest.java index 68caf2182c0..9093c333885 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActionsTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActionsTest.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.peervalidation.PeerValidator; import org.hyperledger.besu.ethereum.eth.sync.PivotBlockSelector; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; @@ -77,6 +78,7 @@ public class FastSyncActionsTest { private BlockchainSetupUtil blockchainSetupUtil; private SyncState syncState; private MetricsSystem metricsSystem; + private PeerTaskExecutor peerTaskExecutor; static class FastSyncActionsTestArguments implements ArgumentsProvider { @Override @@ -103,10 +105,12 @@ public void setUp(final DataStorageFormat storageFormat) { ethPeers = ethContext.getEthPeers(); syncState = new SyncState(blockchain, ethPeers); metricsSystem = new NoOpMetricsSystem(); + peerTaskExecutor = new PeerTaskExecutor(null, null, null, new NoOpMetricsSystem()); fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); } @ParameterizedTest @@ -159,7 +163,8 @@ public void selectPivotBlockShouldSelectBlockPivotDistanceFromBestPeer( fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 5000); @@ -180,7 +185,8 @@ public void selectPivotBlockShouldConsiderTotalDifficultyWhenSelectingBestPeer( fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(1000), 5500); EthProtocolManagerTestUtil.createPeer(ethProtocolManager, Difficulty.of(2000), 4000); @@ -203,7 +209,8 @@ public void selectPivotBlockShouldWaitAndRetryUntilMinHeightEstimatesAreAvailabl fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); final CompletableFuture result = fastSyncActions.selectPivotBlock(FastSyncState.EMPTY_SYNC_STATE); @@ -234,7 +241,8 @@ public void selectPivotBlockShouldWaitAndRetryIfSufficientChainHeightEstimatesAr fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); final long minPivotHeight = syncConfig.getSyncPivotDistance() + 1L; EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager); @@ -285,7 +293,8 @@ public void selectPivotBlockShouldWaitAndRetryIfSufficientValidatedPeersUnavaila fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); final long minPivotHeight = syncConfig.getSyncPivotDistance() + 1L; EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager); @@ -357,7 +366,8 @@ private void selectPivotBlockUsesBestPeerMatchingRequiredCriteria( fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); final long minPivotHeight = syncConfig.getSyncPivotDistance() + 1L; EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager); @@ -409,7 +419,8 @@ public void selectPivotBlockShouldWaitAndRetryIfBestPeerChainIsShorterThanPivotD fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); final long pivotDistance = syncConfig.getSyncPivotDistance(); EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager); @@ -472,7 +483,8 @@ public void downloadPivotBlockHeaderShouldRetrievePivotBlockHeader( fastSyncActions = createFastSyncActions( syncConfig, - new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem)); + new PivotSelectorFromPeers(ethContext, syncConfig, syncState, metricsSystem), + peerTaskExecutor); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1001); final CompletableFuture result = @@ -511,7 +523,8 @@ public void downloadPivotBlockHeaderShouldRetrievePivotBlockHash( metricsSystem, genesisConfig, () -> finalizedEvent, - () -> {})); + () -> {}), + peerTaskExecutor); final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1001); final CompletableFuture result = @@ -526,7 +539,9 @@ public void downloadPivotBlockHeaderShouldRetrievePivotBlockHash( } private FastSyncActions createFastSyncActions( - final SynchronizerConfiguration syncConfig, final PivotBlockSelector pivotBlockSelector) { + final SynchronizerConfiguration syncConfig, + final PivotBlockSelector pivotBlockSelector, + final PeerTaskExecutor peerTaskExecutor) { final ProtocolSchedule protocolSchedule = blockchainSetupUtil.getProtocolSchedule(); final ProtocolContext protocolContext = blockchainSetupUtil.getProtocolContext(); final EthContext ethContext = ethProtocolManager.ethContext(); @@ -538,7 +553,8 @@ private FastSyncActions createFastSyncActions( ethContext, new SyncState(blockchain, ethContext.getEthPeers(), true, Optional.empty()), pivotBlockSelector, - new NoOpMetricsSystem()); + new NoOpMetricsSystem(), + peerTaskExecutor); } @Test diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloaderTest.java index 34014246d28..3df647902db 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloaderTest.java @@ -29,6 +29,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; @@ -62,6 +63,7 @@ public class FastSyncChainDownloaderTest { protected EthContext ethContext; protected ProtocolContext protocolContext; private SyncState syncState; + private PeerTaskExecutor peerTaskExecutor; protected MutableBlockchain localBlockchain; private BlockchainSetupUtil otherBlockchainSetup; @@ -93,6 +95,7 @@ public void setup(final DataStorageFormat storageFormat) { ethContext = ethProtocolManager.ethContext(); syncState = new SyncState(protocolContext.getBlockchain(), ethContext.getEthPeers()); + peerTaskExecutor = new PeerTaskExecutor(null, null, null, new NoOpMetricsSystem()); } @AfterEach @@ -113,7 +116,8 @@ private ChainDownloader downloader( syncState, new NoOpMetricsSystem(), new FastSyncState(otherBlockchain.getBlockHeader(pivotBlockNumber).get()), - SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS); + SyncDurationMetrics.NO_OP_SYNC_DURATION_METRICS, + peerTaskExecutor); } @ParameterizedTest diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java index d7b5970098e..bb24637ca28 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderForkTest.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; @@ -88,6 +89,7 @@ private ChainDownloader downloader(final SynchronizerConfiguration syncConfig) { protocolSchedule, protocolContext, ethContext, + new PeerTaskExecutor(null, null, null, new NoOpMetricsSystem()), syncState, metricsSystem, SyncTerminationCondition.never(), diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java index ac7f0fb8257..cc7d96cb1d0 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTest.java @@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.messages.EthPV62; import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; @@ -120,6 +121,7 @@ private ChainDownloader downloader(final SynchronizerConfiguration syncConfig) { protocolSchedule, protocolContext, ethContext, + new PeerTaskExecutor(null, null, null, new NoOpMetricsSystem()), syncState, metricsSystem, SyncTerminationCondition.never(), diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTotalTerminalDifficultyTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTotalTerminalDifficultyTest.java index 311ccf5de30..cce0d26ae71 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTotalTerminalDifficultyTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncChainDownloaderTotalTerminalDifficultyTest.java @@ -27,6 +27,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; @@ -106,6 +107,7 @@ private ChainDownloader downloader( protocolSchedule, protocolContext, ethContext, + new PeerTaskExecutor(null, null, null, new NoOpMetricsSystem()), syncState, metricsSystem, terminalCondition, diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java index 63e41f6d25c..20c754fe528 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncDownloaderTest.java @@ -25,6 +25,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.TrailingPeerRequirements; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; @@ -95,6 +96,7 @@ private FullSyncDownloader downloader(final SynchronizerConfiguration syncConfig protocolSchedule, protocolContext, ethContext, + new PeerTaskExecutor(null, null, null, new NoOpMetricsSystem()), syncState, metricsSystem, SyncTerminationCondition.never(), diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java index 027bd270d29..3af058dba53 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fullsync/FullSyncTargetManagerTest.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.state.SyncTarget; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; @@ -95,6 +96,7 @@ public void setup(final DataStorageFormat storageFormat) { protocolSchedule, protocolContext, ethContext, + new PeerTaskExecutor(null, null, null, new NoOpMetricsSystem()), new NoOpMetricsSystem(), SyncTerminationCondition.never()); } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java index 73d5e5138b4..5598203897b 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskParameterizedTest.java @@ -30,10 +30,10 @@ import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; -import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; @@ -55,10 +55,12 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.mockito.Mockito; public class DetermineCommonAncestorTaskParameterizedTest { private final ProtocolSchedule protocolSchedule = ProtocolScheduleFixture.MAINNET; private static final BlockDataGenerator blockDataGenerator = new BlockDataGenerator(); + private PeerTaskExecutor peerTaskExecutor; private final MetricsSystem metricsSystem = new NoOpMetricsSystem(); private static Block genesisBlock; @@ -87,6 +89,7 @@ public static void setupClass() { @BeforeEach public void setup() { remoteBlockchain = createInMemoryBlockchain(genesisBlock); + peerTaskExecutor = Mockito.mock(PeerTaskExecutor.class); } public static Stream parameters() throws IOException { @@ -149,7 +152,6 @@ public void searchesAgainstNetwork(final int headerRequestSize, final int common final AtomicReference actualResult = new AtomicReference<>(); final AtomicBoolean done = new AtomicBoolean(false); - final EthContext ethContext = ethProtocolManager.ethContext(); final ProtocolContext protocolContext = new ProtocolContext(localBlockchain, worldStateArchive, null, new BadBlockManager()); @@ -157,7 +159,7 @@ public void searchesAgainstNetwork(final int headerRequestSize, final int common DetermineCommonAncestorTask.create( protocolSchedule, protocolContext, - ethContext, + peerTaskExecutor, respondingEthPeer.getEthPeer(), headerRequestSize, metricsSystem); diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java index 1b19a076d2c..aa67e741400 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/DetermineCommonAncestorTaskTest.java @@ -26,6 +26,7 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.chain.BadBlockManager; @@ -37,30 +38,41 @@ import org.hyperledger.besu.ethereum.core.ProtocolScheduleFixture; import org.hyperledger.besu.ethereum.core.TransactionReceipt; import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; -import org.hyperledger.besu.ethereum.eth.manager.EthContext; import org.hyperledger.besu.ethereum.eth.manager.EthPeer; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManagerTestUtil; import org.hyperledger.besu.ethereum.eth.manager.RespondingEthPeer; import org.hyperledger.besu.ethereum.eth.manager.exceptions.EthTaskException; import org.hyperledger.besu.ethereum.eth.manager.exceptions.EthTaskException.FailureReason; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResponseCode; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutorResult; +import org.hyperledger.besu.ethereum.eth.manager.peertask.task.GetHeadersFromPeerByNumberPeerTask; import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool; import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.p2p.rlpx.wire.messages.DisconnectMessage.DisconnectReason; import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive; import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import org.hyperledger.besu.plugin.services.MetricsSystem; import org.hyperledger.besu.util.ExceptionUtils; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; public class DetermineCommonAncestorTaskTest { @@ -71,8 +83,8 @@ public class DetermineCommonAncestorTaskTest { private MutableBlockchain localBlockchain; private Block localGenesisBlock; private EthProtocolManager ethProtocolManager; - private EthContext ethContext; private ProtocolContext protocolContext; + private PeerTaskExecutor peerTaskExecutor; @BeforeEach public void setup() { @@ -86,9 +98,9 @@ public void setup() { worldStateArchive, mock(TransactionPool.class), EthProtocolConfiguration.defaultConfig()); - ethContext = ethProtocolManager.ethContext(); protocolContext = new ProtocolContext(localBlockchain, worldStateArchive, null, new BadBlockManager()); + peerTaskExecutor = Mockito.mock(PeerTaskExecutor.class); } @Test @@ -103,11 +115,19 @@ public void shouldFailIfPeerDisconnects() { DetermineCommonAncestorTask.create( protocolSchedule, protocolContext, - ethContext, + peerTaskExecutor, respondingEthPeer.getEthPeer(), defaultHeaderRequestSize, metricsSystem); + Mockito.when( + peerTaskExecutor.executeAgainstPeerAsync( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), Mockito.any(EthPeer.class))) + .thenReturn( + CompletableFuture.completedFuture( + new PeerTaskExecutorResult<>( + null, PeerTaskExecutorResponseCode.PEER_DISCONNECTED))); + final AtomicReference failure = new AtomicReference<>(); final CompletableFuture future = task.run(); future.whenComplete( @@ -115,43 +135,66 @@ public void shouldFailIfPeerDisconnects() { failure.set(error); }); - // Disconnect the target peer - respondingEthPeer.disconnect(DisconnectReason.CLIENT_QUITTING); - assertThat(failure.get()).isNotNull(); final Throwable error = ExceptionUtils.rootCause(failure.get()); assertThat(error).isInstanceOf(EthTaskException.class); - assertThat(((EthTaskException) error).reason()).isEqualTo(FailureReason.NO_AVAILABLE_PEERS); + assertThat(((EthTaskException) error).reason()).isEqualTo(FailureReason.PEER_DISCONNECTED); } @Test - public void shouldHandleEmptyResponses() { + public void shouldHandleEmptyResponses() + throws ExecutionException, InterruptedException, TimeoutException { final Blockchain remoteBlockchain = setupLocalAndRemoteChains(11, 11, 5); - final RespondingEthPeer.Responder emptyResponder = RespondingEthPeer.emptyResponder(); - final RespondingEthPeer.Responder fullResponder = - RespondingEthPeer.blockchainResponder(remoteBlockchain); final RespondingEthPeer respondingEthPeer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager); + final AtomicBoolean executeTask = new AtomicBoolean(false); final EthTask task = DetermineCommonAncestorTask.create( protocolSchedule, protocolContext, - ethContext, + peerTaskExecutor, respondingEthPeer.getEthPeer(), defaultHeaderRequestSize, metricsSystem); - + Mockito.when( + peerTaskExecutor.executeAgainstPeerAsync( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), Mockito.any(EthPeer.class))) + .thenAnswer( + new Answer>>>() { + @Override + public CompletableFuture>> answer( + final InvocationOnMock invocationOnMock) throws Throwable { + if (!executeTask.get()) { + // simulate empty response + return CompletableFuture.completedFuture( + new PeerTaskExecutorResult<>(null, PeerTaskExecutorResponseCode.SUCCESS)); + } + GetHeadersFromPeerByNumberPeerTask task = + invocationOnMock.getArgument(0, GetHeadersFromPeerByNumberPeerTask.class); + List blockHeaders = new ArrayList<>(); + int numberDelta = (task.getSkip() + 1) * (task.getDirection().isReverse() ? -1 : 1); + for (long i = 1; i < task.getCount(); i++) { + remoteBlockchain + .getBlockHeader(task.getBlockNumber() + (i * numberDelta)) + .ifPresent(blockHeaders::add); + } + return CompletableFuture.completedFuture( + new PeerTaskExecutorResult<>( + blockHeaders, PeerTaskExecutorResponseCode.SUCCESS)); + } + }); // Empty response should be handled without any error - final CompletableFuture future = task.run(); - respondingEthPeer.respond(emptyResponder); + final CompletableFuture future = + task.runAsync(Executors.newSingleThreadExecutor()); assertThat(future).isNotDone(); // Task should continue on and complete when valid responses are received // Execute task and wait for response - respondingEthPeer.respondWhile(fullResponder, () -> !future.isDone()); + executeTask.set(true); + future.get(5000, TimeUnit.MILLISECONDS); assertThat(future).isDone(); assertThat(future).isNotCompletedExceptionally(); final BlockHeader expectedResult = remoteBlockchain.getBlockHeader(4).get(); @@ -177,8 +220,6 @@ public void calculateSkipInterval() { public void shouldIssueConsistentNumberOfRequestsToPeer() { final Blockchain remoteBlockchain = setupLocalAndRemoteChains(101, 101, 1); - final RespondingEthPeer.Responder responder = - RespondingEthPeer.blockchainResponder(remoteBlockchain); final RespondingEthPeer respondingEthPeer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager); @@ -186,15 +227,37 @@ public void shouldIssueConsistentNumberOfRequestsToPeer() { DetermineCommonAncestorTask.create( protocolSchedule, protocolContext, - ethContext, + peerTaskExecutor, respondingEthPeer.getEthPeer(), defaultHeaderRequestSize, metricsSystem); final DetermineCommonAncestorTask spy = spy(task); + Mockito.when( + peerTaskExecutor.executeAgainstPeerAsync( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), Mockito.any(EthPeer.class))) + .thenAnswer( + new Answer>>>() { + @Override + public CompletableFuture>> answer( + final InvocationOnMock invocationOnMock) throws Throwable { + GetHeadersFromPeerByNumberPeerTask task = + invocationOnMock.getArgument(0, GetHeadersFromPeerByNumberPeerTask.class); + List blockHeaders = new ArrayList<>(); + int numberDelta = (task.getSkip() + 1) * (task.getDirection().isReverse() ? -1 : 1); + for (long i = 1; i < task.getCount(); i++) { + remoteBlockchain + .getBlockHeader(task.getBlockNumber() + (i * numberDelta)) + .ifPresent(blockHeaders::add); + } + return CompletableFuture.completedFuture( + new PeerTaskExecutorResult<>( + blockHeaders, PeerTaskExecutorResponseCode.SUCCESS)); + } + }); + // Execute task final CompletableFuture future = spy.run(); - respondingEthPeer.respondWhile(responder, () -> !future.isDone()); final AtomicReference result = new AtomicReference<>(); future.whenComplete( @@ -213,8 +276,6 @@ public void shouldShortCircuitOnHeaderInInitialRequest() { final Blockchain remoteBlockchain = setupLocalAndRemoteChains(100, 100, 96); final BlockHeader commonHeader = localBlockchain.getBlockHeader(95).get(); - final RespondingEthPeer.Responder responder = - RespondingEthPeer.blockchainResponder(remoteBlockchain); final RespondingEthPeer respondingEthPeer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager); @@ -222,15 +283,37 @@ public void shouldShortCircuitOnHeaderInInitialRequest() { DetermineCommonAncestorTask.create( protocolSchedule, protocolContext, - ethContext, + peerTaskExecutor, respondingEthPeer.getEthPeer(), 10, metricsSystem); final DetermineCommonAncestorTask spy = spy(task); + Mockito.when( + peerTaskExecutor.executeAgainstPeerAsync( + Mockito.any(GetHeadersFromPeerByNumberPeerTask.class), Mockito.any(EthPeer.class))) + .thenAnswer( + new Answer>>>() { + @Override + public CompletableFuture>> answer( + final InvocationOnMock invocationOnMock) throws Throwable { + GetHeadersFromPeerByNumberPeerTask task = + invocationOnMock.getArgument(0, GetHeadersFromPeerByNumberPeerTask.class); + List blockHeaders = new ArrayList<>(); + int numberDelta = (task.getSkip() + 1) * (task.getDirection().isReverse() ? -1 : 1); + for (long i = 1; i < task.getCount(); i++) { + remoteBlockchain + .getBlockHeader(task.getBlockNumber() + (i * numberDelta)) + .ifPresent(blockHeaders::add); + } + return CompletableFuture.completedFuture( + new PeerTaskExecutorResult<>( + blockHeaders, PeerTaskExecutorResponseCode.SUCCESS)); + } + }); + // Execute task final CompletableFuture future = spy.run(); - respondingEthPeer.respondWhile(responder, () -> !future.isDone()); final AtomicReference result = new AtomicReference<>(); future.whenComplete( @@ -254,7 +337,7 @@ public void returnsImmediatelyWhenThereIsNoWorkToDo() throws Exception { DetermineCommonAncestorTask.create( protocolSchedule, protocolContext, - ethContext, + peerTaskExecutor, peer, defaultHeaderRequestSize, metricsSystem); @@ -266,6 +349,7 @@ public void returnsImmediatelyWhenThereIsNoWorkToDo() throws Exception { verify(peer, times(0)).getHeadersByHash(any(), anyInt(), anyInt(), anyBoolean()); verify(peer, times(0)).getHeadersByNumber(anyLong(), anyInt(), anyInt(), anyBoolean()); verify(peer, times(0)).send(any()); + verifyNoInteractions(peerTaskExecutor); } /** diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/GetReceiptsForHeadersTaskTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/GetReceiptsForHeadersTaskTest.java deleted file mode 100644 index 2b28e8a5c98..00000000000 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/tasks/GetReceiptsForHeadersTaskTest.java +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright 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. - * - * SPDX-License-Identifier: Apache-2.0 - */ -package org.hyperledger.besu.ethereum.eth.sync.tasks; - -import static java.util.Collections.emptyList; -import static org.assertj.core.api.Assertions.assertThat; -import static org.hyperledger.besu.datatypes.Hash.EMPTY_TRIE_HASH; - -import org.hyperledger.besu.ethereum.core.BlockHeader; -import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; -import org.hyperledger.besu.ethereum.core.TransactionReceipt; -import org.hyperledger.besu.ethereum.eth.manager.ethtaskutils.RetryingMessageTaskTest; -import org.hyperledger.besu.ethereum.eth.manager.task.EthTask; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import com.google.common.collect.ImmutableMap; -import org.junit.jupiter.api.Test; - -public class GetReceiptsForHeadersTaskTest - extends RetryingMessageTaskTest>> { - - @Override - protected Map> generateDataToBeRequested() { - // Setup data to be requested and expected response - final Map> blocks = new HashMap<>(); - for (long i = 0; i < 3; i++) { - final BlockHeader header = blockchain.getBlockHeader(10 + i).get(); - blocks.put(header, blockchain.getTxReceipts(header.getHash()).get()); - } - return blocks; - } - - @Override - protected EthTask>> createTask( - final Map> requestedData) { - final List headersToComplete = new ArrayList<>(requestedData.keySet()); - return GetReceiptsForHeadersTask.forHeaders( - ethContext, headersToComplete, maxRetries, metricsSystem); - } - - @Test - public void shouldBeCompleteWhenAllReceiptsAreEmpty() { - final BlockHeader header1 = - new BlockHeaderTestFixture().number(1).receiptsRoot(EMPTY_TRIE_HASH).buildHeader(); - final BlockHeader header2 = - new BlockHeaderTestFixture().number(2).receiptsRoot(EMPTY_TRIE_HASH).buildHeader(); - final BlockHeader header3 = - new BlockHeaderTestFixture().number(3).receiptsRoot(EMPTY_TRIE_HASH).buildHeader(); - - final Map> expected = - ImmutableMap.of(header1, emptyList(), header2, emptyList(), header3, emptyList()); - - assertThat(createTask(expected).run()).isCompletedWithValue(expected); - } -} diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java index c679183b0ff..bb4a48062ac 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TestNode.java @@ -44,6 +44,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; import org.hyperledger.besu.ethereum.eth.sync.ChainHeadTracker; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; @@ -170,6 +171,7 @@ public boolean isMessagePermitted(final EnodeURL destinationEnode, final int cod final EthScheduler scheduler = new EthScheduler(1, 1, 1, metricsSystem); final EthContext ethContext = new EthContext(ethPeers, ethMessages, scheduler); + final PeerManager peerManager = new PeerManager(); transactionPool = TransactionPoolFactory.createTransactionPool( @@ -196,7 +198,8 @@ public boolean isMessagePermitted(final EnodeURL destinationEnode, final int cod Collections.emptyList(), Optional.empty(), syncConfig, - scheduler); + scheduler, + peerManager); final NetworkRunner networkRunner = NetworkRunner.builder() diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java index 5742637c3e8..a75bb797417 100644 --- a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPoolFactoryTest.java @@ -45,6 +45,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.eth.manager.EthProtocolManager; import org.hyperledger.besu.ethereum.eth.manager.EthScheduler; +import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerManager; import org.hyperledger.besu.ethereum.eth.sync.SyncMode; import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; @@ -318,6 +319,7 @@ private void setupInitialSyncPhase(final SyncState syncState) { Optional.empty(), mock(SynchronizerConfiguration.class), mock(EthScheduler.class), + mock(PeerManager.class), mock(ForkIdManager.class)); } diff --git a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/peers/EnodeURLImplTest.java b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/peers/EnodeURLImplTest.java index cf8fdd6cf00..954def8dc75 100644 --- a/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/peers/EnodeURLImplTest.java +++ b/ethereum/p2p/src/test/java/org/hyperledger/besu/ethereum/p2p/peers/EnodeURLImplTest.java @@ -497,10 +497,11 @@ public void toURI_WithHostnameShouldWorkWhenDnsEnabled() { @Test @DisabledOnOs( - value = OS.MAC, + value = {OS.MAC, OS.LINUX}, disabledReason = "canonical lookup may not match dns lookup for local machine") public void toURI_WithHostnameShouldWorkWhenDnsEnabledAndUpdateEnabled() throws UnknownHostException { + final String enodeURLString = "enode://" + VALID_NODE_ID