mirror of https://github.com/hyperledger/besu
PAN-2765 Implement dump command to dump a specific block from storage (#1641)
* add dump sub command * add getBlockHash method for the dump sub command * Resolve review issues : Add the ability to export multiple blocks, Rename dump command to export command, Improve tests for the export command Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>pull/2/head
parent
b4ee866cf6
commit
6412c4e028
@ -0,0 +1,88 @@ |
||||
/* |
||||
* 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,111 @@ |
||||
/* |
||||
* 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