mirror of https://github.com/hyperledger/besu
[PIE-1810] Update export subcommand to export blocks in rlp format (#1852)
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
a5daeba71d
commit
f9e50ae8c6
@ -0,0 +1,94 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2018 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.chainexport; |
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.ethereum.chain.Blockchain; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Block; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Hash; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
import org.apache.logging.log4j.LogManager; |
||||||
|
import org.apache.logging.log4j.Logger; |
||||||
|
|
||||||
|
/** Pantheon Block Export Util. */ |
||||||
|
public abstract class BlockExporter { |
||||||
|
private static final Logger LOG = LogManager.getLogger(); |
||||||
|
private final Blockchain blockchain; |
||||||
|
|
||||||
|
protected BlockExporter(final Blockchain blockchain) { |
||||||
|
this.blockchain = blockchain; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Export blocks that are stored in Pantheon's block storage. |
||||||
|
* |
||||||
|
* @param outputFile the path at which to save the exported block data |
||||||
|
* @param maybeStartBlock the starting index of the block list to export (inclusive) |
||||||
|
* @param maybeEndBlock the ending index of the block list to export (exclusive), if not specified |
||||||
|
* a single block will be export |
||||||
|
* @throws IOException if an I/O error occurs while writing data to disk |
||||||
|
*/ |
||||||
|
public void exportBlocks( |
||||||
|
final File outputFile, |
||||||
|
final Optional<Long> maybeStartBlock, |
||||||
|
final Optional<Long> maybeEndBlock) |
||||||
|
throws IOException { |
||||||
|
|
||||||
|
// Get range to export
|
||||||
|
final long startBlock = maybeStartBlock.orElse(BlockHeader.GENESIS_BLOCK_NUMBER); |
||||||
|
final long endBlock = maybeEndBlock.orElse(blockchain.getChainHeadBlockNumber() + 1L); |
||||||
|
checkArgument(startBlock >= 0 && endBlock >= 0, "Start and end blocks must be greater than 0."); |
||||||
|
checkArgument(startBlock < endBlock, "Start block must be less than end block"); |
||||||
|
|
||||||
|
// Append to file if a range is specified
|
||||||
|
final boolean append = maybeStartBlock.isPresent(); |
||||||
|
FileOutputStream outputStream = new FileOutputStream(outputFile, append); |
||||||
|
|
||||||
|
LOG.info( |
||||||
|
"Exporting blocks [{},{}) to file {} (appending: {})", |
||||||
|
startBlock, |
||||||
|
endBlock, |
||||||
|
outputFile.toString(), |
||||||
|
Boolean.toString(append)); |
||||||
|
|
||||||
|
long blockNumber = 0L; |
||||||
|
for (long i = startBlock; i < endBlock; i++) { |
||||||
|
Optional<Hash> blockHash = blockchain.getBlockHashByNumber(i); |
||||||
|
if (blockHash.isEmpty()) { |
||||||
|
LOG.warn("Unable to export blocks [{} - {}). Blocks not found.", i, endBlock); |
||||||
|
break; |
||||||
|
} |
||||||
|
|
||||||
|
final Block block = blockchain.getBlockByHash(blockHash.get()); |
||||||
|
blockNumber = block.getHeader().getNumber(); |
||||||
|
if (blockNumber % 100 == 0) { |
||||||
|
LOG.info("Export at block {}", blockNumber); |
||||||
|
} |
||||||
|
|
||||||
|
exportBlock(outputStream, block); |
||||||
|
} |
||||||
|
|
||||||
|
outputStream.close(); |
||||||
|
LOG.info("Export complete at block {}", blockNumber); |
||||||
|
} |
||||||
|
|
||||||
|
protected abstract void exportBlock(final FileOutputStream outputStream, final Block block) |
||||||
|
throws IOException; |
||||||
|
} |
@ -0,0 +1,35 @@ |
|||||||
|
/* |
||||||
|
* 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.chainexport; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.ethereum.chain.Blockchain; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Block; |
||||||
|
import tech.pegasys.pantheon.ethereum.rlp.RLP; |
||||||
|
import tech.pegasys.pantheon.util.bytes.BytesValue; |
||||||
|
|
||||||
|
import java.io.FileOutputStream; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
public class RlpBlockExporter extends BlockExporter { |
||||||
|
|
||||||
|
public RlpBlockExporter(final Blockchain blockchain) { |
||||||
|
super(blockchain); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void exportBlock(final FileOutputStream outputStream, final Block block) |
||||||
|
throws IOException { |
||||||
|
final BytesValue rlp = RLP.encode(block::writeTo); |
||||||
|
outputStream.write(rlp.getArrayUnsafe()); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,17 @@ |
|||||||
|
/* |
||||||
|
* 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.cli.subcommands.blocks; |
||||||
|
|
||||||
|
public enum BlockExportFormat { |
||||||
|
RLP |
||||||
|
} |
@ -1,88 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2018 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.util; |
|
||||||
|
|
||||||
import tech.pegasys.pantheon.controller.PantheonController; |
|
||||||
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.Hash; |
|
||||||
|
|
||||||
import java.util.ArrayList; |
|
||||||
import java.util.List; |
|
||||||
import java.util.Optional; |
|
||||||
|
|
||||||
import com.google.common.base.MoreObjects; |
|
||||||
|
|
||||||
/** Pantheon Block Export Util. */ |
|
||||||
public class BlockExporter { |
|
||||||
|
|
||||||
/** |
|
||||||
* Export blocks that are stored in Pantheon's block storage. |
|
||||||
* |
|
||||||
* @param pantheonController the PantheonController that defines blockchain behavior |
|
||||||
* @param <C> the consensus context type |
|
||||||
* @param startBlock the starting index of the block list to export (inclusive) |
|
||||||
* @param endBlock the ending index of the block list to export (exclusive), if not specified a |
|
||||||
* single block will be export |
|
||||||
* @return the export result |
|
||||||
*/ |
|
||||||
public <C> ExportResult exportBlockchain( |
|
||||||
final PantheonController<C> pantheonController, final Long startBlock, final Long endBlock) { |
|
||||||
|
|
||||||
final ProtocolContext<C> context = pantheonController.getProtocolContext(); |
|
||||||
final MutableBlockchain blockchain = context.getBlockchain(); |
|
||||||
|
|
||||||
final Long sanitizedEndBlock = sanitizedEndBlockIndex(startBlock, endBlock); |
|
||||||
|
|
||||||
final List<Block> blocks = new ArrayList<>(); |
|
||||||
for (long currentBlockIndex = startBlock; |
|
||||||
currentBlockIndex < sanitizedEndBlock; |
|
||||||
currentBlockIndex += 1) { |
|
||||||
Optional<Hash> blockHashByNumber = blockchain.getBlockHashByNumber(currentBlockIndex); |
|
||||||
blockHashByNumber.ifPresent(hash -> blocks.add(blockchain.getBlockByHash(hash))); |
|
||||||
} |
|
||||||
|
|
||||||
final boolean allBlocksAreFound = blocks.size() == (sanitizedEndBlock - startBlock); |
|
||||||
|
|
||||||
return new ExportResult(blocks, allBlocksAreFound); |
|
||||||
} |
|
||||||
|
|
||||||
private Long sanitizedEndBlockIndex(final Long startBlock, final Long endBlock) { |
|
||||||
if (endBlock == null) { |
|
||||||
return startBlock + 1; |
|
||||||
} else { |
|
||||||
return endBlock; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
public static final class ExportResult { |
|
||||||
|
|
||||||
public final List<Block> blocks; |
|
||||||
|
|
||||||
public final boolean allBlocksAreFound; |
|
||||||
|
|
||||||
ExportResult(final List<Block> blocks, final boolean allBlocksAreFound) { |
|
||||||
this.blocks = blocks; |
|
||||||
this.allBlocksAreFound = allBlocksAreFound; |
|
||||||
} |
|
||||||
|
|
||||||
@Override |
|
||||||
public String toString() { |
|
||||||
return MoreObjects.toStringHelper(this) |
|
||||||
.add("blocks", blocks) |
|
||||||
.add("allBlocksAreFound", allBlocksAreFound) |
|
||||||
.toString(); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -0,0 +1,245 @@ |
|||||||
|
/* |
||||||
|
* Copyright 2018 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.chainexport; |
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat; |
||||||
|
import static org.assertj.core.api.Assertions.assertThatThrownBy; |
||||||
|
|
||||||
|
import tech.pegasys.pantheon.chainimport.RlpBlockImporter; |
||||||
|
import tech.pegasys.pantheon.config.GenesisConfigFile; |
||||||
|
import tech.pegasys.pantheon.controller.PantheonController; |
||||||
|
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair; |
||||||
|
import tech.pegasys.pantheon.ethereum.chain.Blockchain; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.Block; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockBody; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.BlockHeader; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder; |
||||||
|
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; |
||||||
|
import tech.pegasys.pantheon.ethereum.eth.EthProtocolConfiguration; |
||||||
|
import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; |
||||||
|
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPoolConfiguration; |
||||||
|
import tech.pegasys.pantheon.ethereum.mainnet.ProtocolSchedule; |
||||||
|
import tech.pegasys.pantheon.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; |
||||||
|
import tech.pegasys.pantheon.ethereum.util.RawBlockIterator; |
||||||
|
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; |
||||||
|
import tech.pegasys.pantheon.testutil.BlockTestUtil; |
||||||
|
import tech.pegasys.pantheon.testutil.TestClock; |
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
import java.nio.file.Path; |
||||||
|
import java.util.Optional; |
||||||
|
|
||||||
|
import org.junit.BeforeClass; |
||||||
|
import org.junit.ClassRule; |
||||||
|
import org.junit.Test; |
||||||
|
import org.junit.rules.TemporaryFolder; |
||||||
|
|
||||||
|
/** Tests for {@link BlockExporter}. */ |
||||||
|
public final class RlpBlockExporterTest { |
||||||
|
|
||||||
|
@ClassRule public static final TemporaryFolder folder = new TemporaryFolder(); |
||||||
|
private static Blockchain blockchain; |
||||||
|
private static long chainHead; |
||||||
|
private static ProtocolSchedule<?> protocolSchedule; |
||||||
|
|
||||||
|
@BeforeClass |
||||||
|
public static void setupBlockchain() throws IOException { |
||||||
|
final PantheonController<?> controller = createController(); |
||||||
|
final Path blocks = folder.newFile("1000.blocks").toPath(); |
||||||
|
BlockTestUtil.write1000Blocks(blocks); |
||||||
|
blockchain = importBlocks(controller, blocks); |
||||||
|
chainHead = blockchain.getChainHeadBlockNumber(); |
||||||
|
protocolSchedule = controller.getProtocolSchedule(); |
||||||
|
} |
||||||
|
|
||||||
|
private static Blockchain importBlocks( |
||||||
|
final PantheonController<?> controller, final Path blocksFile) throws IOException { |
||||||
|
final RlpBlockImporter blockImporter = new RlpBlockImporter(); |
||||||
|
|
||||||
|
blockImporter.importBlockchain(blocksFile, controller); |
||||||
|
return controller.getProtocolContext().getBlockchain(); |
||||||
|
} |
||||||
|
|
||||||
|
private static PantheonController<?> createController() throws IOException { |
||||||
|
final Path dataDir = folder.newFolder().toPath(); |
||||||
|
return new PantheonController.Builder() |
||||||
|
.fromGenesisConfig(GenesisConfigFile.mainnet()) |
||||||
|
.synchronizerConfiguration(SynchronizerConfiguration.builder().build()) |
||||||
|
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig()) |
||||||
|
.storageProvider(new InMemoryStorageProvider()) |
||||||
|
.networkId(1) |
||||||
|
.miningParameters(new MiningParametersTestBuilder().enabled(false).build()) |
||||||
|
.nodeKeys(KeyPair.generate()) |
||||||
|
.metricsSystem(new NoOpMetricsSystem()) |
||||||
|
.privacyParameters(PrivacyParameters.DEFAULT) |
||||||
|
.dataDirectory(dataDir) |
||||||
|
.clock(TestClock.fixed()) |
||||||
|
.transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) |
||||||
|
.build(); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exportBlocks_noBounds() throws IOException { |
||||||
|
final File outputPath = folder.newFile(); |
||||||
|
final RlpBlockExporter exporter = new RlpBlockExporter(blockchain); |
||||||
|
exporter.exportBlocks(outputPath, Optional.empty(), Optional.empty()); |
||||||
|
|
||||||
|
// Iterate over blocks and check that they match expectations
|
||||||
|
RawBlockIterator blockIterator = getBlockIterator(outputPath.toPath()); |
||||||
|
long currentBlockNumber = 0; |
||||||
|
while (blockIterator.hasNext()) { |
||||||
|
final Block actual = blockIterator.next(); |
||||||
|
final Block expected = getBlock(blockchain, currentBlockNumber); |
||||||
|
assertThat(actual).isEqualTo(expected); |
||||||
|
currentBlockNumber++; |
||||||
|
} |
||||||
|
|
||||||
|
// Check that we iterated to the end of the chain
|
||||||
|
assertThat(currentBlockNumber).isEqualTo(chainHead + 1L); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exportBlocks_withLowerBound() throws IOException { |
||||||
|
final File outputPath = folder.newFile(); |
||||||
|
final RlpBlockExporter exporter = new RlpBlockExporter(blockchain); |
||||||
|
|
||||||
|
final long lowerBound = 990; |
||||||
|
exporter.exportBlocks(outputPath, Optional.of(lowerBound), Optional.empty()); |
||||||
|
|
||||||
|
// Iterate over blocks and check that they match expectations
|
||||||
|
RawBlockIterator blockIterator = getBlockIterator(outputPath.toPath()); |
||||||
|
long currentBlockNumber = lowerBound; |
||||||
|
while (blockIterator.hasNext()) { |
||||||
|
final Block actual = blockIterator.next(); |
||||||
|
final Block expected = getBlock(blockchain, currentBlockNumber); |
||||||
|
assertThat(actual).isEqualTo(expected); |
||||||
|
currentBlockNumber++; |
||||||
|
} |
||||||
|
|
||||||
|
// Check that we iterated to the end of the chain
|
||||||
|
assertThat(currentBlockNumber).isEqualTo(chainHead + 1L); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exportBlocks_withUpperBound() throws IOException { |
||||||
|
final File outputPath = folder.newFile(); |
||||||
|
final RlpBlockExporter exporter = new RlpBlockExporter(blockchain); |
||||||
|
|
||||||
|
final long upperBound = 10; |
||||||
|
exporter.exportBlocks(outputPath, Optional.empty(), Optional.of(upperBound)); |
||||||
|
|
||||||
|
// Iterate over blocks and check that they match expectations
|
||||||
|
RawBlockIterator blockIterator = getBlockIterator(outputPath.toPath()); |
||||||
|
long currentBlockNumber = 0; |
||||||
|
while (blockIterator.hasNext()) { |
||||||
|
final Block actual = blockIterator.next(); |
||||||
|
final Block expected = getBlock(blockchain, currentBlockNumber); |
||||||
|
assertThat(actual).isEqualTo(expected); |
||||||
|
currentBlockNumber++; |
||||||
|
} |
||||||
|
|
||||||
|
// Check that we iterated to the end of the chain
|
||||||
|
assertThat(currentBlockNumber).isEqualTo(upperBound); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exportBlocks_withUpperAndLowerBounds() throws IOException { |
||||||
|
final File outputPath = folder.newFile(); |
||||||
|
final RlpBlockExporter exporter = new RlpBlockExporter(blockchain); |
||||||
|
|
||||||
|
final long lowerBound = 5; |
||||||
|
final long upperBound = 10; |
||||||
|
exporter.exportBlocks(outputPath, Optional.of(lowerBound), Optional.of(upperBound)); |
||||||
|
|
||||||
|
// Iterate over blocks and check that they match expectations
|
||||||
|
RawBlockIterator blockIterator = getBlockIterator(outputPath.toPath()); |
||||||
|
long currentBlockNumber = lowerBound; |
||||||
|
while (blockIterator.hasNext()) { |
||||||
|
final Block actual = blockIterator.next(); |
||||||
|
final Block expected = getBlock(blockchain, currentBlockNumber); |
||||||
|
assertThat(actual).isEqualTo(expected); |
||||||
|
currentBlockNumber++; |
||||||
|
} |
||||||
|
|
||||||
|
// Check that we iterated to the end of the chain
|
||||||
|
assertThat(currentBlockNumber).isEqualTo(upperBound); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exportBlocks_withRangeBeyondChainHead() throws IOException { |
||||||
|
final File outputPath = folder.newFile(); |
||||||
|
final RlpBlockExporter exporter = new RlpBlockExporter(blockchain); |
||||||
|
|
||||||
|
final long lowerBound = chainHead - 10; |
||||||
|
final long upperBound = chainHead + 10; |
||||||
|
exporter.exportBlocks(outputPath, Optional.of(lowerBound), Optional.of(upperBound)); |
||||||
|
|
||||||
|
// Iterate over blocks and check that they match expectations
|
||||||
|
RawBlockIterator blockIterator = getBlockIterator(outputPath.toPath()); |
||||||
|
long currentBlockNumber = lowerBound; |
||||||
|
while (blockIterator.hasNext()) { |
||||||
|
final Block actual = blockIterator.next(); |
||||||
|
final Block expected = getBlock(blockchain, currentBlockNumber); |
||||||
|
assertThat(actual).isEqualTo(expected); |
||||||
|
currentBlockNumber++; |
||||||
|
} |
||||||
|
|
||||||
|
// Check that we iterated to the end of the chain
|
||||||
|
assertThat(currentBlockNumber).isEqualTo(chainHead + 1L); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exportBlocks_negativeStartNumber() throws IOException { |
||||||
|
final File outputPath = folder.newFile(); |
||||||
|
final RlpBlockExporter exporter = new RlpBlockExporter(blockchain); |
||||||
|
|
||||||
|
assertThatThrownBy(() -> exporter.exportBlocks(outputPath, Optional.of(-1L), Optional.empty())) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessageContaining("greater than 0"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exportBlocks_negativeEndNumber() throws IOException { |
||||||
|
final File outputPath = folder.newFile(); |
||||||
|
final RlpBlockExporter exporter = new RlpBlockExporter(blockchain); |
||||||
|
|
||||||
|
assertThatThrownBy(() -> exporter.exportBlocks(outputPath, Optional.empty(), Optional.of(-1L))) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessageContaining("greater than 0"); |
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void exportBlocks_outOfOrderBounds() throws IOException { |
||||||
|
final File outputPath = folder.newFile(); |
||||||
|
final RlpBlockExporter exporter = new RlpBlockExporter(blockchain); |
||||||
|
|
||||||
|
assertThatThrownBy(() -> exporter.exportBlocks(outputPath, Optional.of(10L), Optional.of(2L))) |
||||||
|
.isInstanceOf(IllegalArgumentException.class) |
||||||
|
.hasMessageContaining("Start block must be less than end block"); |
||||||
|
} |
||||||
|
|
||||||
|
private RawBlockIterator getBlockIterator(final Path blocks) throws IOException { |
||||||
|
return new RawBlockIterator( |
||||||
|
blocks, |
||||||
|
rlp -> |
||||||
|
BlockHeader.readFrom(rlp, ScheduleBasedBlockHeaderFunctions.create(protocolSchedule))); |
||||||
|
} |
||||||
|
|
||||||
|
private Block getBlock(final Blockchain blockchain, final long blockNumber) { |
||||||
|
final BlockHeader header = blockchain.getBlockHeader(blockNumber).get(); |
||||||
|
final BlockBody body = blockchain.getBlockBody(header.getHash()).get(); |
||||||
|
return new Block(header, body); |
||||||
|
} |
||||||
|
} |
@ -1,111 +0,0 @@ |
|||||||
/* |
|
||||||
* Copyright 2018 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.util; |
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat; |
|
||||||
|
|
||||||
import tech.pegasys.pantheon.config.GenesisConfigFile; |
|
||||||
import tech.pegasys.pantheon.controller.PantheonController; |
|
||||||
import tech.pegasys.pantheon.crypto.SECP256K1; |
|
||||||
import tech.pegasys.pantheon.ethereum.chain.MutableBlockchain; |
|
||||||
import tech.pegasys.pantheon.ethereum.core.Block; |
|
||||||
import tech.pegasys.pantheon.ethereum.core.InMemoryStorageProvider; |
|
||||||
import tech.pegasys.pantheon.ethereum.core.MiningParametersTestBuilder; |
|
||||||
import tech.pegasys.pantheon.ethereum.core.PrivacyParameters; |
|
||||||
import tech.pegasys.pantheon.ethereum.eth.EthProtocolConfiguration; |
|
||||||
import tech.pegasys.pantheon.ethereum.eth.sync.SynchronizerConfiguration; |
|
||||||
import tech.pegasys.pantheon.ethereum.eth.transactions.TransactionPoolConfiguration; |
|
||||||
import tech.pegasys.pantheon.metrics.noop.NoOpMetricsSystem; |
|
||||||
import tech.pegasys.pantheon.testutil.BlockTestUtil; |
|
||||||
import tech.pegasys.pantheon.testutil.TestClock; |
|
||||||
|
|
||||||
import java.io.IOException; |
|
||||||
import java.nio.file.Path; |
|
||||||
|
|
||||||
import org.junit.BeforeClass; |
|
||||||
import org.junit.ClassRule; |
|
||||||
import org.junit.Test; |
|
||||||
import org.junit.rules.TemporaryFolder; |
|
||||||
|
|
||||||
/** Tests for {@link BlockExporter}. */ |
|
||||||
public final class BlockExporterTest { |
|
||||||
|
|
||||||
@ClassRule public static final TemporaryFolder folder = new TemporaryFolder(); |
|
||||||
|
|
||||||
private static PantheonController<?> targetController; |
|
||||||
|
|
||||||
private final BlockExporter blockExporter = new BlockExporter(); |
|
||||||
|
|
||||||
@BeforeClass |
|
||||||
public static void initPantheonController() throws IOException { |
|
||||||
final BlockImporter blockImporter = new BlockImporter(); |
|
||||||
final Path dataDir = folder.newFolder().toPath(); |
|
||||||
final Path source = dataDir.resolve("1000.blocks"); |
|
||||||
BlockTestUtil.write1000Blocks(source); |
|
||||||
targetController = |
|
||||||
new PantheonController.Builder() |
|
||||||
.fromGenesisConfig(GenesisConfigFile.mainnet()) |
|
||||||
.synchronizerConfiguration(SynchronizerConfiguration.builder().build()) |
|
||||||
.ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig()) |
|
||||||
.storageProvider(new InMemoryStorageProvider()) |
|
||||||
.networkId(1) |
|
||||||
.miningParameters(new MiningParametersTestBuilder().enabled(false).build()) |
|
||||||
.nodeKeys(SECP256K1.KeyPair.generate()) |
|
||||||
.metricsSystem(new NoOpMetricsSystem()) |
|
||||||
.privacyParameters(PrivacyParameters.DEFAULT) |
|
||||||
.dataDirectory(dataDir) |
|
||||||
.clock(TestClock.fixed()) |
|
||||||
.transactionPoolConfiguration(TransactionPoolConfiguration.builder().build()) |
|
||||||
.build(); |
|
||||||
blockImporter.importBlockchain(source, targetController); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void callingBlockExporterWithOnlyStartBlockShouldReturnOneBlock() throws Exception { |
|
||||||
|
|
||||||
final long startBlock = 0L; |
|
||||||
|
|
||||||
final MutableBlockchain blockchain = targetController.getProtocolContext().getBlockchain(); |
|
||||||
|
|
||||||
final Block blockFromBlockchain = |
|
||||||
blockchain.getBlockByHash(blockchain.getBlockHashByNumber(startBlock).get()); |
|
||||||
|
|
||||||
BlockExporter.ExportResult exportResult = |
|
||||||
blockExporter.exportBlockchain(targetController, startBlock, null); |
|
||||||
|
|
||||||
assertThat(exportResult.blocks).contains(blockFromBlockchain); |
|
||||||
} |
|
||||||
|
|
||||||
@Test |
|
||||||
public void callingBlockExporterWithStartBlockAndBlockShouldReturnSeveralBlocks() |
|
||||||
throws Exception { |
|
||||||
|
|
||||||
final long startBlock = 0L; |
|
||||||
final long endBlock = 1L; |
|
||||||
|
|
||||||
final MutableBlockchain blockchain = targetController.getProtocolContext().getBlockchain(); |
|
||||||
|
|
||||||
final Block blockFromBlockchain = |
|
||||||
blockchain.getBlockByHash(blockchain.getBlockHashByNumber(startBlock).get()); |
|
||||||
|
|
||||||
final Block secondBlockFromBlockchain = |
|
||||||
blockchain.getBlockByHash(blockchain.getBlockHashByNumber(endBlock - 1).get()); |
|
||||||
|
|
||||||
BlockExporter.ExportResult exportResult = |
|
||||||
blockExporter.exportBlockchain(targetController, startBlock, endBlock); |
|
||||||
|
|
||||||
assertThat(exportResult.blocks) |
|
||||||
.contains(blockFromBlockchain) |
|
||||||
.contains(secondBlockFromBlockchain); |
|
||||||
} |
|
||||||
} |
|
Loading…
Reference in new issue