diff --git a/.gitattributes b/.gitattributes index 79522fa32..2e408995e 100644 --- a/.gitattributes +++ b/.gitattributes @@ -17,4 +17,5 @@ *.p12 binary *.db binary *.woff2 binary +*.era1 binary goss-linux-amd64 binary diff --git a/CHANGELOG.md b/CHANGELOG.md index d5708b318..fffb59408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ - Update `eth_getLogs` to return a `Block not found` error when the requested block is not found. [#8290](https://github.com/hyperledger/besu/pull/8290) - Change `Invalid block, unable to parse RLP` RPC error message to `Invalid block param (block not found)` [#8328](https://github.com/hyperledger/besu/pull/8328) - Support pending transaction score when saving and restoring txpool [#8363](https://github.com/hyperledger/besu/pull/8363) +- Add era1 format to blocks import subcommand [#7935](https://github.com/hyperledger/besu/issues/7935) ### Bug fixes - Add missing RPC method `debug_accountRange` to `RpcMethod.java` so this method can be used with `--rpc-http-api-method-no-auth` [#8153](https://github.com/hyperledger/besu/issues/8153) diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java index a0dc5b47b..03ff0d1e6 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/ThreadBesuNodeRunner.java @@ -19,6 +19,7 @@ import static org.hyperledger.besu.controller.BesuController.DATABASE_PATH; import org.hyperledger.besu.Runner; import org.hyperledger.besu.RunnerBuilder; import org.hyperledger.besu.chainexport.RlpBlockExporter; +import org.hyperledger.besu.chainimport.Era1BlockImporter; import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.cli.BesuCommand; @@ -632,6 +633,7 @@ public class ThreadBesuNodeRunner implements BesuNodeRunner { new BesuCommand( RlpBlockImporter::new, JsonBlockImporter::new, + Era1BlockImporter::new, RlpBlockExporter::new, new RunnerBuilder(), new BesuController.Builder(), diff --git a/besu/src/main/java/org/hyperledger/besu/chainimport/Era1BlockImporter.java b/besu/src/main/java/org/hyperledger/besu/chainimport/Era1BlockImporter.java new file mode 100644 index 000000000..cead4e229 --- /dev/null +++ b/besu/src/main/java/org/hyperledger/besu/chainimport/Era1BlockImporter.java @@ -0,0 +1,171 @@ +/* + * Copyright contributors to Besu. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.chainimport; + +import org.hyperledger.besu.controller.BesuController; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderFunctions; +import org.hyperledger.besu.ethereum.core.BlockImporter; +import org.hyperledger.besu.ethereum.core.TransactionReceipt; +import org.hyperledger.besu.ethereum.mainnet.BlockImportResult; +import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode; +import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ScheduleBasedBlockHeaderFunctions; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; +import org.hyperledger.besu.ethereum.rlp.RLPInput; +import org.hyperledger.besu.util.era1.Era1BlockIndex; +import org.hyperledger.besu.util.era1.Era1ExecutionBlockBody; +import org.hyperledger.besu.util.era1.Era1ExecutionBlockHeader; +import org.hyperledger.besu.util.era1.Era1ExecutionBlockReceipts; +import org.hyperledger.besu.util.era1.Era1Reader; +import org.hyperledger.besu.util.era1.Era1ReaderListener; +import org.hyperledger.besu.util.snappy.SnappyFactory; + +import java.io.Closeable; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import org.apache.tuweni.bytes.Bytes; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Tool for importing era1-encoded block data, headers, and transaction receipts from era1 files. + */ +public class Era1BlockImporter implements Closeable { + private static final Logger LOG = LoggerFactory.getLogger(Era1BlockImporter.class); + + private static final int ERA1_BLOCK_COUNT_MAX = 8192; + private static final int IMPORT_COUNT_FOR_LOG_UPDATE = 1000; + + /** Default Constructor. */ + public Era1BlockImporter() {} + + /** + * Imports the blocks, headers, and transaction receipts from the file found at the supplied path + * + * @param controller The BesuController + * @param path The path + * @throws IOException IOException + * @throws ExecutionException ExecutionException + * @throws InterruptedException InterruptedException + * @throws TimeoutException TimeoutException + */ + public void importBlocks(final BesuController controller, final Path path) + throws IOException, ExecutionException, InterruptedException, TimeoutException { + final ProtocolSchedule protocolSchedule = controller.getProtocolSchedule(); + final BlockHeaderFunctions blockHeaderFunctions = + ScheduleBasedBlockHeaderFunctions.create(protocolSchedule); + final ProtocolContext context = controller.getProtocolContext(); + + Era1Reader reader = new Era1Reader(new SnappyFactory()); + + final List> headersFutures = new ArrayList<>(ERA1_BLOCK_COUNT_MAX); + final List> bodiesFutures = new ArrayList<>(ERA1_BLOCK_COUNT_MAX); + final List>> receiptsFutures = + new ArrayList<>(ERA1_BLOCK_COUNT_MAX); + reader.read( + new FileInputStream(path.toFile()), + new Era1ReaderListener() { + + @Override + public void handleExecutionBlockHeader( + final Era1ExecutionBlockHeader executionBlockHeader) { + headersFutures.add( + CompletableFuture.supplyAsync( + () -> + BlockHeader.readFrom( + new BytesValueRLPInput( + Bytes.wrap(executionBlockHeader.header()), false), + blockHeaderFunctions))); + } + + @Override + public void handleExecutionBlockBody(final Era1ExecutionBlockBody executionBlockBody) { + bodiesFutures.add( + CompletableFuture.supplyAsync( + () -> + BlockBody.readWrappedBodyFrom( + new BytesValueRLPInput(Bytes.wrap(executionBlockBody.block()), false), + blockHeaderFunctions, + true))); + } + + @Override + public void handleExecutionBlockReceipts( + final Era1ExecutionBlockReceipts executionBlockReceipts) { + receiptsFutures.add( + CompletableFuture.supplyAsync( + () -> { + RLPInput input = + new BytesValueRLPInput( + Bytes.wrap(executionBlockReceipts.receipts()), false); + final List receiptsForBlock = new ArrayList<>(); + input.readList((in) -> receiptsForBlock.add(TransactionReceipt.readFrom(in))); + return receiptsForBlock; + })); + } + + @Override + public void handleBlockIndex(final Era1BlockIndex blockIndex) { + // not really necessary, do nothing + } + }); + + LOG.info("Read {} blocks, now importing", headersFutures.size()); + + Block block = null; + for (int i = 0; i < headersFutures.size(); i++) { + BlockHeader blockHeader = headersFutures.get(i).get(10, TimeUnit.SECONDS); + BlockImporter blockImporter = + protocolSchedule.getByBlockHeader(blockHeader).getBlockImporter(); + block = new Block(blockHeader, bodiesFutures.get(i).get(10, TimeUnit.SECONDS)); + + BlockImportResult importResult = + blockImporter.importBlockForSyncing( + context, + block, + receiptsFutures.get(i).get(10, TimeUnit.SECONDS), + HeaderValidationMode.NONE, + HeaderValidationMode.NONE, + BodyValidationMode.NONE, + false); + if (importResult.getStatus() != BlockImportResult.BlockImportStatus.IMPORTED) { + LOG.warn( + "Failed to import block {} due to {}", + blockHeader.getNumber(), + importResult.getStatus()); + } else if (i % IMPORT_COUNT_FOR_LOG_UPDATE == 0) { + LOG.info("{}/{} blocks imported", i, headersFutures.size()); + } + } + LOG.info("Done importing {} blocks", headersFutures.size()); + } + + @Override + public void close() throws IOException {} +} diff --git a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java index c92a6a9b2..c0a50432c 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/BesuCommand.java @@ -31,6 +31,7 @@ import org.hyperledger.besu.BesuInfo; import org.hyperledger.besu.Runner; import org.hyperledger.besu.RunnerBuilder; import org.hyperledger.besu.chainexport.RlpBlockExporter; +import org.hyperledger.besu.chainimport.Era1BlockImporter; import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.cli.config.EthNetworkConfig; @@ -284,6 +285,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { private final Supplier rlpBlockImporter; private final Function jsonBlockImporterFactory; + private final Supplier era1BlockImporter; private final Function rlpBlockExporterFactory; // Unstable CLI options @@ -719,6 +721,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { * * @param rlpBlockImporter RlpBlockImporter supplier * @param jsonBlockImporterFactory instance of {@code Function} + * @param era1BlockImporter Era1BlockImporter supplier * @param rlpBlockExporterFactory instance of {@code Function} * @param runnerBuilder instance of RunnerBuilder * @param controllerBuilder instance of BesuController.Builder @@ -729,6 +732,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { public BesuCommand( final Supplier rlpBlockImporter, final Function jsonBlockImporterFactory, + final Supplier era1BlockImporter, final Function rlpBlockExporterFactory, final RunnerBuilder runnerBuilder, final BesuController.Builder controllerBuilder, @@ -738,6 +742,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { this( rlpBlockImporter, jsonBlockImporterFactory, + era1BlockImporter, rlpBlockExporterFactory, runnerBuilder, controllerBuilder, @@ -760,6 +765,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { * * @param rlpBlockImporter RlpBlockImporter supplier * @param jsonBlockImporterFactory instance of {@code Function} + * @param era1BlockImporter Era1BlockImporter supplier * @param rlpBlockExporterFactory instance of {@code Function} * @param runnerBuilder instance of RunnerBuilder * @param controllerBuilder instance of BesuController.Builder @@ -780,6 +786,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { protected BesuCommand( final Supplier rlpBlockImporter, final Function jsonBlockImporterFactory, + final Supplier era1BlockImporter, final Function rlpBlockExporterFactory, final RunnerBuilder runnerBuilder, final BesuController.Builder controllerBuilder, @@ -798,8 +805,9 @@ public class BesuCommand implements DefaultCommandValues, Runnable { this.logger = commandLogger; this.rlpBlockImporter = rlpBlockImporter; - this.rlpBlockExporterFactory = rlpBlockExporterFactory; this.jsonBlockImporterFactory = jsonBlockImporterFactory; + this.era1BlockImporter = era1BlockImporter; + this.rlpBlockExporterFactory = rlpBlockExporterFactory; this.runnerBuilder = runnerBuilder; this.controllerBuilder = controllerBuilder; this.besuPluginContext = besuPluginContext; @@ -1100,6 +1108,7 @@ public class BesuCommand implements DefaultCommandValues, Runnable { new BlocksSubCommand( rlpBlockImporter, jsonBlockImporterFactory, + era1BlockImporter, rlpBlockExporterFactory, commandLine.getOut())); commandLine.addSubcommand( diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlockImportFormat.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlockImportFormat.java index 77e58cfb7..b9677b1a1 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlockImportFormat.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlockImportFormat.java @@ -19,5 +19,7 @@ public enum BlockImportFormat { /** RLP block import format. */ RLP, /** Json block import format. */ - JSON + JSON, + /** Era1 block import format. */ + ERA1, } diff --git a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommand.java b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommand.java index 4c727f924..c05dca5fb 100644 --- a/besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommand.java +++ b/besu/src/main/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommand.java @@ -18,6 +18,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import static org.hyperledger.besu.cli.subcommands.blocks.BlocksSubCommand.COMMAND_NAME; import org.hyperledger.besu.chainexport.RlpBlockExporter; +import org.hyperledger.besu.chainimport.Era1BlockImporter; import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.cli.BesuCommand; @@ -51,6 +52,7 @@ import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.concurrent.TimeoutException; import java.util.function.Function; import java.util.function.Supplier; @@ -92,6 +94,7 @@ public class BlocksSubCommand implements Runnable { private final Supplier rlpBlockImporter; private final Function jsonBlockImporterFactory; + private final Supplier era1BlockImporter; private final Function rlpBlockExporterFactory; private final PrintWriter out; @@ -101,17 +104,20 @@ public class BlocksSubCommand implements Runnable { * * @param rlpBlockImporter the RLP block importer * @param jsonBlockImporterFactory the Json block importer factory + * @param era1BlockImporter the era1 block importer supplier * @param rlpBlockExporterFactory the RLP block exporter factory * @param out Instance of PrintWriter where command usage will be written. */ public BlocksSubCommand( final Supplier rlpBlockImporter, final Function jsonBlockImporterFactory, + final Supplier era1BlockImporter, final Function rlpBlockExporterFactory, final PrintWriter out) { this.rlpBlockImporter = rlpBlockImporter; - this.rlpBlockExporterFactory = rlpBlockExporterFactory; this.jsonBlockImporterFactory = jsonBlockImporterFactory; + this.era1BlockImporter = era1BlockImporter; + this.rlpBlockExporterFactory = rlpBlockExporterFactory; this.out = out; } @@ -198,6 +204,7 @@ public class BlocksSubCommand implements Runnable { checkCommand(parentCommand); checkNotNull(parentCommand.rlpBlockImporter); checkNotNull(parentCommand.jsonBlockImporterFactory); + checkNotNull(parentCommand.era1BlockImporter); if (blockImportFiles.isEmpty()) { throw new ParameterException(spec.commandLine(), "No files specified to import."); } @@ -214,12 +221,9 @@ public class BlocksSubCommand implements Runnable { try { LOG.info("Importing from {}", path); switch (format) { - case RLP: - importRlpBlocks(controller, path); - break; - case JSON: - importJsonBlocks(controller, path); - break; + case RLP -> importRlpBlocks(controller, path); + case JSON -> importJsonBlocks(controller, path); + case ERA1 -> importEra1Blocks(controller, path); } } catch (final FileNotFoundException e) { if (blockImportFiles.size() == 1) { @@ -297,6 +301,14 @@ public class BlocksSubCommand implements Runnable { .get() .importBlockchain(path, controller, skipPow, startBlock, endBlock); } + + private void importEra1Blocks(final BesuController controller, final Path path) + throws IOException, + java.util.concurrent.ExecutionException, + InterruptedException, + TimeoutException { + parentCommand.era1BlockImporter.get().importBlocks(controller, path); + } } /** diff --git a/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java b/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java index ce4f00db7..cef6ebd2d 100644 --- a/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java +++ b/besu/src/main/java/org/hyperledger/besu/components/BesuCommandModule.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.components; import org.hyperledger.besu.Besu; import org.hyperledger.besu.RunnerBuilder; import org.hyperledger.besu.chainexport.RlpBlockExporter; +import org.hyperledger.besu.chainimport.Era1BlockImporter; import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.cli.BesuCommand; @@ -50,6 +51,7 @@ public class BesuCommandModule { new BesuCommand( RlpBlockImporter::new, JsonBlockImporter::new, + Era1BlockImporter::new, RlpBlockExporter::new, new RunnerBuilder(), new BesuController.Builder(), diff --git a/besu/src/test/java/org/hyperledger/besu/chainimport/Era1BlockImporterTest.java b/besu/src/test/java/org/hyperledger/besu/chainimport/Era1BlockImporterTest.java new file mode 100644 index 000000000..47871d4f9 --- /dev/null +++ b/besu/src/test/java/org/hyperledger/besu/chainimport/Era1BlockImporterTest.java @@ -0,0 +1,95 @@ +/* + * Copyright contributors to Besu. + * + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.chainimport; + +import static org.mockito.Mockito.mock; + +import org.hyperledger.besu.cli.config.EthNetworkConfig; +import org.hyperledger.besu.cli.config.NetworkName; +import org.hyperledger.besu.components.BesuComponent; +import org.hyperledger.besu.controller.BesuController; +import org.hyperledger.besu.cryptoservices.NodeKeyUtils; +import org.hyperledger.besu.ethereum.api.ImmutableApiConfiguration; +import org.hyperledger.besu.ethereum.chain.Blockchain; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider; +import org.hyperledger.besu.ethereum.core.MiningConfiguration; +import org.hyperledger.besu.ethereum.core.PrivacyParameters; +import org.hyperledger.besu.ethereum.eth.EthProtocolConfiguration; +import org.hyperledger.besu.ethereum.eth.sync.SyncMode; +import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; +import org.hyperledger.besu.ethereum.eth.transactions.TransactionPoolConfiguration; +import org.hyperledger.besu.ethereum.p2p.config.NetworkingConfiguration; +import org.hyperledger.besu.evm.internal.EvmConfiguration; +import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; +import org.hyperledger.besu.testutil.BlockTestUtil; +import org.hyperledger.besu.testutil.TestClock; + +import java.io.IOException; +import java.math.BigInteger; +import java.net.URISyntaxException; +import java.nio.file.Path; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +public class Era1BlockImporterTest { + @TempDir Path dataDirectory; + + private final Era1BlockImporter era1BlockImporter = new Era1BlockImporter(); + + @Test + public void testImport() + throws IOException, + ExecutionException, + InterruptedException, + TimeoutException, + URISyntaxException { + final Path source = + Path.of( + BlockTestUtil.class + .getClassLoader() + .getResource("mainnet-00000-5ec1ffb8.era1") + .toURI()); + final BesuController targetController = + new BesuController.Builder() + .fromEthNetworkConfig( + EthNetworkConfig.getNetworkConfig(NetworkName.MAINNET), SyncMode.FAST) + .synchronizerConfiguration(SynchronizerConfiguration.builder().build()) + .ethProtocolConfiguration(EthProtocolConfiguration.defaultConfig()) + .storageProvider(new InMemoryKeyValueStorageProvider()) + .networkId(BigInteger.ONE) + .miningParameters(MiningConfiguration.newDefault()) + .nodeKey(NodeKeyUtils.generate()) + .metricsSystem(new NoOpMetricsSystem()) + .privacyParameters(PrivacyParameters.DEFAULT) + .clock(TestClock.fixed()) + .transactionPoolConfiguration(TransactionPoolConfiguration.DEFAULT) + .evmConfiguration(EvmConfiguration.DEFAULT) + .networkConfiguration(NetworkingConfiguration.create()) + .besuComponent(mock(BesuComponent.class)) + .apiConfiguration(ImmutableApiConfiguration.builder().build()) + .dataDirectory(dataDirectory) + .build(); + era1BlockImporter.importBlocks(targetController, source); + + Blockchain blockchain = targetController.getProtocolContext().getBlockchain(); + BlockHeader chainHeadHeader = blockchain.getChainHeadHeader(); + Assertions.assertEquals(8191, chainHeadHeader.getNumber()); + } +} diff --git a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java index b6464e6ff..e04ec95ca 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/CommandTestAbstract.java @@ -31,6 +31,7 @@ import static org.mockito.Mockito.when; import org.hyperledger.besu.Runner; import org.hyperledger.besu.RunnerBuilder; import org.hyperledger.besu.chainexport.RlpBlockExporter; +import org.hyperledger.besu.chainimport.Era1BlockImporter; import org.hyperledger.besu.chainimport.JsonBlockImporter; import org.hyperledger.besu.chainimport.RlpBlockImporter; import org.hyperledger.besu.cli.config.EthNetworkConfig; @@ -216,6 +217,7 @@ public abstract class CommandTestAbstract { @Mock protected RlpBlockExporter rlpBlockExporter; @Mock protected JsonBlockImporter jsonBlockImporter; @Mock protected RlpBlockImporter rlpBlockImporter; + @Mock protected Era1BlockImporter era1BlockImporter; @Mock protected StorageServiceImpl storageService; @Mock protected TransactionSelectionServiceImpl txSelectionService; @Mock protected SecurityModuleServiceImpl securityModuleService; @@ -472,6 +474,7 @@ public abstract class CommandTestAbstract { return new TestBesuCommandWithRequiredOption( () -> rlpBlockImporter, this::jsonBlockImporterFactory, + () -> era1BlockImporter, (blockchain) -> rlpBlockExporter, mockRunnerBuilder, mockControllerBuilderFactory, @@ -485,6 +488,7 @@ public abstract class CommandTestAbstract { return new TestBesuCommand( () -> rlpBlockImporter, this::jsonBlockImporterFactory, + () -> era1BlockImporter, (blockchain) -> rlpBlockExporter, mockRunnerBuilder, mockControllerBuilderFactory, @@ -498,6 +502,7 @@ public abstract class CommandTestAbstract { return new TestBesuCommandWithoutPortCheck( () -> rlpBlockImporter, this::jsonBlockImporterFactory, + () -> era1BlockImporter, (blockchain) -> rlpBlockExporter, mockRunnerBuilder, mockControllerBuilderFactory, @@ -538,6 +543,7 @@ public abstract class CommandTestAbstract { TestBesuCommand( final Supplier mockBlockImporter, final Function jsonBlockImporterFactory, + final Supplier era1BlockImporter, final Function rlpBlockExporterFactory, final RunnerBuilder mockRunnerBuilder, final BesuController.Builder controllerBuilderFactory, @@ -550,6 +556,7 @@ public abstract class CommandTestAbstract { super( mockBlockImporter, jsonBlockImporterFactory, + era1BlockImporter, rlpBlockExporterFactory, mockRunnerBuilder, controllerBuilderFactory, @@ -635,6 +642,7 @@ public abstract class CommandTestAbstract { TestBesuCommandWithRequiredOption( final Supplier mockBlockImporter, final Function jsonBlockImporterFactory, + final Supplier era1BlockImporter, final Function rlpBlockExporterFactory, final RunnerBuilder mockRunnerBuilder, final BesuController.Builder controllerBuilderFactory, @@ -647,6 +655,7 @@ public abstract class CommandTestAbstract { super( mockBlockImporter, jsonBlockImporterFactory, + era1BlockImporter, rlpBlockExporterFactory, mockRunnerBuilder, controllerBuilderFactory, @@ -669,6 +678,7 @@ public abstract class CommandTestAbstract { TestBesuCommandWithoutPortCheck( final Supplier mockBlockImporter, final Function jsonBlockImporterFactory, + final Supplier era1BlockImporter, final Function rlpBlockExporterFactory, final RunnerBuilder mockRunnerBuilder, final BesuController.Builder controllerBuilderFactory, @@ -681,6 +691,7 @@ public abstract class CommandTestAbstract { super( mockBlockImporter, jsonBlockImporterFactory, + era1BlockImporter, rlpBlockExporterFactory, mockRunnerBuilder, controllerBuilderFactory, diff --git a/besu/src/test/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommandTest.java index f43d039b4..69dd1a4a9 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/subcommands/blocks/BlocksSubCommandTest.java @@ -72,7 +72,7 @@ public class BlocksSubCommandTest extends CommandTestAbstract { + " (exclusive). If not specified all blocks after\n" + " the start block will be imported.\n" + " --format= The type of data to be imported, possible values\n" - + " are: RLP, JSON (default: RLP).\n" + + " are: RLP, JSON, ERA1 (default: RLP).\n" + " --from[=...] File containing blocks to import.\n" + " -h, --help Show this help message and exit.\n" + " --run Start besu after importing.\n" diff --git a/besu/src/test/resources/mainnet-00000-5ec1ffb8.era1 b/besu/src/test/resources/mainnet-00000-5ec1ffb8.era1 new file mode 100755 index 000000000..dbc1d316c Binary files /dev/null and b/besu/src/test/resources/mainnet-00000-5ec1ffb8.era1 differ diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java index 0eff53c4e..151fc28c0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/BlockHeader.java @@ -252,7 +252,10 @@ public class BlockHeader extends SealableBlockHeader public static BlockHeader readFrom( final RLPInput input, final BlockHeaderFunctions blockHeaderFunctions) { final RLPInput headerRlp = input.readAsRlp(); - headerRlp.enterList(); + if (headerRlp.enterList() == 0) { + return null; + } + final Hash parentHash = Hash.wrap(headerRlp.readBytes32()); final Hash ommersHash = Hash.wrap(headerRlp.readBytes32()); final Address coinbase = Address.readFrom(headerRlp);