mirror of https://github.com/hyperledger/besu
Pipeline chain download - fetch and import data (#1207)
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
362219d908
commit
bac2828313
@ -0,0 +1,66 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.exceptions.InvalidBlockException; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||
|
||||
import java.util.function.Function; |
||||
import java.util.stream.Stream; |
||||
|
||||
public class CheckpointHeaderValidationStep<C> |
||||
implements Function<CheckpointRangeHeaders, Stream<BlockHeader>> { |
||||
|
||||
private final ProtocolSchedule<C> protocolSchedule; |
||||
private final ProtocolContext<C> protocolContext; |
||||
private final ValidationPolicy validationPolicy; |
||||
|
||||
public CheckpointHeaderValidationStep( |
||||
final ProtocolSchedule<C> protocolSchedule, |
||||
final ProtocolContext<C> protocolContext, |
||||
final ValidationPolicy validationPolicy) { |
||||
this.protocolSchedule = protocolSchedule; |
||||
this.protocolContext = protocolContext; |
||||
this.validationPolicy = validationPolicy; |
||||
} |
||||
|
||||
@Override |
||||
public Stream<BlockHeader> apply(final CheckpointRangeHeaders checkpointRangeHeaders) { |
||||
final BlockHeader expectedParent = checkpointRangeHeaders.getCheckpointRange().getStart(); |
||||
final BlockHeader firstHeaderToImport = checkpointRangeHeaders.getFirstHeaderToImport(); |
||||
|
||||
if (isValid(expectedParent, firstHeaderToImport)) { |
||||
return checkpointRangeHeaders.getHeadersToImport().stream(); |
||||
} else { |
||||
throw new InvalidBlockException( |
||||
"Provided first header does not connect to last header.", |
||||
expectedParent.getNumber(), |
||||
expectedParent.getHash()); |
||||
} |
||||
} |
||||
|
||||
private boolean isValid(final BlockHeader expectedParent, final BlockHeader firstHeaderToImport) { |
||||
final BlockHeaderValidator<C> validator = |
||||
protocolSchedule |
||||
.getByBlockNumber(firstHeaderToImport.getNumber()) |
||||
.getBlockHeaderValidator(); |
||||
return validator.validateHeader( |
||||
firstHeaderToImport, |
||||
expectedParent, |
||||
protocolContext, |
||||
validationPolicy.getValidationModeForNextBlock()); |
||||
} |
||||
} |
@ -0,0 +1,72 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync; |
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||
|
||||
import java.util.List; |
||||
import java.util.Objects; |
||||
|
||||
import com.google.common.base.MoreObjects; |
||||
|
||||
public class CheckpointRangeHeaders { |
||||
private final CheckpointRange checkpointRange; |
||||
private final List<BlockHeader> headersToImport; |
||||
|
||||
public CheckpointRangeHeaders( |
||||
final CheckpointRange checkpointRange, final List<BlockHeader> headersToImport) { |
||||
checkArgument(!headersToImport.isEmpty(), "Must have at least one header to import"); |
||||
this.checkpointRange = checkpointRange; |
||||
this.headersToImport = headersToImport; |
||||
} |
||||
|
||||
public CheckpointRange getCheckpointRange() { |
||||
return checkpointRange; |
||||
} |
||||
|
||||
public List<BlockHeader> getHeadersToImport() { |
||||
return headersToImport; |
||||
} |
||||
|
||||
public BlockHeader getFirstHeaderToImport() { |
||||
return headersToImport.get(0); |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(final Object o) { |
||||
if (this == o) { |
||||
return true; |
||||
} |
||||
if (o == null || getClass() != o.getClass()) { |
||||
return false; |
||||
} |
||||
final CheckpointRangeHeaders that = (CheckpointRangeHeaders) o; |
||||
return Objects.equals(checkpointRange, that.checkpointRange) |
||||
&& Objects.equals(headersToImport, that.headersToImport); |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
return Objects.hash(checkpointRange, headersToImport); |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
return MoreObjects.toStringHelper(this) |
||||
.add("checkpointRange", checkpointRange) |
||||
.add("headersToImport", headersToImport) |
||||
.toString(); |
||||
} |
||||
} |
@ -0,0 +1,47 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.Block; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.CompleteBlocksTask; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||
import tech.pegasys.pantheon.metrics.MetricsSystem; |
||||
|
||||
import java.util.List; |
||||
import java.util.concurrent.CompletableFuture; |
||||
import java.util.function.Function; |
||||
|
||||
public class DownloadBodiesStep<C> |
||||
implements Function<List<BlockHeader>, CompletableFuture<List<Block>>> { |
||||
|
||||
private final ProtocolSchedule<C> protocolSchedule; |
||||
private final EthContext ethContext; |
||||
private final MetricsSystem metricsSystem; |
||||
|
||||
public DownloadBodiesStep( |
||||
final ProtocolSchedule<C> protocolSchedule, |
||||
final EthContext ethContext, |
||||
final MetricsSystem metricsSystem) { |
||||
this.protocolSchedule = protocolSchedule; |
||||
this.ethContext = ethContext; |
||||
this.metricsSystem = metricsSystem; |
||||
} |
||||
|
||||
@Override |
||||
public CompletableFuture<List<Block>> apply(final List<BlockHeader> blockHeaders) { |
||||
return CompleteBlocksTask.forHeaders(protocolSchedule, ethContext, blockHeaders, metricsSystem) |
||||
.run(); |
||||
} |
||||
} |
@ -0,0 +1,79 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.DownloadHeaderSequenceTask; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||
import tech.pegasys.pantheon.metrics.MetricsSystem; |
||||
import tech.pegasys.pantheon.util.FutureUtils; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.concurrent.CompletableFuture; |
||||
import java.util.function.Function; |
||||
|
||||
public class DownloadHeadersStep<C> |
||||
implements Function<CheckpointRange, CompletableFuture<CheckpointRangeHeaders>> { |
||||
|
||||
private final ProtocolSchedule<C> protocolSchedule; |
||||
private final ProtocolContext<C> protocolContext; |
||||
private final EthContext ethContext; |
||||
private final ValidationPolicy validationPolicy; |
||||
private final MetricsSystem metricsSystem; |
||||
|
||||
public DownloadHeadersStep( |
||||
final ProtocolSchedule<C> protocolSchedule, |
||||
final ProtocolContext<C> protocolContext, |
||||
final EthContext ethContext, |
||||
final ValidationPolicy validationPolicy, |
||||
final MetricsSystem metricsSystem) { |
||||
this.protocolSchedule = protocolSchedule; |
||||
this.protocolContext = protocolContext; |
||||
this.ethContext = ethContext; |
||||
this.validationPolicy = validationPolicy; |
||||
this.metricsSystem = metricsSystem; |
||||
} |
||||
|
||||
@Override |
||||
public CompletableFuture<CheckpointRangeHeaders> apply(final CheckpointRange checkpointRange) { |
||||
final CompletableFuture<List<BlockHeader>> taskFuture = downloadHeaders(checkpointRange); |
||||
final CompletableFuture<CheckpointRangeHeaders> processedFuture = |
||||
taskFuture.thenApply(headers -> processHeaders(checkpointRange, headers)); |
||||
FutureUtils.propagateCancellation(processedFuture, taskFuture); |
||||
return processedFuture; |
||||
} |
||||
|
||||
private CompletableFuture<List<BlockHeader>> downloadHeaders( |
||||
final CheckpointRange checkpointRange) { |
||||
return DownloadHeaderSequenceTask.endingAtHeader( |
||||
protocolSchedule, |
||||
protocolContext, |
||||
ethContext, |
||||
checkpointRange.getEnd(), |
||||
// -1 because we don't want to request the range starting header
|
||||
checkpointRange.getSegmentLength() - 1, |
||||
validationPolicy, |
||||
metricsSystem) |
||||
.run(); |
||||
} |
||||
|
||||
private CheckpointRangeHeaders processHeaders( |
||||
final CheckpointRange checkpointRange, final List<BlockHeader> headers) { |
||||
final List<BlockHeader> headersToImport = new ArrayList<>(headers); |
||||
headersToImport.add(checkpointRange.getEnd()); |
||||
return new CheckpointRangeHeaders(checkpointRange, headersToImport); |
||||
} |
||||
} |
@ -0,0 +1,64 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync.fastsync; |
||||
|
||||
import static java.util.Collections.emptyList; |
||||
import static java.util.stream.Collectors.toList; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.core.Block; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||
import tech.pegasys.pantheon.ethereum.core.TransactionReceipt; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthContext; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.GetReceiptsForHeadersTask; |
||||
import tech.pegasys.pantheon.metrics.MetricsSystem; |
||||
import tech.pegasys.pantheon.util.FutureUtils; |
||||
|
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.concurrent.CompletableFuture; |
||||
import java.util.function.Function; |
||||
|
||||
public class DownloadReceiptsStep |
||||
implements Function<List<Block>, CompletableFuture<List<BlockWithReceipts>>> { |
||||
private final EthContext ethContext; |
||||
private final MetricsSystem metricsSystem; |
||||
|
||||
public DownloadReceiptsStep(final EthContext ethContext, final MetricsSystem metricsSystem) { |
||||
this.ethContext = ethContext; |
||||
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; |
||||
} |
||||
|
||||
private List<BlockWithReceipts> combineBlocksAndReceipts( |
||||
final List<Block> blocks, final Map<BlockHeader, List<TransactionReceipt>> receiptsByHeader) { |
||||
return blocks.stream() |
||||
.map( |
||||
block -> { |
||||
final List<TransactionReceipt> receipts = |
||||
receiptsByHeader.getOrDefault(block.getHeader(), emptyList()); |
||||
return new BlockWithReceipts(block, receipts); |
||||
}) |
||||
.collect(toList()); |
||||
} |
||||
} |
@ -0,0 +1,60 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync.fastsync; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockImporter; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.ValidationPolicy; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.exceptions.InvalidBlockException; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||
|
||||
import java.util.List; |
||||
import java.util.function.Consumer; |
||||
|
||||
public class FastImportBlocksStep<C> implements Consumer<List<BlockWithReceipts>> { |
||||
|
||||
private final ProtocolSchedule<C> protocolSchedule; |
||||
private final ProtocolContext<C> protocolContext; |
||||
private final ValidationPolicy validationPolicy; |
||||
|
||||
public FastImportBlocksStep( |
||||
final ProtocolSchedule<C> protocolSchedule, |
||||
final ProtocolContext<C> protocolContext, |
||||
final ValidationPolicy validationPolicy) { |
||||
this.protocolSchedule = protocolSchedule; |
||||
this.protocolContext = protocolContext; |
||||
this.validationPolicy = validationPolicy; |
||||
} |
||||
|
||||
@Override |
||||
public void accept(final List<BlockWithReceipts> blocksWithReceipts) { |
||||
for (final BlockWithReceipts blockWithReceipts : blocksWithReceipts) { |
||||
if (!importBlock(blockWithReceipts)) { |
||||
throw new InvalidBlockException( |
||||
"Failed to import block", |
||||
blockWithReceipts.getHeader().getNumber(), |
||||
blockWithReceipts.getHash()); |
||||
} |
||||
} |
||||
} |
||||
|
||||
private boolean importBlock(final BlockWithReceipts blockWithReceipts) { |
||||
final BlockImporter<C> importer = |
||||
protocolSchedule.getByBlockNumber(blockWithReceipts.getNumber()).getBlockImporter(); |
||||
return importer.fastImportBlock( |
||||
protocolContext, |
||||
blockWithReceipts.getBlock(), |
||||
blockWithReceipts.getReceipts(), |
||||
validationPolicy.getValidationModeForNextBlock()); |
||||
} |
||||
} |
@ -0,0 +1,91 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync; |
||||
|
||||
import static java.util.Arrays.asList; |
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.anyLong; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.verifyNoMoreInteractions; |
||||
import static org.mockito.Mockito.when; |
||||
import static tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode.DETACHED_ONLY; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockDataGenerator; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.exceptions.InvalidBlockException; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec; |
||||
|
||||
import java.util.stream.Stream; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class CheckpointHeaderValidationStepTest { |
||||
@Mock private ProtocolSchedule<Void> protocolSchedule; |
||||
@Mock private ProtocolSpec<Void> protocolSpec; |
||||
@Mock private ProtocolContext<Void> protocolContext; |
||||
@Mock private BlockHeaderValidator<Void> headerValidator; |
||||
@Mock private ValidationPolicy validationPolicy; |
||||
private final BlockDataGenerator gen = new BlockDataGenerator(); |
||||
private CheckpointHeaderValidationStep<Void> validationStep; |
||||
|
||||
private final BlockHeader checkpointStart = gen.header(10); |
||||
private final BlockHeader firstHeader = gen.header(11); |
||||
private final CheckpointRangeHeaders rangeHeaders = |
||||
new CheckpointRangeHeaders( |
||||
new CheckpointRange(checkpointStart, gen.header(13)), |
||||
asList(firstHeader, gen.header(12), gen.header(13))); |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
when(protocolSchedule.getByBlockNumber(anyLong())).thenReturn(protocolSpec); |
||||
when(protocolSpec.getBlockHeaderValidator()).thenReturn(headerValidator); |
||||
when(validationPolicy.getValidationModeForNextBlock()).thenReturn(DETACHED_ONLY); |
||||
|
||||
validationStep = |
||||
new CheckpointHeaderValidationStep<>(protocolSchedule, protocolContext, validationPolicy); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldValidateFirstHeaderAgainstCheckpointStartHeader() { |
||||
when(headerValidator.validateHeader( |
||||
firstHeader, checkpointStart, protocolContext, DETACHED_ONLY)) |
||||
.thenReturn(true); |
||||
final Stream<BlockHeader> result = validationStep.apply(rangeHeaders); |
||||
|
||||
verify(protocolSchedule).getByBlockNumber(firstHeader.getNumber()); |
||||
verify(validationPolicy).getValidationModeForNextBlock(); |
||||
verify(headerValidator) |
||||
.validateHeader(firstHeader, checkpointStart, protocolContext, DETACHED_ONLY); |
||||
verifyNoMoreInteractions(headerValidator, validationPolicy); |
||||
|
||||
assertThat(result).containsExactlyElementsOf(rangeHeaders.getHeadersToImport()); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldThrowExceptionWhenValidationFails() { |
||||
when(headerValidator.validateHeader( |
||||
firstHeader, checkpointStart, protocolContext, DETACHED_ONLY)) |
||||
.thenReturn(false); |
||||
assertThatThrownBy(() -> validationStep.apply(rangeHeaders)) |
||||
.isInstanceOf(InvalidBlockException.class); |
||||
} |
||||
} |
@ -0,0 +1,107 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync; |
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManager; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManagerTestUtil; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.ethtaskutils.BlockchainSetupUtil; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collections; |
||||
import java.util.List; |
||||
import java.util.concurrent.CompletableFuture; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.BeforeClass; |
||||
import org.junit.Test; |
||||
|
||||
public class DownloadHeadersStepTest { |
||||
|
||||
private static ProtocolSchedule<Void> protocolSchedule; |
||||
private static ProtocolContext<Void> protocolContext; |
||||
private static MutableBlockchain blockchain; |
||||
|
||||
private EthProtocolManager ethProtocolManager; |
||||
private DownloadHeadersStep<Void> downloader; |
||||
private CheckpointRange checkpointRange; |
||||
|
||||
@BeforeClass |
||||
public static void setUpClass() { |
||||
final BlockchainSetupUtil<Void> setupUtil = BlockchainSetupUtil.forTesting(); |
||||
setupUtil.importFirstBlocks(20); |
||||
protocolSchedule = setupUtil.getProtocolSchedule(); |
||||
protocolContext = setupUtil.getProtocolContext(); |
||||
blockchain = protocolContext.getBlockchain(); |
||||
} |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
ethProtocolManager = |
||||
EthProtocolManagerTestUtil.create(blockchain, protocolContext.getWorldStateArchive()); |
||||
downloader = |
||||
new DownloadHeadersStep<>( |
||||
protocolSchedule, |
||||
protocolContext, |
||||
ethProtocolManager.ethContext(), |
||||
() -> HeaderValidationMode.DETACHED_ONLY, |
||||
new NoOpMetricsSystem()); |
||||
|
||||
checkpointRange = |
||||
new CheckpointRange( |
||||
blockchain.getBlockHeader(1).get(), blockchain.getBlockHeader(10).get()); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldRetrieveHeadersForCheckpointRange() { |
||||
final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); |
||||
final CompletableFuture<CheckpointRangeHeaders> result = downloader.apply(checkpointRange); |
||||
|
||||
peer.respond(RespondingEthPeer.blockchainResponder(blockchain)); |
||||
|
||||
// The start of the range should have been imported as part of the previous batch hence 2-10.
|
||||
assertThat(result) |
||||
.isCompletedWithValue(new CheckpointRangeHeaders(checkpointRange, headersFromChain(2, 10))); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldCancelRequestToPeerWhenReturnedFutureIsCancelled() { |
||||
final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); |
||||
|
||||
final CompletableFuture<CheckpointRangeHeaders> result = this.downloader.apply(checkpointRange); |
||||
|
||||
result.cancel(true); |
||||
|
||||
EthProtocolManagerTestUtil.disableEthSchedulerAutoRun(ethProtocolManager); |
||||
|
||||
peer.respond(RespondingEthPeer.blockchainResponder(blockchain)); |
||||
|
||||
assertThat(EthProtocolManagerTestUtil.getPendingFuturesCount(ethProtocolManager)).isZero(); |
||||
} |
||||
|
||||
private List<BlockHeader> headersFromChain(final long startNumber, final long endNumber) { |
||||
final List<BlockHeader> headers = new ArrayList<>(); |
||||
for (long i = startNumber; i <= endNumber; i++) { |
||||
headers.add(blockchain.getBlockHeader(i).get()); |
||||
} |
||||
return Collections.unmodifiableList(headers); |
||||
} |
||||
} |
@ -0,0 +1,88 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync.fastsync; |
||||
|
||||
import static java.util.Arrays.asList; |
||||
import static org.assertj.core.api.Assertions.assertThat; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; |
||||
import tech.pegasys.pantheon.ethereum.core.Block; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||
import tech.pegasys.pantheon.ethereum.core.TransactionReceipt; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManager; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.EthProtocolManagerTestUtil; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.RespondingEthPeer; |
||||
import tech.pegasys.pantheon.ethereum.eth.manager.ethtaskutils.BlockchainSetupUtil; |
||||
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; |
||||
|
||||
import java.util.List; |
||||
import java.util.concurrent.CompletableFuture; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.BeforeClass; |
||||
import org.junit.Test; |
||||
|
||||
public class DownloadReceiptsStepTest { |
||||
|
||||
private static ProtocolContext<Void> protocolContext; |
||||
private static MutableBlockchain blockchain; |
||||
|
||||
private EthProtocolManager ethProtocolManager; |
||||
private DownloadReceiptsStep downloadReceiptsStep; |
||||
|
||||
@BeforeClass |
||||
public static void setUpClass() { |
||||
final BlockchainSetupUtil<Void> setupUtil = BlockchainSetupUtil.forTesting(); |
||||
setupUtil.importFirstBlocks(20); |
||||
protocolContext = setupUtil.getProtocolContext(); |
||||
blockchain = setupUtil.getBlockchain(); |
||||
} |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
ethProtocolManager = |
||||
EthProtocolManagerTestUtil.create(blockchain, protocolContext.getWorldStateArchive()); |
||||
downloadReceiptsStep = |
||||
new DownloadReceiptsStep(ethProtocolManager.ethContext(), new NoOpMetricsSystem()); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldDownloadReceiptsForBlocks() { |
||||
final RespondingEthPeer peer = EthProtocolManagerTestUtil.createPeer(ethProtocolManager, 1000); |
||||
|
||||
final List<Block> blocks = asList(block(1), block(2), block(3), block(4)); |
||||
final CompletableFuture<List<BlockWithReceipts>> result = downloadReceiptsStep.apply(blocks); |
||||
|
||||
peer.respond(RespondingEthPeer.blockchainResponder(blockchain)); |
||||
|
||||
assertThat(result) |
||||
.isCompletedWithValue( |
||||
asList( |
||||
blockWithReceipts(1), |
||||
blockWithReceipts(2), |
||||
blockWithReceipts(3), |
||||
blockWithReceipts(4))); |
||||
} |
||||
|
||||
private Block block(final long number) { |
||||
final BlockHeader header = blockchain.getBlockHeader(number).get(); |
||||
return new Block(header, blockchain.getBlockBody(header.getHash()).get()); |
||||
} |
||||
|
||||
private BlockWithReceipts blockWithReceipts(final long number) { |
||||
final Block block = block(number); |
||||
final List<TransactionReceipt> receipts = blockchain.getTxReceipts(block.getHash()).get(); |
||||
return new BlockWithReceipts(block, receipts); |
||||
} |
||||
} |
@ -0,0 +1,95 @@ |
||||
/* |
||||
* Copyright 2019 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. |
||||
*/ |
||||
package tech.pegasys.pantheon.ethereum.eth.sync.fastsync; |
||||
|
||||
import static java.util.Collections.singletonList; |
||||
import static java.util.stream.Collectors.toList; |
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||
import static org.mockito.ArgumentMatchers.anyLong; |
||||
import static org.mockito.Mockito.times; |
||||
import static org.mockito.Mockito.verify; |
||||
import static org.mockito.Mockito.when; |
||||
import static tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode.FULL; |
||||
|
||||
import tech.pegasys.pantheon.ethereum.ProtocolContext; |
||||
import tech.pegasys.pantheon.ethereum.core.Block; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockDataGenerator; |
||||
import tech.pegasys.pantheon.ethereum.core.BlockImporter; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.ValidationPolicy; |
||||
import tech.pegasys.pantheon.ethereum.eth.sync.tasks.exceptions.InvalidBlockException; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSpec; |
||||
|
||||
import java.util.List; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.junit.runner.RunWith; |
||||
import org.mockito.Mock; |
||||
import org.mockito.junit.MockitoJUnitRunner; |
||||
|
||||
@RunWith(MockitoJUnitRunner.class) |
||||
public class FastImportBlocksStepTest { |
||||
|
||||
@Mock private ProtocolSchedule<Void> protocolSchedule; |
||||
@Mock private ProtocolSpec<Void> protocolSpec; |
||||
@Mock private ProtocolContext<Void> protocolContext; |
||||
@Mock private BlockImporter<Void> blockImporter; |
||||
@Mock private ValidationPolicy validationPolicy; |
||||
private final BlockDataGenerator gen = new BlockDataGenerator(); |
||||
|
||||
private FastImportBlocksStep<Void> importBlocksStep; |
||||
|
||||
@Before |
||||
public void setUp() { |
||||
when(protocolSchedule.getByBlockNumber(anyLong())).thenReturn(protocolSpec); |
||||
when(protocolSpec.getBlockImporter()).thenReturn(blockImporter); |
||||
when(validationPolicy.getValidationModeForNextBlock()).thenReturn(FULL); |
||||
|
||||
importBlocksStep = |
||||
new FastImportBlocksStep<>(protocolSchedule, protocolContext, validationPolicy); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldImportBlocks() { |
||||
final List<Block> blocks = gen.blockSequence(5); |
||||
final List<BlockWithReceipts> blocksWithReceipts = |
||||
blocks.stream() |
||||
.map(block -> new BlockWithReceipts(block, gen.receipts(block))) |
||||
.collect(toList()); |
||||
|
||||
for (final BlockWithReceipts blockWithReceipts : blocksWithReceipts) { |
||||
when(blockImporter.fastImportBlock( |
||||
protocolContext, blockWithReceipts.getBlock(), blockWithReceipts.getReceipts(), FULL)) |
||||
.thenReturn(true); |
||||
} |
||||
importBlocksStep.accept(blocksWithReceipts); |
||||
|
||||
for (final BlockWithReceipts blockWithReceipts : blocksWithReceipts) { |
||||
verify(protocolSchedule).getByBlockNumber(blockWithReceipts.getNumber()); |
||||
} |
||||
verify(validationPolicy, times(blocks.size())).getValidationModeForNextBlock(); |
||||
} |
||||
|
||||
@Test |
||||
public void shouldThrowExceptionWhenValidationFails() { |
||||
final Block block = gen.block(); |
||||
final BlockWithReceipts blockWithReceipts = new BlockWithReceipts(block, gen.receipts(block)); |
||||
|
||||
when(blockImporter.fastImportBlock( |
||||
protocolContext, block, blockWithReceipts.getReceipts(), FULL)) |
||||
.thenReturn(false); |
||||
assertThatThrownBy(() -> importBlocksStep.accept(singletonList(blockWithReceipts))) |
||||
.isInstanceOf(InvalidBlockException.class); |
||||
} |
||||
} |
Loading…
Reference in new issue