use of tech.pegasys.teku.networking.eth2.peers.Eth2Peer in project teku by ConsenSys.
the class FetchBlockTask method run.
public SafeFuture<FetchBlockResult> run() {
if (cancelled.get()) {
return SafeFuture.completedFuture(FetchBlockResult.createFailed(Status.CANCELLED));
}
final Optional<Eth2Peer> maybePeer = eth2Network.streamPeers().filter(p -> !queriedPeers.contains(p.getId())).min(Comparator.comparing(Eth2Peer::getOutstandingRequests).thenComparing(SHUFFLING_COMPARATOR));
if (maybePeer.isEmpty()) {
return SafeFuture.completedFuture(FetchBlockResult.createFailed(Status.NO_AVAILABLE_PEERS));
}
final Eth2Peer peer = maybePeer.get();
numberOfRuns.incrementAndGet();
queriedPeers.add(peer.getId());
return peer.requestBlockByRoot(blockRoot).thenApply(maybeBlock -> maybeBlock.map(FetchBlockResult::createSuccessful).orElseGet(() -> FetchBlockResult.createFailed(Status.FETCH_FAILED))).exceptionally(err -> {
LOG.debug("Failed to fetch block " + blockRoot, err);
return FetchBlockResult.createFailed(Status.FETCH_FAILED);
});
}
use of tech.pegasys.teku.networking.eth2.peers.Eth2Peer in project teku by ConsenSys.
the class SyncManagerTest method sync_retrySyncIfNotSuccessful.
@Test
void sync_retrySyncIfNotSuccessful() {
when(network.streamPeers()).thenReturn(Stream.of(peer));
final SafeFuture<PeerSyncResult> syncFuture = new SafeFuture<>();
when(peerSync.sync(peer)).thenReturn(syncFuture);
assertThat(syncManager.start()).isCompleted();
assertThat(syncManager.isSyncActive()).isTrue();
assertThat(syncManager.isSyncQueued()).isFalse();
verify(peerSync).sync(peer);
// The sync didn't complete correctly so we should start a new one with a new peer
final Eth2Peer peer2 = mock(Eth2Peer.class);
when(peer2.getStatus()).thenReturn(peerStatus);
when(network.streamPeers()).thenReturn(Stream.of(peer2));
when(peerSync.sync(peer2)).thenReturn(new SafeFuture<>());
syncFuture.complete(PeerSyncResult.FAULTY_ADVERTISEMENT);
asyncRunner.executeQueuedActions();
verify(peerSync).sync(peer2);
assertThat(syncManager.isSyncActive()).isTrue();
assertThat(syncManager.isSyncQueued()).isFalse();
}
use of tech.pegasys.teku.networking.eth2.peers.Eth2Peer in project teku by ConsenSys.
the class PeerSync method executeSync.
@SuppressWarnings("FutureReturnValueIgnored")
private SafeFuture<PeerSyncResult> executeSync(final Eth2Peer peer, final UInt64 startSlot, final SafeFuture<Void> readyForRequest, final boolean findCommonAncestor) {
if (stopped.get()) {
return SafeFuture.completedFuture(PeerSyncResult.CANCELLED);
}
final PeerStatus status = peer.getStatus();
final UInt64 count = calculateNumberOfBlocksToRequest(startSlot, status);
if (count.longValue() == 0) {
return completeSyncWithPeer(peer, status);
}
return readyForRequest.thenCompose(__ -> {
if (!findCommonAncestor) {
return SafeFuture.completedFuture(startSlot);
}
CommonAncestor ancestor = new CommonAncestor(storageClient);
return ancestor.getCommonAncestor(peer, startSlot, status.getHeadSlot());
}).thenCompose((ancestorStartSlot) -> {
if (findCommonAncestor) {
LOG.trace("Start sync from slot {}, instead of {}", ancestorStartSlot, startSlot);
}
LOG.debug("Request {} blocks starting at {} from peer {}", count, ancestorStartSlot, peer.getId());
final SafeFuture<Void> readyForNextRequest = asyncRunner.getDelayedFuture(NEXT_REQUEST_TIMEOUT);
final PeerSyncBlockRequest request = new PeerSyncBlockRequest(readyForNextRequest, ancestorStartSlot.plus(count), this::blockResponseListener);
return peer.requestBlocksByRange(ancestorStartSlot, count, STEP, request).thenApply((res) -> request);
}).thenCompose((blockRequest) -> {
final UInt64 nextSlot = blockRequest.getActualEndSlot().plus(UInt64.ONE);
LOG.trace("Completed request for {} slots from peer {}. Next request starts from {}", count, peer.getId(), nextSlot);
if (count.compareTo(MIN_SLOTS_TO_PROGRESS_PER_REQUEST) > 0 && startSlot.plus(MIN_SLOTS_TO_PROGRESS_PER_REQUEST).compareTo(nextSlot) > 0) {
final int throttledRequests = throttledRequestCount.incrementAndGet();
LOG.debug("Received {} consecutive excessively throttled response from {}", throttledRequests, peer.getId());
if (throttledRequests > MAX_THROTTLED_REQUESTS) {
LOG.debug("Rejecting peer {} as sync target because it excessively throttled returned blocks", peer.getId());
return SafeFuture.completedFuture(PeerSyncResult.EXCESSIVE_THROTTLING);
}
} else {
throttledRequestCount.set(0);
}
return executeSync(peer, nextSlot, blockRequest.getReadyForNextRequest(), false);
}).exceptionally(err -> handleFailedRequestToPeer(peer, status, err));
}
use of tech.pegasys.teku.networking.eth2.peers.Eth2Peer in project teku by ConsenSys.
the class PeerSync method handleFailedRequestToPeer.
private PeerSyncResult handleFailedRequestToPeer(Eth2Peer peer, final PeerStatus peerStatus, Throwable err) {
Throwable rootException = Throwables.getRootCause(err);
if (rootException instanceof FailedBlockImportException) {
final FailedBlockImportException importException = (FailedBlockImportException) rootException;
final FailureReason reason = importException.getResult().getFailureReason();
final SignedBeaconBlock block = importException.getBlock();
if (reason.equals(FailureReason.UNKNOWN_PARENT) && !hasPeerFinalizedBlock(block, peerStatus)) {
// We received a block that doesn't connect to our chain.
// This can happen if our peer is sending us blocks from the non-final portion of their
// chain. They may be sending us blocks from a stale fork that we have already pruned out of
// our Store.
LOG.debug("Failed to import non-final block from peer (err: {}) {}: {}", reason, block, peer);
return PeerSyncResult.IMPORT_FAILED;
} else if (BAD_BLOCK_FAILURE_REASONS.contains(reason)) {
LOG.warn("Failed to import block from peer (err: {}) {}: {}", reason, block, peer);
LOG.debug("Disconnecting from peer ({}) who sent invalid block ({}): {}", peer, reason.name(), block);
disconnectFromPeer(peer);
return PeerSyncResult.BAD_BLOCK;
} else {
LOG.warn("Failed to import block from peer (err: {}) {}: {}", reason, block, peer);
return PeerSyncResult.IMPORT_FAILED;
}
}
if (rootException instanceof CancellationException) {
return PeerSyncResult.CANCELLED;
}
if (rootException instanceof BlocksByRangeResponseInvalidResponseException || rootException instanceof RpcException) {
disconnectFromPeer(peer);
return PeerSyncResult.INVALID_RESPONSE;
}
if (err instanceof RuntimeException) {
throw (RuntimeException) err;
} else {
throw new RuntimeException("Unhandled error while syncing", err);
}
}
use of tech.pegasys.teku.networking.eth2.peers.Eth2Peer in project teku by ConsenSys.
the class SyncManager method isPeerSyncSuitable.
private boolean isPeerSyncSuitable(Eth2Peer peer) {
UInt64 ourFinalizedEpoch = storageClient.getFinalizedEpoch();
LOG.trace("Looking for suitable peer (out of {}) with finalized epoch > {}.", network.getPeerCount(), ourFinalizedEpoch);
final PeerStatus peerStatus = peer.getStatus();
return !peersWithSyncErrors.contains(peer.getId()) && peerStatusIsConsistentWithOurNode(peerStatus) && peerIsAheadOfOurNode(peerStatus, ourFinalizedEpoch);
}
Aggregations