diff --git a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/tasks/DownloadHeaderSequenceTaskTest.java b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/tasks/DownloadHeaderSequenceTaskTest.java index 55a73b6b24..107f7b7d86 100644 --- a/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/tasks/DownloadHeaderSequenceTaskTest.java +++ b/ethereum/eth/src/test/java/tech/pegasys/pantheon/ethereum/eth/sync/tasks/DownloadHeaderSequenceTaskTest.java @@ -12,12 +12,29 @@ */ package tech.pegasys.pantheon.ethereum.eth.sync.tasks; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + import tech.pegasys.pantheon.ethereum.core.BlockHeader; +import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManagerTestUtil; import tech.pegasys.pantheon.ethereum.eth.manager.EthTask; +import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer; +import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer.Responder; import tech.pegasys.pantheon.ethereum.eth.manager.ethtaskutils.RetryingMessageTaskTest; +import tech.pegasys.pantheon.ethereum.eth.manager.exceptions.MaxRetriesReachedException; +import tech.pegasys.pantheon.ethereum.eth.messages.BlockHeadersMessage; +import tech.pegasys.pantheon.ethereum.eth.messages.EthPV62; +import tech.pegasys.pantheon.ethereum.p2p.api.MessageData; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import com.google.common.collect.Streams; +import org.junit.Test; public class DownloadHeaderSequenceTaskTest extends RetryingMessageTaskTest> { @@ -44,4 +61,62 @@ protected EthTask> createTask(final List requeste requestedData.size(), maxRetries); } + + @Test + public void failsWhenPeerReturnsOnlyReferenceHeader() { + final RespondingEthPeer respondingPeer = + EthProtocolManagerTestUtil.createPeer(ethProtocolManager); + + // Execute task and wait for response + BlockHeader referenceHeader = blockchain.getChainHeadHeader(); + final EthTask> task = + DownloadHeaderSequenceTask.endingAtHeader( + protocolSchedule, protocolContext, ethContext, referenceHeader, 10, maxRetries); + final CompletableFuture> future = task.run(); + + // Respond with only the reference header + final Responder responder = + (cap, message) -> + Optional.of(BlockHeadersMessage.create(Collections.singletonList(referenceHeader))); + respondingPeer.respondWhile(responder, () -> !future.isDone()); + + assertThat(future.isDone()).isTrue(); + assertThat(future.isCompletedExceptionally()).isTrue(); + assertThatThrownBy(future::get).hasCauseInstanceOf(MaxRetriesReachedException.class); + } + + @Test + public void failsWhenPeerReturnsOnlySubsetOfHeaders() { + final RespondingEthPeer respondingPeer = + EthProtocolManagerTestUtil.createPeer(ethProtocolManager); + + // Execute task and wait for response + BlockHeader referenceHeader = blockchain.getChainHeadHeader(); + final EthTask> task = + DownloadHeaderSequenceTask.endingAtHeader( + protocolSchedule, protocolContext, ethContext, referenceHeader, 10, maxRetries); + final CompletableFuture> future = task.run(); + + // Filter response to include only reference header and previous header + final Responder fullResponder = RespondingEthPeer.blockchainResponder(blockchain); + final Responder responder = + (cap, message) -> { + Optional fullResponse = fullResponder.respond(cap, message); + if (!fullResponse.isPresent() || message.getCode() != EthPV62.GET_BLOCK_HEADERS) { + return fullResponse; + } + BlockHeadersMessage headersMessage = BlockHeadersMessage.readFrom(fullResponse.get()); + // Filter for a subset of headers + List headerSubset = + Streams.stream(headersMessage.getHeaders(protocolSchedule)) + .filter(h -> h.getNumber() >= referenceHeader.getNumber() - 1L) + .collect(Collectors.toList()); + return Optional.of(BlockHeadersMessage.create(headerSubset)); + }; + respondingPeer.respondTimes(responder, 100); + + assertThat(future.isDone()).isTrue(); + assertThat(future.isCompletedExceptionally()).isTrue(); + assertThatThrownBy(future::get).hasCauseInstanceOf(MaxRetriesReachedException.class); + } } diff --git a/ethereum/eth/src/test/resources/log4j2.xml b/ethereum/eth/src/test/resources/log4j2.xml new file mode 100644 index 0000000000..af05117015 --- /dev/null +++ b/ethereum/eth/src/test/resources/log4j2.xml @@ -0,0 +1,16 @@ + + + + INFO + + + + + + + + + + + +