7311: add GetReceiptsFromPeerTask (#7638)

Signed-off-by: Matilda Clerke <matilda.clerke@consensys.net>
pull/7837/head
Matilda-Clerke 3 weeks ago committed by GitHub
parent f9f721c10e
commit db29df7c8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 8
      besu/src/main/java/org/hyperledger/besu/controller/BesuControllerBuilder.java
  2. 3
      besu/src/main/java/org/hyperledger/besu/controller/TransitionBesuControllerBuilder.java
  3. 30
      besu/src/test/java/org/hyperledger/besu/RunnerTest.java
  4. 4
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/execution/JsonRpcExecutor.java
  5. 1
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthProtocolManager.java
  6. 15
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/EthScheduler.java
  7. 4
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTask.java
  8. 2
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutor.java
  9. 135
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/GetReceiptsFromPeerTask.java
  10. 5
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/DefaultSynchronizer.java
  11. 73
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloadBlockStep.java
  12. 4
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointDownloaderFactory.java
  13. 4
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncActions.java
  14. 10
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncChainDownloader.java
  15. 14
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckpointSyncDownloadPipelineFactory.java
  16. 80
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStep.java
  17. 5
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActions.java
  18. 10
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloader.java
  19. 7
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncDownloadPipelineFactory.java
  20. 3
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/worldstate/FastDownloaderFactory.java
  21. 3
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/snapsync/SnapDownloaderFactory.java
  22. 12
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/peertask/PeerTaskExecutorTest.java
  23. 264
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/manager/peertask/task/GetReceiptsFromPeerTaskTest.java
  24. 137
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/checkpointsync/CheckPointSyncChainDownloaderTest.java
  25. 73
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/DownloadReceiptsStepTest.java
  26. 7
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastDownloaderFactoryTest.java
  27. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncActionsTest.java
  28. 2
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/FastSyncChainDownloaderTest.java

@ -55,6 +55,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.MonitoredExecutors;
import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskExecutor;
import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTaskRequestSender;
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;
@ -653,6 +655,8 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
}
final EthContext ethContext = new EthContext(ethPeers, ethMessages, snapMessages, scheduler);
final PeerTaskExecutor peerTaskExecutor =
new PeerTaskExecutor(ethPeers, new PeerTaskRequestSender(), metricsSystem);
final boolean fullSyncDisabled = !SyncMode.isFullSync(syncConfig.getSyncMode());
final SyncState syncState = new SyncState(blockchain, ethPeers, fullSyncDisabled, checkpoint);
@ -704,6 +708,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
worldStateStorageCoordinator,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
ethProtocolManager,
pivotBlockSelector);
@ -830,6 +835,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
* @param worldStateStorageCoordinator the world state storage
* @param protocolContext the protocol context
* @param ethContext the eth context
* @param peerTaskExecutor the PeerTaskExecutor
* @param syncState the sync state
* @param ethProtocolManager the eth protocol manager
* @param pivotBlockSelector the pivot block selector
@ -840,6 +846,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
final WorldStateStorageCoordinator worldStateStorageCoordinator,
final ProtocolContext protocolContext,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final SyncState syncState,
final EthProtocolManager ethProtocolManager,
final PivotBlockSelector pivotBlockSelector) {
@ -851,6 +858,7 @@ public abstract class BesuControllerBuilder implements MiningParameterOverrides
worldStateStorageCoordinator,
ethProtocolManager.getBlockBroadcaster(),
ethContext,
peerTaskExecutor,
syncState,
dataDirectory,
storageProvider,

@ -40,6 +40,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.MergePeerFilter;
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;
@ -225,6 +226,7 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
final WorldStateStorageCoordinator worldStateStorageCoordinator,
final ProtocolContext protocolContext,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final SyncState syncState,
final EthProtocolManager ethProtocolManager,
final PivotBlockSelector pivotBlockSelector) {
@ -235,6 +237,7 @@ public class TransitionBesuControllerBuilder extends BesuControllerBuilder {
worldStateStorageCoordinator,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
ethProtocolManager,
pivotBlockSelector);

@ -153,7 +153,15 @@ public final class RunnerTest {
// set merge flag to false, otherwise this test can fail if a merge test runs first
MergeConfiguration.setMergeEnabled(false);
syncFromGenesis(SyncMode.FULL, getFastSyncGenesis());
syncFromGenesis(SyncMode.FULL, getFastSyncGenesis(), false);
}
@Test
public void fullSyncFromGenesisUsingPeerTaskSystem() throws Exception {
// set merge flag to false, otherwise this test can fail if a merge test runs first
MergeConfiguration.setMergeEnabled(false);
syncFromGenesis(SyncMode.FULL, getFastSyncGenesis(), true);
}
@Test
@ -161,10 +169,21 @@ public final class RunnerTest {
// set merge flag to false, otherwise this test can fail if a merge test runs first
MergeConfiguration.setMergeEnabled(false);
syncFromGenesis(SyncMode.FAST, getFastSyncGenesis());
syncFromGenesis(SyncMode.FAST, getFastSyncGenesis(), false);
}
@Test
public void fastSyncFromGenesisUsingPeerTaskSystem() throws Exception {
// set merge flag to false, otherwise this test can fail if a merge test runs first
MergeConfiguration.setMergeEnabled(false);
syncFromGenesis(SyncMode.FAST, getFastSyncGenesis(), true);
}
private void syncFromGenesis(final SyncMode mode, final GenesisConfigFile genesisConfig)
private void syncFromGenesis(
final SyncMode mode,
final GenesisConfigFile genesisConfig,
final boolean isPeerTaskSystemEnabled)
throws Exception {
final Path dataDirAhead = Files.createTempDirectory(temp, "db-ahead");
final Path dbAhead = dataDirAhead.resolve("database");
@ -172,7 +191,10 @@ public final class RunnerTest {
final NodeKey aheadDbNodeKey = NodeKeyUtils.createFrom(KeyPairUtil.loadKeyPair(dataDirAhead));
final NodeKey behindDbNodeKey = NodeKeyUtils.generate();
final SynchronizerConfiguration syncConfigAhead =
SynchronizerConfiguration.builder().syncMode(SyncMode.FULL).build();
SynchronizerConfiguration.builder()
.syncMode(SyncMode.FULL)
.isPeerTaskSystemEnabled(isPeerTaskSystemEnabled)
.build();
final ObservableMetricsSystem noOpMetricsSystem = new NoOpMetricsSystem();
final var miningParameters = MiningParameters.newDefault();
final var dataStorageConfiguration = DataStorageConfiguration.DEFAULT_FOREST_CONFIG;

@ -104,9 +104,9 @@ public class JsonRpcExecutor {
private Optional<RpcErrorType> validateMethodAvailability(final JsonRpcRequest request) {
final String name = request.getMethod();
if (LOG.isDebugEnabled()) {
if (LOG.isTraceEnabled()) {
final JsonArray params = JsonObject.mapFrom(request).getJsonArray("params");
LOG.debug("JSON-RPC request -> {} {}", name, params);
LOG.trace("JSON-RPC request -> {} {}", name, params);
}
final JsonRpcMethod method = rpcMethods.get(name);

@ -337,7 +337,6 @@ public class EthProtocolManager implements ProtocolManager, MinedBlockObserver {
public void handleNewConnection(final PeerConnection connection) {
ethPeers.registerNewConnection(connection, peerValidators);
final EthPeer peer = ethPeers.peer(connection);
final Capability cap = connection.capability(getSupportedProtocol());
final ForkId latestForkId =
cap.getVersion() >= 64 ? forkIdManager.getForkIdForChainHead() : null;

@ -145,7 +145,7 @@ public class EthScheduler {
servicesExecutor.execute(command);
}
public <T> CompletableFuture<Void> scheduleServiceTask(final Runnable task) {
public CompletableFuture<Void> scheduleServiceTask(final Runnable task) {
return CompletableFuture.runAsync(task, servicesExecutor);
}
@ -156,6 +156,19 @@ public class EthScheduler {
return serviceFuture;
}
public <T> CompletableFuture<T> scheduleServiceTask(final Supplier<CompletableFuture<T>> future) {
final CompletableFuture<T> promise = new CompletableFuture<>();
final Future<?> workerFuture = servicesExecutor.submit(() -> propagateResult(future, promise));
// If returned promise is cancelled, cancel the worker future
promise.whenComplete(
(r, t) -> {
if (t instanceof CancellationException) {
workerFuture.cancel(false);
}
});
return promise;
}
public CompletableFuture<Void> startPipeline(final Pipeline<?> pipeline) {
final CompletableFuture<Void> pipelineFuture = pipeline.start(servicesExecutor);
pendingFutures.add(pipelineFuture);

@ -41,13 +41,13 @@ public interface PeerTask<T> {
MessageData getRequestMessage();
/**
* Parses the MessageData response from the EthPeer
* Parses and processes 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;
T processResponse(MessageData messageData) throws InvalidPeerTaskResponseException;
/**
* Gets the number of times this task may be attempted against other peers

@ -133,7 +133,7 @@ public class PeerTaskExecutor {
MessageData responseMessageData =
requestSender.sendRequest(peerTask.getSubProtocol(), requestMessageData, peer);
result = peerTask.parseResponse(responseMessageData);
result = peerTask.processResponse(responseMessageData);
} finally {
inflightRequestCountForThisTaskClass.decrementAndGet();
}

@ -0,0 +1,135 @@
/*
* 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 static java.util.Collections.emptyList;
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.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.peertask.InvalidPeerTaskResponseException;
import org.hyperledger.besu.ethereum.eth.manager.peertask.PeerTask;
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.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.SubProtocol;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;
public class GetReceiptsFromPeerTask
implements PeerTask<Map<BlockHeader, List<TransactionReceipt>>> {
private final Collection<BlockHeader> blockHeaders;
private final ProtocolSchedule protocolSchedule;
private final Map<BlockHeader, List<TransactionReceipt>> receiptsByBlockHeader = new HashMap<>();
private final Map<Hash, List<BlockHeader>> headersByReceiptsRoot = new HashMap<>();
private final long requiredBlockchainHeight;
public GetReceiptsFromPeerTask(
final Collection<BlockHeader> blockHeaders, final ProtocolSchedule protocolSchedule) {
this.blockHeaders = new ArrayList<>(blockHeaders);
this.protocolSchedule = protocolSchedule;
// pre-fill any headers with an empty receipts root into the result map
this.blockHeaders.stream()
.filter(header -> header.getReceiptsRoot().equals(Hash.EMPTY_TRIE_HASH))
.forEach(header -> receiptsByBlockHeader.put(header, emptyList()));
this.blockHeaders.removeAll(receiptsByBlockHeader.keySet());
// group headers by their receipts root hash to reduce total number of receipts hashes requested
// for
this.blockHeaders.forEach(
header ->
headersByReceiptsRoot
.computeIfAbsent(header.getReceiptsRoot(), key -> new ArrayList<>())
.add(header));
// calculate the minimum required blockchain height a peer will need to be able to fulfil this
// request
requiredBlockchainHeight =
this.blockHeaders.stream()
.mapToLong(BlockHeader::getNumber)
.max()
.orElse(BlockHeader.GENESIS_BLOCK_NUMBER);
}
@Override
public SubProtocol getSubProtocol() {
return EthProtocol.get();
}
@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<Hash> blockHashes =
headersByReceiptsRoot.values().stream()
.map(headers -> headers.getFirst().getHash())
.toList();
return GetReceiptsMessage.create(blockHashes);
}
@Override
public Map<BlockHeader, List<TransactionReceipt>> processResponse(final MessageData messageData)
throws InvalidPeerTaskResponseException {
if (messageData == null) {
throw new InvalidPeerTaskResponseException();
}
final ReceiptsMessage receiptsMessage = ReceiptsMessage.readFrom(messageData);
final List<List<TransactionReceipt>> receiptsByBlock = receiptsMessage.receipts();
// take a copy of the pre-filled receiptsByBlockHeader, to ensure idempotency of subsequent
// calls to processResponse
final Map<BlockHeader, List<TransactionReceipt>> receiptsByHeader =
new HashMap<>(receiptsByBlockHeader);
if (!blockHeaders.isEmpty()) {
if (receiptsByBlock.isEmpty() || receiptsByBlock.size() > blockHeaders.size()) {
throw new InvalidPeerTaskResponseException();
}
for (final List<TransactionReceipt> receiptsInBlock : receiptsByBlock) {
final List<BlockHeader> 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 Predicate<EthPeer> getPeerRequirementFilter() {
return (ethPeer) ->
ethPeer.getProtocolName().equals(getSubProtocol().getName())
&& (protocolSchedule.anyMatch((ps) -> ps.spec().isPoS())
|| ethPeer.chainState().getEstimatedHeight() >= requiredBlockchainHeight);
}
@Override
public boolean isSuccess(final Map<BlockHeader, List<TransactionReceipt>> result) {
return !result.isEmpty();
}
}

@ -22,6 +22,7 @@ import org.hyperledger.besu.datatypes.Address;
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;
@ -82,6 +83,7 @@ public class DefaultSynchronizer implements Synchronizer, UnverifiedForkchoiceLi
final WorldStateStorageCoordinator worldStateStorageCoordinator,
final BlockBroadcaster blockBroadcaster,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final SyncState syncState,
final Path dataDirectory,
final StorageProvider storageProvider,
@ -147,6 +149,7 @@ public class DefaultSynchronizer implements Synchronizer, UnverifiedForkchoiceLi
protocolContext,
metricsSystem,
ethContext,
peerTaskExecutor,
worldStateStorageCoordinator,
syncState,
clock,
@ -163,6 +166,7 @@ public class DefaultSynchronizer implements Synchronizer, UnverifiedForkchoiceLi
protocolContext,
metricsSystem,
ethContext,
peerTaskExecutor,
worldStateStorageCoordinator,
syncState,
clock,
@ -179,6 +183,7 @@ public class DefaultSynchronizer implements Synchronizer, UnverifiedForkchoiceLi
protocolContext,
metricsSystem,
ethContext,
peerTaskExecutor,
worldStateStorageCoordinator,
syncState,
clock,

@ -16,17 +16,23 @@ package org.hyperledger.besu.ethereum.eth.sync.checkpointsync;
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.SynchronizerConfiguration;
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;
@ -34,17 +40,23 @@ public class CheckpointDownloadBlockStep {
private final ProtocolSchedule protocolSchedule;
private final EthContext ethContext;
private final PeerTaskExecutor peerTaskExecutor;
private final Checkpoint checkpoint;
private final SynchronizerConfiguration synchronizerConfiguration;
private final MetricsSystem metricsSystem;
public CheckpointDownloadBlockStep(
final ProtocolSchedule protocolSchedule,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final Checkpoint checkpoint,
final SynchronizerConfiguration synchronizerConfiguration,
final MetricsSystem metricsSystem) {
this.protocolSchedule = protocolSchedule;
this.ethContext = ethContext;
this.peerTaskExecutor = peerTaskExecutor;
this.checkpoint = checkpoint;
this.synchronizerConfiguration = synchronizerConfiguration;
this.metricsSystem = metricsSystem;
}
@ -65,17 +77,52 @@ public class CheckpointDownloadBlockStep {
private CompletableFuture<Optional<BlockWithReceipts>> downloadReceipts(
final PeerTaskResult<Block> peerTaskResult) {
final Block block = peerTaskResult.getResult();
final GetReceiptsFromPeerTask getReceiptsFromPeerTask =
GetReceiptsFromPeerTask.forHeaders(ethContext, List.of(block.getHeader()), metricsSystem);
return getReceiptsFromPeerTask
.run()
.thenCompose(
receiptTaskResult -> {
final Optional<List<TransactionReceipt>> transactionReceipts =
Optional.ofNullable(receiptTaskResult.getResult().get(block.getHeader()));
return CompletableFuture.completedFuture(
transactionReceipts.map(receipts -> new BlockWithReceipts(block, receipts)));
})
.exceptionally(throwable -> Optional.empty());
if (synchronizerConfiguration.isPeerTaskSystemEnabled()) {
return ethContext
.getScheduler()
.scheduleServiceTask(
() -> {
GetReceiptsFromPeerTask task =
new GetReceiptsFromPeerTask(List.of(block.getHeader()), protocolSchedule);
PeerTaskExecutorResult<Map<BlockHeader, List<TransactionReceipt>>> executorResult =
peerTaskExecutor.execute(task);
if (executorResult.responseCode() == PeerTaskExecutorResponseCode.SUCCESS) {
List<TransactionReceipt> transactionReceipts =
executorResult
.result()
.map((map) -> map.get(block.getHeader()))
.orElseThrow(
() ->
new IllegalStateException(
"PeerTask response code was success, but empty"));
if (block.getBody().getTransactions().size() != transactionReceipts.size()) {
throw new IllegalStateException(
"PeerTask response code was success, but incorrect number of receipts returned");
}
BlockWithReceipts blockWithReceipts =
new BlockWithReceipts(block, transactionReceipts);
return CompletableFuture.completedFuture(Optional.of(blockWithReceipts));
} else {
return CompletableFuture.completedFuture(Optional.empty());
}
});
} else {
final org.hyperledger.besu.ethereum.eth.manager.task.GetReceiptsFromPeerTask
getReceiptsFromPeerTask =
org.hyperledger.besu.ethereum.eth.manager.task.GetReceiptsFromPeerTask.forHeaders(
ethContext, List.of(block.getHeader()), metricsSystem);
return getReceiptsFromPeerTask
.run()
.thenCompose(
receiptTaskResult -> {
final Optional<List<TransactionReceipt>> transactionReceipts =
Optional.ofNullable(receiptTaskResult.getResult().get(block.getHeader()));
return CompletableFuture.completedFuture(
transactionReceipts.map(receipts -> new BlockWithReceipts(block, receipts)));
})
.exceptionally(throwable -> Optional.empty());
}
}
}

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.eth.sync.checkpointsync;
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;
@ -61,6 +62,7 @@ public class CheckpointDownloaderFactory extends SnapDownloaderFactory {
final ProtocolContext protocolContext,
final MetricsSystem metricsSystem,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final WorldStateStorageCoordinator worldStateStorageCoordinator,
final SyncState syncState,
final Clock clock,
@ -110,6 +112,7 @@ public class CheckpointDownloaderFactory extends SnapDownloaderFactory {
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
pivotBlockSelector,
metricsSystem);
@ -127,6 +130,7 @@ public class CheckpointDownloaderFactory extends SnapDownloaderFactory {
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
pivotBlockSelector,
metricsSystem);

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.sync.checkpointsync;
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;
@ -34,6 +35,7 @@ public class CheckpointSyncActions extends FastSyncActions {
final ProtocolSchedule protocolSchedule,
final ProtocolContext protocolContext,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final SyncState syncState,
final PivotBlockSelector pivotBlockSelector,
final MetricsSystem metricsSystem) {
@ -43,6 +45,7 @@ public class CheckpointSyncActions extends FastSyncActions {
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
pivotBlockSelector,
metricsSystem);
@ -57,6 +60,7 @@ public class CheckpointSyncActions extends FastSyncActions {
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
metricsSystem,
currentState,

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.sync.checkpointsync;
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;
@ -36,6 +37,7 @@ public class CheckpointSyncChainDownloader extends FastSyncChainDownloader {
final ProtocolSchedule protocolSchedule,
final ProtocolContext protocolContext,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final SyncState syncState,
final MetricsSystem metricsSystem,
final FastSyncState fastSyncState,
@ -55,7 +57,13 @@ public class CheckpointSyncChainDownloader extends FastSyncChainDownloader {
syncState,
syncTargetManager,
new CheckpointSyncDownloadPipelineFactory(
config, protocolSchedule, protocolContext, ethContext, fastSyncState, metricsSystem),
config,
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
fastSyncState,
metricsSystem),
ethContext.getScheduler(),
metricsSystem,
syncDurationMetrics);

@ -19,6 +19,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.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;
@ -40,9 +41,17 @@ public class CheckpointSyncDownloadPipelineFactory extends FastSyncDownloadPipel
final ProtocolSchedule protocolSchedule,
final ProtocolContext protocolContext,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final FastSyncState fastSyncState,
final MetricsSystem metricsSystem) {
super(syncConfig, protocolSchedule, protocolContext, ethContext, fastSyncState, metricsSystem);
super(
syncConfig,
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
fastSyncState,
metricsSystem);
}
@Override
@ -76,7 +85,8 @@ public class CheckpointSyncDownloadPipelineFactory extends FastSyncDownloadPipel
checkPointSource, checkpoint, protocolContext.getBlockchain());
final CheckpointDownloadBlockStep checkPointDownloadBlockStep =
new CheckpointDownloadBlockStep(protocolSchedule, ethContext, checkpoint, metricsSystem);
new CheckpointDownloadBlockStep(
protocolSchedule, ethContext, peerTaskExecutor, checkpoint, syncConfig, metricsSystem);
return PipelineBuilder.createPipelineFrom(
"fetchCheckpoints",

@ -22,10 +22,16 @@ 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.sync.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.sync.tasks.GetReceiptsForHeadersTask;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.plugin.services.MetricsSystem;
import org.hyperledger.besu.util.FutureUtils;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
@ -33,24 +39,69 @@ import java.util.function.Function;
public class DownloadReceiptsStep
implements Function<List<Block>, CompletableFuture<List<BlockWithReceipts>>> {
private final ProtocolSchedule protocolSchedule;
private final EthContext ethContext;
private final PeerTaskExecutor peerTaskExecutor;
private final SynchronizerConfiguration synchronizerConfiguration;
private final MetricsSystem metricsSystem;
public DownloadReceiptsStep(final EthContext ethContext, final MetricsSystem metricsSystem) {
public DownloadReceiptsStep(
final ProtocolSchedule protocolSchedule,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final SynchronizerConfiguration synchronizerConfiguration,
final MetricsSystem metricsSystem) {
this.protocolSchedule = protocolSchedule;
this.ethContext = ethContext;
this.peerTaskExecutor = peerTaskExecutor;
this.synchronizerConfiguration = synchronizerConfiguration;
this.metricsSystem = metricsSystem;
}
@Override
public CompletableFuture<List<BlockWithReceipts>> apply(final List<Block> blocks) {
final List<BlockHeader> headers = blocks.stream().map(Block::getHeader).collect(toList());
final CompletableFuture<Map<BlockHeader, List<TransactionReceipt>>> getReceipts =
GetReceiptsForHeadersTask.forHeaders(ethContext, headers, metricsSystem).run();
final CompletableFuture<List<BlockWithReceipts>> combineWithBlocks =
getReceipts.thenApply(
receiptsByHeader -> combineBlocksAndReceipts(blocks, receiptsByHeader));
FutureUtils.propagateCancellation(combineWithBlocks, getReceipts);
return combineWithBlocks;
if (synchronizerConfiguration.isPeerTaskSystemEnabled()) {
return ethContext
.getScheduler()
.scheduleServiceTask(() -> getReceiptsWithPeerTaskSystem(headers))
.thenApply((receipts) -> combineBlocksAndReceipts(blocks, receipts));
} else {
return GetReceiptsForHeadersTask.forHeaders(ethContext, headers, metricsSystem)
.run()
.thenApply((receipts) -> combineBlocksAndReceipts(blocks, receipts));
}
}
private CompletableFuture<Map<BlockHeader, List<TransactionReceipt>>>
getReceiptsWithPeerTaskSystem(final List<BlockHeader> headers) {
Map<BlockHeader, List<TransactionReceipt>> getReceipts = new HashMap<>();
do {
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(headers, protocolSchedule);
PeerTaskExecutorResult<Map<BlockHeader, List<TransactionReceipt>>> getReceiptsResult =
peerTaskExecutor.execute(task);
if (getReceiptsResult.responseCode() == PeerTaskExecutorResponseCode.SUCCESS
&& getReceiptsResult.result().isPresent()) {
Map<BlockHeader, List<TransactionReceipt>> taskResult = getReceiptsResult.result().get();
taskResult
.keySet()
.forEach(
(blockHeader) ->
getReceipts.merge(
blockHeader,
taskResult.get(blockHeader),
(initialReceipts, newReceipts) -> {
throw new IllegalStateException(
"Unexpectedly got receipts for block header already populated!");
}));
// remove all the headers we found receipts for
headers.removeAll(getReceipts.keySet());
}
// repeat until all headers have receipts
} while (!headers.isEmpty());
return CompletableFuture.completedFuture(getReceipts);
}
private List<BlockWithReceipts> combineBlocksAndReceipts(
@ -60,8 +111,17 @@ public class DownloadReceiptsStep
block -> {
final List<TransactionReceipt> receipts =
receiptsByHeader.getOrDefault(block.getHeader(), emptyList());
if (block.getBody().getTransactions().size() != receipts.size()) {
throw new IllegalStateException(
"PeerTask response code was success, but incorrect number of receipts returned. Header hash: "
+ block.getHeader().getHash()
+ ", Transactions: "
+ block.getBody().getTransactions().size()
+ ", receipts: "
+ receipts.size());
}
return new BlockWithReceipts(block, receipts);
})
.collect(toList());
.toList();
}
}

@ -19,6 +19,7 @@ import static java.util.concurrent.CompletableFuture.completedFuture;
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;
@ -48,6 +49,7 @@ public class FastSyncActions {
protected final ProtocolSchedule protocolSchedule;
protected final ProtocolContext protocolContext;
protected final EthContext ethContext;
protected final PeerTaskExecutor peerTaskExecutor;
protected final SyncState syncState;
protected final PivotBlockSelector pivotBlockSelector;
protected final MetricsSystem metricsSystem;
@ -60,6 +62,7 @@ public class FastSyncActions {
final ProtocolSchedule protocolSchedule,
final ProtocolContext protocolContext,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final SyncState syncState,
final PivotBlockSelector pivotBlockSelector,
final MetricsSystem metricsSystem) {
@ -68,6 +71,7 @@ public class FastSyncActions {
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
this.ethContext = ethContext;
this.peerTaskExecutor = peerTaskExecutor;
this.syncState = syncState;
this.pivotBlockSelector = pivotBlockSelector;
this.metricsSystem = metricsSystem;
@ -164,6 +168,7 @@ public class FastSyncActions {
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
metricsSystem,
currentState,

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.eth.sync.fastsync;
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;
@ -35,6 +36,7 @@ public class FastSyncChainDownloader {
final ProtocolSchedule protocolSchedule,
final ProtocolContext protocolContext,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final SyncState syncState,
final MetricsSystem metricsSystem,
final FastSyncState fastSyncState,
@ -53,7 +55,13 @@ public class FastSyncChainDownloader {
syncState,
syncTargetManager,
new FastSyncDownloadPipelineFactory(
config, protocolSchedule, protocolContext, ethContext, fastSyncState, metricsSystem),
config,
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
fastSyncState,
metricsSystem),
ethContext.getScheduler(),
metricsSystem,
syncDurationMetrics);

@ -26,6 +26,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.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;
@ -58,6 +59,7 @@ public class FastSyncDownloadPipelineFactory implements DownloadPipelineFactory
protected final ProtocolSchedule protocolSchedule;
protected final ProtocolContext protocolContext;
protected final EthContext ethContext;
protected final PeerTaskExecutor peerTaskExecutor;
protected final FastSyncState fastSyncState;
protected final MetricsSystem metricsSystem;
protected final FastSyncValidationPolicy attachedValidationPolicy;
@ -69,12 +71,14 @@ public class FastSyncDownloadPipelineFactory implements DownloadPipelineFactory
final ProtocolSchedule protocolSchedule,
final ProtocolContext protocolContext,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final FastSyncState fastSyncState,
final MetricsSystem metricsSystem) {
this.syncConfig = syncConfig;
this.protocolSchedule = protocolSchedule;
this.protocolContext = protocolContext;
this.ethContext = ethContext;
this.peerTaskExecutor = peerTaskExecutor;
this.fastSyncState = fastSyncState;
this.metricsSystem = metricsSystem;
final LabelledMetric<Counter> fastSyncValidationCounter =
@ -145,7 +149,8 @@ public class FastSyncDownloadPipelineFactory implements DownloadPipelineFactory
final DownloadBodiesStep downloadBodiesStep =
new DownloadBodiesStep(protocolSchedule, ethContext, metricsSystem);
final DownloadReceiptsStep downloadReceiptsStep =
new DownloadReceiptsStep(ethContext, metricsSystem);
new DownloadReceiptsStep(
protocolSchedule, ethContext, peerTaskExecutor, syncConfig, metricsSystem);
final ImportBlocksStep importBlockStep =
new ImportBlocksStep(
protocolSchedule,

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.eth.sync.fastsync.worldstate;
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;
@ -59,6 +60,7 @@ public class FastDownloaderFactory {
final ProtocolContext protocolContext,
final MetricsSystem metricsSystem,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final WorldStateStorageCoordinator worldStateStorageCoordinator,
final SyncState syncState,
final Clock clock,
@ -126,6 +128,7 @@ public class FastDownloaderFactory {
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
pivotBlockSelector,
metricsSystem),

@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.eth.sync.snapsync;
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;
@ -57,6 +58,7 @@ public class SnapDownloaderFactory extends FastDownloaderFactory {
final ProtocolContext protocolContext,
final MetricsSystem metricsSystem,
final EthContext ethContext,
final PeerTaskExecutor peerTaskExecutor,
final WorldStateStorageCoordinator worldStateStorageCoordinator,
final SyncState syncState,
final Clock clock,
@ -121,6 +123,7 @@ public class SnapDownloaderFactory extends FastDownloaderFactory {
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
pivotBlockSelector,
metricsSystem),

@ -72,7 +72,7 @@ public class PeerTaskExecutorTest {
Mockito.when(subprotocol.getName()).thenReturn("subprotocol");
Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, ethPeer))
.thenReturn(responseMessageData);
Mockito.when(peerTask.parseResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.processResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.isSuccess(responseObject)).thenReturn(true);
PeerTaskExecutorResult<Object> result = peerTaskExecutor.executeAgainstPeer(peerTask, ethPeer);
@ -101,7 +101,7 @@ public class PeerTaskExecutorTest {
Mockito.when(subprotocol.getName()).thenReturn("subprotocol");
Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, ethPeer))
.thenReturn(responseMessageData);
Mockito.when(peerTask.parseResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.processResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.isSuccess(responseObject)).thenReturn(false);
PeerTaskExecutorResult<Object> result = peerTaskExecutor.executeAgainstPeer(peerTask, ethPeer);
@ -130,7 +130,7 @@ public class PeerTaskExecutorTest {
.thenThrow(new TimeoutException())
.thenReturn(responseMessageData);
Mockito.when(requestMessageData.getCode()).thenReturn(requestMessageDataCode);
Mockito.when(peerTask.parseResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.processResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.isSuccess(responseObject)).thenReturn(true);
PeerTaskExecutorResult<Object> result = peerTaskExecutor.executeAgainstPeer(peerTask, ethPeer);
@ -204,7 +204,7 @@ public class PeerTaskExecutorTest {
Mockito.when(subprotocol.getName()).thenReturn("subprotocol");
Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, ethPeer))
.thenReturn(responseMessageData);
Mockito.when(peerTask.parseResponse(responseMessageData))
Mockito.when(peerTask.processResponse(responseMessageData))
.thenThrow(new InvalidPeerTaskResponseException());
PeerTaskExecutorResult<Object> result = peerTaskExecutor.executeAgainstPeer(peerTask, ethPeer);
@ -236,7 +236,7 @@ public class PeerTaskExecutorTest {
Mockito.when(subprotocol.getName()).thenReturn("subprotocol");
Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, ethPeer))
.thenReturn(responseMessageData);
Mockito.when(peerTask.parseResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.processResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.isSuccess(responseObject)).thenReturn(true);
PeerTaskExecutorResult<Object> result = peerTaskExecutor.executeAgainstPeer(peerTask, ethPeer);
@ -274,7 +274,7 @@ public class PeerTaskExecutorTest {
Mockito.when(requestMessageData.getCode()).thenReturn(requestMessageDataCode);
Mockito.when(requestSender.sendRequest(subprotocol, requestMessageData, peer2))
.thenReturn(responseMessageData);
Mockito.when(peerTask.parseResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.processResponse(responseMessageData)).thenReturn(responseObject);
Mockito.when(peerTask.isSuccess(responseObject)).thenReturn(true);
PeerTaskExecutorResult<Object> result = peerTaskExecutor.execute(peerTask);

@ -0,0 +1,264 @@
/*
* 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.ChainState;
import org.hyperledger.besu.ethereum.eth.manager.EthPeer;
import org.hyperledger.besu.ethereum.eth.manager.peertask.InvalidPeerTaskResponseException;
import org.hyperledger.besu.ethereum.eth.messages.EthPV63;
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.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.p2p.rlpx.wire.MessageData;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class GetReceiptsFromPeerTaskTest {
@Test
public void testGetSubProtocol() {
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(Collections.emptyList(), null);
Assertions.assertEquals(EthProtocol.get(), task.getSubProtocol());
}
@Test
public void testGetRequestMessage() {
BlockHeader blockHeader1 = mockBlockHeader(1);
TransactionReceipt receiptForBlock1 =
new TransactionReceipt(1, 123, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader1.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock1)));
BlockHeader blockHeader2 = mockBlockHeader(2);
TransactionReceipt receiptForBlock2 =
new TransactionReceipt(1, 456, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader2.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock2)));
BlockHeader blockHeader3 = mockBlockHeader(3);
TransactionReceipt receiptForBlock3 =
new TransactionReceipt(1, 789, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader3.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock3)));
GetReceiptsFromPeerTask task =
new GetReceiptsFromPeerTask(List.of(blockHeader1, blockHeader2, blockHeader3), null);
MessageData messageData = task.getRequestMessage();
GetReceiptsMessage getReceiptsMessage = GetReceiptsMessage.readFrom(messageData);
Assertions.assertEquals(EthPV63.GET_RECEIPTS, getReceiptsMessage.getCode());
Iterable<Hash> hashesInMessage = getReceiptsMessage.hashes();
List<Hash> expectedHashes =
List.of(
Hash.fromHexString(StringUtils.repeat("00", 31) + "11"),
Hash.fromHexString(StringUtils.repeat("00", 31) + "21"),
Hash.fromHexString(StringUtils.repeat("00", 31) + "31"));
List<Hash> actualHashes = new ArrayList<>();
hashesInMessage.forEach(actualHashes::add);
Assertions.assertEquals(3, actualHashes.size());
Assertions.assertEquals(
expectedHashes.stream().sorted().toList(), actualHashes.stream().sorted().toList());
}
@Test
public void testParseResponseWithNullResponseMessage() {
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(Collections.emptyList(), null);
Assertions.assertThrows(
InvalidPeerTaskResponseException.class, () -> task.processResponse(null));
}
@Test
public void testParseResponseForInvalidResponse() {
BlockHeader blockHeader1 = mockBlockHeader(1);
TransactionReceipt receiptForBlock1 =
new TransactionReceipt(1, 123, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader1.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock1)));
BlockHeader blockHeader2 = mockBlockHeader(2);
TransactionReceipt receiptForBlock2 =
new TransactionReceipt(1, 456, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader2.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock2)));
BlockHeader blockHeader3 = mockBlockHeader(3);
TransactionReceipt receiptForBlock3 =
new TransactionReceipt(1, 789, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader3.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock3)));
GetReceiptsFromPeerTask task =
new GetReceiptsFromPeerTask(List.of(blockHeader1, blockHeader2, blockHeader3), null);
ReceiptsMessage receiptsMessage =
ReceiptsMessage.create(
List.of(
List.of(receiptForBlock1),
List.of(receiptForBlock2),
List.of(receiptForBlock3),
List.of(
new TransactionReceipt(1, 101112, Collections.emptyList(), Optional.empty()))));
Assertions.assertThrows(
InvalidPeerTaskResponseException.class, () -> task.processResponse(receiptsMessage));
}
@Test
public void testParseResponse() throws InvalidPeerTaskResponseException {
BlockHeader blockHeader1 = mockBlockHeader(1);
TransactionReceipt receiptForBlock1 =
new TransactionReceipt(1, 123, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader1.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock1)));
BlockHeader blockHeader2 = mockBlockHeader(2);
TransactionReceipt receiptForBlock2 =
new TransactionReceipt(1, 456, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader2.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock2)));
BlockHeader blockHeader3 = mockBlockHeader(3);
TransactionReceipt receiptForBlock3 =
new TransactionReceipt(1, 789, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader3.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock3)));
BlockHeader blockHeader4 = mockBlockHeader(4);
Mockito.when(blockHeader4.getReceiptsRoot()).thenReturn(Hash.EMPTY_TRIE_HASH);
GetReceiptsFromPeerTask task =
new GetReceiptsFromPeerTask(
List.of(blockHeader1, blockHeader2, blockHeader3, blockHeader4), null);
ReceiptsMessage receiptsMessage =
ReceiptsMessage.create(
List.of(
List.of(receiptForBlock1), List.of(receiptForBlock2), List.of(receiptForBlock3)));
Map<BlockHeader, List<TransactionReceipt>> resultMap = task.processResponse(receiptsMessage);
Assertions.assertEquals(4, resultMap.size());
Assertions.assertEquals(Collections.emptyList(), resultMap.get(blockHeader4));
Assertions.assertEquals(List.of(receiptForBlock1), resultMap.get(blockHeader1));
Assertions.assertEquals(List.of(receiptForBlock2), resultMap.get(blockHeader2));
Assertions.assertEquals(List.of(receiptForBlock3), resultMap.get(blockHeader3));
}
@Test
public void testParseResponseForOnlyPrefilledEmptyTrieReceiptsRoots()
throws InvalidPeerTaskResponseException {
BlockHeader blockHeader1 = mockBlockHeader(1);
Mockito.when(blockHeader1.getReceiptsRoot()).thenReturn(Hash.EMPTY_TRIE_HASH);
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(List.of(blockHeader1), null);
ReceiptsMessage receiptsMessage = ReceiptsMessage.create(Collections.emptyList());
Map<BlockHeader, List<TransactionReceipt>> resultMap = task.processResponse(receiptsMessage);
Assertions.assertEquals(1, resultMap.size());
Assertions.assertEquals(Collections.emptyList(), resultMap.get(blockHeader1));
}
@Test
public void testGetPeerRequirementFilter() {
BlockHeader blockHeader1 = mockBlockHeader(1);
TransactionReceipt receiptForBlock1 =
new TransactionReceipt(1, 123, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader1.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock1)));
BlockHeader blockHeader2 = mockBlockHeader(2);
TransactionReceipt receiptForBlock2 =
new TransactionReceipt(1, 456, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader2.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock2)));
BlockHeader blockHeader3 = mockBlockHeader(3);
TransactionReceipt receiptForBlock3 =
new TransactionReceipt(1, 789, Collections.emptyList(), Optional.empty());
Mockito.when(blockHeader3.getReceiptsRoot())
.thenReturn(BodyValidation.receiptsRoot(List.of(receiptForBlock3)));
ProtocolSchedule protocolSchedule = Mockito.mock(ProtocolSchedule.class);
Mockito.when(protocolSchedule.anyMatch(Mockito.any())).thenReturn(false);
GetReceiptsFromPeerTask task =
new GetReceiptsFromPeerTask(
List.of(blockHeader1, blockHeader2, blockHeader3), protocolSchedule);
EthPeer failForIncorrectProtocol = mockPeer("incorrectProtocol", 5);
EthPeer failForShortChainHeight = mockPeer("incorrectProtocol", 1);
EthPeer successfulCandidate = mockPeer(EthProtocol.NAME, 5);
Assertions.assertFalse(task.getPeerRequirementFilter().test(failForIncorrectProtocol));
Assertions.assertFalse(task.getPeerRequirementFilter().test(failForShortChainHeight));
Assertions.assertTrue(task.getPeerRequirementFilter().test(successfulCandidate));
}
@Test
public void testIsSuccessForPartialSuccess() {
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(Collections.emptyList(), null);
Assertions.assertFalse(task.isSuccess(Collections.emptyMap()));
}
@Test
public void testIsSuccessForFullSuccess() {
GetReceiptsFromPeerTask task = new GetReceiptsFromPeerTask(Collections.emptyList(), null);
Map<BlockHeader, List<TransactionReceipt>> map = new HashMap<>();
map.put(mockBlockHeader(1), null);
Assertions.assertTrue(task.isSuccess(map));
}
private BlockHeader mockBlockHeader(final long blockNumber) {
BlockHeader blockHeader = Mockito.mock(BlockHeader.class);
Mockito.when(blockHeader.getNumber()).thenReturn(blockNumber);
// second to last hex digit indicates the blockNumber, last hex digit indicates the usage of the
// hash
Mockito.when(blockHeader.getHash())
.thenReturn(Hash.fromHexString(StringUtils.repeat("00", 31) + blockNumber + "1"));
return blockHeader;
}
private EthPeer mockPeer(final String protocol, final long chainHeight) {
EthPeer ethPeer = Mockito.mock(EthPeer.class);
ChainState chainState = Mockito.mock(ChainState.class);
Mockito.when(ethPeer.getProtocolName()).thenReturn(protocol);
Mockito.when(ethPeer.chainState()).thenReturn(chainState);
Mockito.when(chainState.getEstimatedHeight()).thenReturn(chainHeight);
return ethPeer;
}
}

@ -22,13 +22,19 @@ import static org.mockito.Mockito.when;
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,8 +50,15 @@ import org.hyperledger.besu.metrics.SyncDurationMetrics;
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.concurrent.ExecutionException;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import org.junit.jupiter.api.AfterEach;
@ -55,12 +68,16 @@ import org.junit.jupiter.params.ParameterizedTest;
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 {
protected ProtocolSchedule protocolSchedule;
protected EthProtocolManager ethProtocolManager;
protected EthContext ethContext;
private PeerTaskExecutor peerTaskExecutor;
protected ProtocolContext protocolContext;
private SyncState syncState;
@ -100,6 +117,7 @@ public class CheckPointSyncChainDownloaderTest {
localBlockchain = localBlockchainSetup.getBlockchain();
otherBlockchainSetup = BlockchainSetupUtil.forTesting(dataStorageFormat);
otherBlockchain = otherBlockchainSetup.getBlockchain();
otherBlockchainSetup.importFirstBlocks(30);
protocolSchedule = localBlockchainSetup.getProtocolSchedule();
protocolContext = localBlockchainSetup.getProtocolContext();
ethProtocolManager =
@ -123,6 +141,41 @@ public class CheckPointSyncChainDownloaderTest {
ethContext.getEthPeers(),
true,
Optional.of(checkpoint));
peerTaskExecutor = mock(PeerTaskExecutor.class);
when(peerTaskExecutor.execute(any(GetReceiptsFromPeerTask.class)))
.thenAnswer(
new Answer<PeerTaskExecutorResult<Map<BlockHeader, List<TransactionReceipt>>>>() {
@Override
public PeerTaskExecutorResult<Map<BlockHeader, List<TransactionReceipt>>> answer(
final InvocationOnMock invocationOnMock) throws Throwable {
GetReceiptsFromPeerTask task =
invocationOnMock.getArgument(0, GetReceiptsFromPeerTask.class);
return processTask(task);
}
});
}
@SuppressWarnings("unchecked")
private PeerTaskExecutorResult<Map<BlockHeader, List<TransactionReceipt>>> processTask(
final GetReceiptsFromPeerTask task) throws IllegalAccessException {
Map<BlockHeader, List<TransactionReceipt>> getReceiptsFromPeerTaskResult = new HashMap<>();
List<Field> fields =
ReflectionUtils.findFields(
task.getClass(),
(field) -> field.getName().equals("blockHeaders"),
ReflectionUtils.HierarchyTraversalMode.TOP_DOWN);
fields.forEach((f) -> f.setAccessible(true));
Collection<BlockHeader> blockHeaders = (Collection<BlockHeader>) fields.getFirst().get(task);
blockHeaders.forEach(
(bh) ->
getReceiptsFromPeerTaskResult.put(
bh, otherBlockchain.getTxReceipts(bh.getHash()).get()));
return new PeerTaskExecutorResult<>(
Optional.of(getReceiptsFromPeerTaskResult), PeerTaskExecutorResponseCode.SUCCESS);
}
@AfterEach
@ -140,6 +193,7 @@ public class CheckPointSyncChainDownloaderTest {
protocolSchedule,
protocolContext,
ethContext,
peerTaskExecutor,
syncState,
new NoOpMetricsSystem(),
new FastSyncState(otherBlockchain.getBlockHeader(pivotBlockNumber).get()),
@ -148,9 +202,9 @@ public class CheckPointSyncChainDownloaderTest {
@ParameterizedTest
@ArgumentsSource(CheckPointSyncChainDownloaderTestArguments.class)
public void shouldSyncToPivotBlockInMultipleSegments(final DataStorageFormat storageFormat) {
public void shouldSyncToPivotBlockInMultipleSegments(final DataStorageFormat storageFormat)
throws IllegalAccessException {
setup(storageFormat);
otherBlockchainSetup.importFirstBlocks(30);
final RespondingEthPeer peer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain);
@ -161,6 +215,7 @@ public class CheckPointSyncChainDownloaderTest {
SynchronizerConfiguration.builder()
.downloaderChainSegmentSize(5)
.downloaderHeadersRequestSize(3)
.isPeerTaskSystemEnabled(false)
.build();
final long pivotBlockNumber = 25;
ethContext
@ -184,9 +239,9 @@ public class CheckPointSyncChainDownloaderTest {
@ParameterizedTest
@ArgumentsSource(CheckPointSyncChainDownloaderTestArguments.class)
public void shouldSyncToPivotBlockInSingleSegment(final DataStorageFormat storageFormat) {
public void shouldSyncToPivotBlockInSingleSegment(final DataStorageFormat storageFormat)
throws IllegalAccessException {
setup(storageFormat);
otherBlockchainSetup.importFirstBlocks(30);
final RespondingEthPeer peer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain);
@ -194,7 +249,79 @@ public class CheckPointSyncChainDownloaderTest {
RespondingEthPeer.blockchainResponder(otherBlockchain);
final long pivotBlockNumber = 10;
final SynchronizerConfiguration syncConfig = SynchronizerConfiguration.builder().build();
final SynchronizerConfiguration syncConfig =
SynchronizerConfiguration.builder().isPeerTaskSystemEnabled(false).build();
ethContext
.getEthPeers()
.streamAvailablePeers()
.forEach(
ethPeer -> {
ethPeer.setCheckpointHeader(
otherBlockchainSetup.getBlocks().get((int) checkpoint.blockNumber()).getHeader());
});
final ChainDownloader downloader = downloader(syncConfig, pivotBlockNumber);
final CompletableFuture<Void> result = downloader.start();
peer.respondWhileOtherThreadsWork(responder, () -> !result.isDone());
assertThat(result).isCompleted();
assertThat(localBlockchain.getChainHeadBlockNumber()).isEqualTo(pivotBlockNumber);
assertThat(localBlockchain.getChainHeadHeader())
.isEqualTo(otherBlockchain.getBlockHeader(pivotBlockNumber).get());
}
@ParameterizedTest
@ArgumentsSource(CheckPointSyncChainDownloaderTestArguments.class)
public void shouldSyncToPivotBlockInMultipleSegmentsWithPeerTaskSystem(
final DataStorageFormat storageFormat)
throws IllegalAccessException, ExecutionException, InterruptedException, TimeoutException {
setup(storageFormat);
final RespondingEthPeer peer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain);
final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(otherBlockchain);
final SynchronizerConfiguration syncConfig =
SynchronizerConfiguration.builder()
.downloaderChainSegmentSize(5)
.downloaderHeadersRequestSize(3)
.isPeerTaskSystemEnabled(true)
.build();
final long pivotBlockNumber = 25;
ethContext
.getEthPeers()
.streamAvailablePeers()
.forEach(
ethPeer -> {
ethPeer.setCheckpointHeader(
otherBlockchainSetup.getBlocks().get((int) checkpoint.blockNumber()).getHeader());
});
final ChainDownloader downloader = downloader(syncConfig, pivotBlockNumber);
final CompletableFuture<Void> result = downloader.start();
peer.respondWhileOtherThreadsWork(responder, () -> !result.isDone());
assertThat(result).isCompleted();
assertThat(localBlockchain.getChainHeadBlockNumber()).isEqualTo(pivotBlockNumber);
assertThat(localBlockchain.getChainHeadHeader())
.isEqualTo(otherBlockchain.getBlockHeader(pivotBlockNumber).get());
}
@ParameterizedTest
@ArgumentsSource(CheckPointSyncChainDownloaderTestArguments.class)
public void shouldSyncToPivotBlockInSingleSegmentWithPeerTaskSystem(
final DataStorageFormat storageFormat) throws IllegalAccessException {
setup(storageFormat);
final RespondingEthPeer peer =
EthProtocolManagerTestUtil.createPeer(ethProtocolManager, otherBlockchain);
final RespondingEthPeer.Responder responder =
RespondingEthPeer.blockchainResponder(otherBlockchain);
final long pivotBlockNumber = 10;
final SynchronizerConfiguration syncConfig =
SynchronizerConfiguration.builder().isPeerTaskSystemEnabled(true).build();
ethContext
.getEthPeers()
.streamAvailablePeers()

@ -18,47 +18,64 @@ import static java.util.Arrays.asList;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mock;
import org.hyperledger.besu.datatypes.Hash;
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.BlockBody;
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.Transaction;
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.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.SynchronizerConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.TransactionPool;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
public class DownloadReceiptsStepTest {
private static ProtocolContext protocolContext;
private static ProtocolSchedule protocolSchedule;
private static MutableBlockchain blockchain;
private PeerTaskExecutor peerTaskExecutor;
private EthProtocolManager ethProtocolManager;
private DownloadReceiptsStep downloadReceiptsStep;
@BeforeAll
public static void setUpClass() {
final BlockchainSetupUtil setupUtil = BlockchainSetupUtil.forTesting(DataStorageFormat.FOREST);
setupUtil.importFirstBlocks(20);
protocolContext = setupUtil.getProtocolContext();
protocolSchedule = setupUtil.getProtocolSchedule();
blockchain = setupUtil.getBlockchain();
}
@BeforeEach
public void setUp() {
peerTaskExecutor = mock(PeerTaskExecutor.class);
TransactionPool transactionPool = mock(TransactionPool.class);
ethProtocolManager =
EthProtocolManagerTestUtil.create(
@ -68,12 +85,17 @@ public class DownloadReceiptsStepTest {
protocolContext.getWorldStateArchive(),
transactionPool,
EthProtocolConfiguration.defaultConfig());
downloadReceiptsStep =
new DownloadReceiptsStep(ethProtocolManager.ethContext(), new NoOpMetricsSystem());
}
@Test
public void shouldDownloadReceiptsForBlocks() {
DownloadReceiptsStep downloadReceiptsStep =
new DownloadReceiptsStep(
protocolSchedule,
ethProtocolManager.ethContext(),
peerTaskExecutor,
SynchronizerConfiguration.builder().isPeerTaskSystemEnabled(false).build(),
new NoOpMetricsSystem());
final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000);
final List<Block> blocks = asList(block(1), block(2), block(3), block(4));
@ -90,6 +112,39 @@ public class DownloadReceiptsStepTest {
blockWithReceipts(4)));
}
@Test
public void shouldDownloadReceiptsForBlocksUsingPeerTaskSystem()
throws ExecutionException, InterruptedException {
DownloadReceiptsStep downloadReceiptsStep =
new DownloadReceiptsStep(
protocolSchedule,
ethProtocolManager.ethContext(),
peerTaskExecutor,
SynchronizerConfiguration.builder().isPeerTaskSystemEnabled(true).build(),
new NoOpMetricsSystem());
final List<Block> blocks = asList(mockBlock(), mockBlock(), mockBlock(), mockBlock());
Map<BlockHeader, List<TransactionReceipt>> receiptsMap = new HashMap<>();
blocks.forEach(
(b) -> receiptsMap.put(b.getHeader(), List.of(Mockito.mock(TransactionReceipt.class))));
PeerTaskExecutorResult<Map<BlockHeader, List<TransactionReceipt>>> peerTaskResult =
new PeerTaskExecutorResult<>(
Optional.of(receiptsMap), PeerTaskExecutorResponseCode.SUCCESS);
Mockito.when(peerTaskExecutor.execute(Mockito.any(GetReceiptsFromPeerTask.class)))
.thenReturn(peerTaskResult);
final CompletableFuture<List<BlockWithReceipts>> result = downloadReceiptsStep.apply(blocks);
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 Block block(final long number) {
final BlockHeader header = blockchain.getBlockHeader(number).get();
return new Block(header, blockchain.getBlockBody(header.getHash()).get());
@ -100,4 +155,16 @@ public class DownloadReceiptsStepTest {
final List<TransactionReceipt> receipts = blockchain.getTxReceipts(block.getHash()).get();
return new BlockWithReceipts(block, receipts);
}
private Block mockBlock() {
final Block block = Mockito.mock(Block.class);
final BlockHeader blockHeader = Mockito.mock(BlockHeader.class);
Mockito.when(block.getHeader()).thenAnswer((invocationOnMock) -> blockHeader);
Mockito.when(blockHeader.getReceiptsRoot()).thenReturn(Hash.fromHexStringLenient("DEADBEEF"));
final BlockBody blockBody = Mockito.mock(BlockBody.class);
Mockito.when(block.getBody()).thenAnswer((invocationOnMock) -> blockBody);
Mockito.when(blockBody.getTransactions())
.thenAnswer((invocationOnMock) -> List.of(Mockito.mock(Transaction.class)));
return block;
}
}

@ -25,6 +25,7 @@ import static org.mockito.Mockito.when;
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;
@ -71,6 +72,7 @@ public class FastDownloaderFactoryTest {
@Mock private ProtocolContext protocolContext;
@Mock private MetricsSystem metricsSystem;
@Mock private EthContext ethContext;
@Mock private PeerTaskExecutor peerTaskExecutor;
@Mock private SyncState syncState;
@Mock private Clock clock;
@Mock private Path dataDirectory;
@ -114,6 +116,7 @@ public class FastDownloaderFactoryTest {
protocolContext,
metricsSystem,
ethContext,
peerTaskExecutor,
worldStateStorageCoordinator,
syncState,
clock,
@ -139,6 +142,7 @@ public class FastDownloaderFactoryTest {
protocolContext,
metricsSystem,
ethContext,
peerTaskExecutor,
worldStateStorageCoordinator,
syncState,
clock,
@ -167,6 +171,7 @@ public class FastDownloaderFactoryTest {
protocolContext,
metricsSystem,
ethContext,
peerTaskExecutor,
worldStateStorageCoordinator,
syncState,
clock,
@ -202,6 +207,7 @@ public class FastDownloaderFactoryTest {
protocolContext,
metricsSystem,
ethContext,
peerTaskExecutor,
worldStateStorageCoordinator,
syncState,
clock,
@ -239,6 +245,7 @@ public class FastDownloaderFactoryTest {
protocolContext,
metricsSystem,
ethContext,
peerTaskExecutor,
worldStateStorageCoordinator,
syncState,
clock,

@ -34,6 +34,7 @@ import org.hyperledger.besu.ethereum.eth.manager.EthPeers;
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;
@ -536,6 +537,7 @@ public class FastSyncActionsTest {
protocolSchedule,
protocolContext,
ethContext,
new PeerTaskExecutor(null, null, new NoOpMetricsSystem()),
new SyncState(blockchain, ethContext.getEthPeers(), true, Optional.empty()),
pivotBlockSelector,
new NoOpMetricsSystem());

@ -29,6 +29,7 @@ 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.messages.EthPV62;
import org.hyperledger.besu.ethereum.eth.messages.GetBlockHeadersMessage;
import org.hyperledger.besu.ethereum.eth.sync.ChainDownloader;
@ -110,6 +111,7 @@ public class FastSyncChainDownloaderTest {
protocolSchedule,
protocolContext,
ethContext,
new PeerTaskExecutor(null, null, new NoOpMetricsSystem()),
syncState,
new NoOpMetricsSystem(),
new FastSyncState(otherBlockchain.getBlockHeader(pivotBlockNumber).get()),

Loading…
Cancel
Save