Split Block Validation from Importing (#579)

Ibft is required to validate a block upon reception, but not import
it until a later time.

As such, IBFT will require validation and importing to be separated.

The validator has been exposed as part of the ProtocolSpecification.
Signed-off-by: Adrian Sutton <adrian.sutton@consensys.net>
pull/2/head
tmohay 6 years ago committed by GitHub
parent c212205f9f
commit 7e373ccc97
  1. 2
      consensus/clique/src/main/java/tech/pegasys/pantheon/consensus/clique/CliqueProtocolSchedule.java
  2. 7
      consensus/ibft/src/main/java/tech/pegasys/pantheon/consensus/ibft/IbftProtocolSchedule.java
  3. 7
      consensus/ibftlegacy/src/main/java/tech/pegasys/pantheon/consensus/ibftlegacy/IbftProtocolSchedule.java
  4. 47
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/BlockValidator.java
  5. 109
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/MainnetBlockValidator.java
  6. 75
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetBlockImporter.java
  7. 2
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/MainnetProtocolSpecs.java
  8. 15
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpec.java
  9. 25
      ethereum/core/src/main/java/tech/pegasys/pantheon/ethereum/mainnet/ProtocolSpecBuilder.java
  10. 2
      ethereum/jsonrpc/src/test/java/tech/pegasys/pantheon/ethereum/jsonrpc/internal/methods/EthGetTransactionReceiptTest.java

@ -18,6 +18,7 @@ import tech.pegasys.pantheon.config.CliqueConfigOptions;
import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.crypto.SECP256K1.KeyPair;
import tech.pegasys.pantheon.ethereum.MainnetBlockValidator;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.Util;
import tech.pegasys.pantheon.ethereum.core.Wei;
@ -63,6 +64,7 @@ public class CliqueProtocolSchedule {
difficultyCalculator -> cliqueBlockHeaderValidator(secondsBetweenBlocks, epochManager),
difficultyCalculator -> cliqueBlockHeaderValidator(secondsBetweenBlocks, epochManager),
MainnetBlockBodyValidator::new,
MainnetBlockValidator::new,
MainnetBlockImporter::new,
new CliqueDifficultyCalculator(localNodeAddress))
.blockReward(Wei.ZERO)

@ -18,6 +18,7 @@ import tech.pegasys.pantheon.config.GenesisConfigOptions;
import tech.pegasys.pantheon.config.IbftConfigOptions;
import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.consensus.common.VoteTallyUpdater;
import tech.pegasys.pantheon.ethereum.MainnetBlockValidator;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockBodyValidator;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockImporter;
@ -57,10 +58,10 @@ public class IbftProtocolSchedule {
difficultyCalculator -> ibftBlockHeaderValidator(secondsBetweenBlocks),
difficultyCalculator -> ibftBlockHeaderValidator(secondsBetweenBlocks),
MainnetBlockBodyValidator::new,
(blockHeaderValidator, blockBodyValidator, blockProcessor) ->
MainnetBlockValidator::new,
(blockValidator) ->
new IbftBlockImporter(
new MainnetBlockImporter<>(
blockHeaderValidator, blockBodyValidator, blockProcessor),
new MainnetBlockImporter<>(blockValidator),
new VoteTallyUpdater(epochManager, new IbftBlockInterface())),
(time, parent, protocolContext) -> BigInteger.ONE)
.blockReward(Wei.ZERO)

@ -20,6 +20,7 @@ import tech.pegasys.pantheon.consensus.common.EpochManager;
import tech.pegasys.pantheon.consensus.common.VoteTallyUpdater;
import tech.pegasys.pantheon.consensus.ibft.IbftBlockImporter;
import tech.pegasys.pantheon.consensus.ibft.IbftContext;
import tech.pegasys.pantheon.ethereum.MainnetBlockValidator;
import tech.pegasys.pantheon.ethereum.core.Wei;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockBodyValidator;
import tech.pegasys.pantheon.ethereum.mainnet.MainnetBlockImporter;
@ -59,10 +60,10 @@ public class IbftProtocolSchedule {
difficultyCalculator -> ibftBlockHeaderValidator(secondsBetweenBlocks),
difficultyCalculator -> ibftBlockHeaderValidator(secondsBetweenBlocks),
MainnetBlockBodyValidator::new,
(blockHeaderValidator, blockBodyValidator, blockProcessor) ->
MainnetBlockValidator::new,
(blockValidator) ->
new IbftBlockImporter(
new MainnetBlockImporter<>(
blockHeaderValidator, blockBodyValidator, blockProcessor),
new MainnetBlockImporter<>(blockValidator),
new VoteTallyUpdater(epochManager, new IbftLegacyBlockInterface())),
(time, parent, protocolContext) -> BigInteger.ONE)
.blockReward(Wei.ZERO)

@ -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;
import tech.pegasys.pantheon.ethereum.core.Block;
import tech.pegasys.pantheon.ethereum.core.MutableWorldState;
import tech.pegasys.pantheon.ethereum.core.TransactionReceipt;
import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode;
import java.util.List;
import java.util.Optional;
public interface BlockValidator<C> {
class BlockProcessingOutputs {
public final MutableWorldState worldState;
public final List<TransactionReceipt> receipts;
public BlockProcessingOutputs(
final MutableWorldState worldState, final List<TransactionReceipt> receipts) {
this.worldState = worldState;
this.receipts = receipts;
}
}
Optional<BlockProcessingOutputs> validateAndProcessBlock(
final ProtocolContext<C> context,
final Block block,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode);
boolean fastBlockValidation(
final ProtocolContext<C> context,
final Block block,
final List<TransactionReceipt> receipts,
final HeaderValidationMode headerValidationMode);
}

@ -0,0 +1,109 @@
/*
* 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;
import static org.apache.logging.log4j.LogManager.getLogger;
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.MutableWorldState;
import tech.pegasys.pantheon.ethereum.core.TransactionReceipt;
import tech.pegasys.pantheon.ethereum.mainnet.BlockBodyValidator;
import tech.pegasys.pantheon.ethereum.mainnet.BlockHeaderValidator;
import tech.pegasys.pantheon.ethereum.mainnet.BlockProcessor;
import tech.pegasys.pantheon.ethereum.mainnet.HeaderValidationMode;
import java.util.List;
import java.util.Optional;
import org.apache.logging.log4j.Logger;
public class MainnetBlockValidator<C> implements BlockValidator<C> {
private static final Logger LOG = getLogger();
private final BlockHeaderValidator<C> blockHeaderValidator;
private final BlockBodyValidator<C> blockBodyValidator;
private final BlockProcessor blockProcessor;
public MainnetBlockValidator(
final BlockHeaderValidator<C> blockHeaderValidator,
final BlockBodyValidator<C> blockBodyValidator,
final BlockProcessor blockProcessor) {
this.blockHeaderValidator = blockHeaderValidator;
this.blockBodyValidator = blockBodyValidator;
this.blockProcessor = blockProcessor;
}
@Override
public Optional<BlockProcessingOutputs> validateAndProcessBlock(
final ProtocolContext<C> context,
final Block block,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode) {
final BlockHeader header = block.getHeader();
final Optional<BlockHeader> maybeParentHeader =
context.getBlockchain().getBlockHeader(header.getParentHash());
if (!maybeParentHeader.isPresent()) {
LOG.error(
"Attempted to import block {} with hash {} but parent block {} was not present",
header.getNumber(),
header.getHash(),
header.getParentHash());
return Optional.empty();
}
final BlockHeader parentHeader = maybeParentHeader.get();
if (!blockHeaderValidator.validateHeader(header, parentHeader, context, headerValidationMode)) {
return Optional.empty();
}
final MutableBlockchain blockchain = context.getBlockchain();
final MutableWorldState worldState =
context.getWorldStateArchive().getMutable(parentHeader.getStateRoot());
final BlockProcessor.Result result = blockProcessor.processBlock(blockchain, worldState, block);
if (!result.isSuccessful()) {
return Optional.empty();
}
final List<TransactionReceipt> receipts = result.getReceipts();
if (!blockBodyValidator.validateBody(
context, block, receipts, worldState.rootHash(), ommerValidationMode)) {
return Optional.empty();
}
return Optional.of(new BlockProcessingOutputs(worldState, receipts));
}
@Override
public boolean fastBlockValidation(
final ProtocolContext<C> context,
final Block block,
final List<TransactionReceipt> receipts,
final HeaderValidationMode headerValidationMode) {
final BlockHeader header = block.getHeader();
if (!blockHeaderValidator.validateHeader(header, context, headerValidationMode)) {
return false;
}
if (!blockBodyValidator.validateBodyLight(
context, block, receipts, HeaderValidationMode.FULL)) {
return false;
}
return true;
}
}

@ -14,12 +14,12 @@ package tech.pegasys.pantheon.ethereum.mainnet;
import static org.apache.logging.log4j.LogManager.getLogger;
import tech.pegasys.pantheon.ethereum.BlockValidator;
import tech.pegasys.pantheon.ethereum.BlockValidator.BlockProcessingOutputs;
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.BlockImporter;
import tech.pegasys.pantheon.ethereum.core.MutableWorldState;
import tech.pegasys.pantheon.ethereum.core.TransactionReceipt;
import java.util.List;
@ -28,21 +28,13 @@ import java.util.Optional;
import org.apache.logging.log4j.Logger;
public class MainnetBlockImporter<C> implements BlockImporter<C> {
private static final Logger LOG = getLogger();
private final BlockHeaderValidator<C> blockHeaderValidator;
private final BlockBodyValidator<C> blockBodyValidator;
private static final Logger LOG = getLogger();
private final BlockProcessor blockProcessor;
final BlockValidator<C> blockValidator;
public MainnetBlockImporter(
final BlockHeaderValidator<C> blockHeaderValidator,
final BlockBodyValidator<C> blockBodyValidator,
final BlockProcessor blockProcessor) {
this.blockHeaderValidator = blockHeaderValidator;
this.blockBodyValidator = blockBodyValidator;
this.blockProcessor = blockProcessor;
public MainnetBlockImporter(final BlockValidator<C> blockValidator) {
this.blockValidator = blockValidator;
}
@Override
@ -51,41 +43,23 @@ public class MainnetBlockImporter<C> implements BlockImporter<C> {
final Block block,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode) {
final BlockHeader header = block.getHeader();
final Optional<BlockHeader> maybeParentHeader =
context.getBlockchain().getBlockHeader(header.getParentHash());
if (!maybeParentHeader.isPresent()) {
LOG.error(
"Attempted to import block {} with hash {} but parent block {} was not present",
header.getNumber(),
header.getHash(),
header.getParentHash());
return false;
}
final BlockHeader parentHeader = maybeParentHeader.get();
if (!blockHeaderValidator.validateHeader(header, parentHeader, context, headerValidationMode)) {
return false;
}
final Optional<BlockProcessingOutputs> outputs =
blockValidator.validateAndProcessBlock(
context, block, headerValidationMode, ommerValidationMode);
final MutableBlockchain blockchain = context.getBlockchain();
final MutableWorldState worldState =
context.getWorldStateArchive().getMutable(parentHeader.getStateRoot());
final BlockProcessor.Result result = blockProcessor.processBlock(blockchain, worldState, block);
if (!result.isSuccessful()) {
return false;
}
outputs.ifPresent(processingOutputs -> persistState(processingOutputs, block, context));
final List<TransactionReceipt> receipts = result.getReceipts();
if (!blockBodyValidator.validateBody(
context, block, receipts, worldState.rootHash(), ommerValidationMode)) {
return false;
return outputs.isPresent();
}
blockchain.appendBlock(block, receipts);
return true;
private void persistState(
final BlockProcessingOutputs processingOutputs,
final Block block,
final ProtocolContext<C> context) {
processingOutputs.worldState.persist();
final MutableBlockchain blockchain = context.getBlockchain();
blockchain.appendBlock(block, processingOutputs.receipts);
}
@Override
@ -94,19 +68,12 @@ public class MainnetBlockImporter<C> implements BlockImporter<C> {
final Block block,
final List<TransactionReceipt> receipts,
final HeaderValidationMode headerValidationMode) {
final BlockHeader header = block.getHeader();
if (!blockHeaderValidator.validateHeader(header, context, headerValidationMode)) {
return false;
if (blockValidator.fastBlockValidation(context, block, receipts, headerValidationMode)) {
context.getBlockchain().appendBlock(block, receipts);
return true;
}
if (!blockBodyValidator.validateBodyLight(
context, block, receipts, HeaderValidationMode.FULL)) {
return false;
}
context.getBlockchain().appendBlock(block, receipts);
return true;
}
}

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.ethereum.MainnetBlockValidator;
import tech.pegasys.pantheon.ethereum.chain.Blockchain;
import tech.pegasys.pantheon.ethereum.core.Address;
import tech.pegasys.pantheon.ethereum.core.BlockHeader;
@ -87,6 +88,7 @@ public abstract class MainnetProtocolSpecs {
.transactionReceiptFactory(MainnetProtocolSpecs::frontierTransactionReceiptFactory)
.blockReward(FRONTIER_BLOCK_REWARD)
.blockProcessorBuilder(MainnetBlockProcessor::new)
.blockValidatorBuilder(MainnetBlockValidator::new)
.blockImporterBuilder(MainnetBlockImporter::new)
.transactionReceiptType(TransactionReceiptType.ROOT)
.blockHashFunction(MainnetBlockHashFunction::createHash)

@ -12,6 +12,7 @@
*/
package tech.pegasys.pantheon.ethereum.mainnet;
import tech.pegasys.pantheon.ethereum.BlockValidator;
import tech.pegasys.pantheon.ethereum.core.BlockHashFunction;
import tech.pegasys.pantheon.ethereum.core.BlockImporter;
import tech.pegasys.pantheon.ethereum.core.Wei;
@ -36,6 +37,8 @@ public class ProtocolSpec<C> {
private final BlockImporter<C> blockImporter;
private final BlockValidator<C> blockValidator;
private final BlockProcessor blockProcessor;
private final BlockHashFunction blockHashFunction;
@ -60,6 +63,7 @@ public class ProtocolSpec<C> {
* @param blockBodyValidator the block body validator to use
* @param blockProcessor the block processor to use
* @param blockImporter the block importer to use
* @param blockValidator the block validator to use
* @param blockHashFunction the block hash function to use
* @param transactionReceiptFactory the transactionReceiptFactory to use
* @param difficultyCalculator the difficultyCalculator to use
@ -77,6 +81,7 @@ public class ProtocolSpec<C> {
final BlockBodyValidator<C> blockBodyValidator,
final BlockProcessor blockProcessor,
final BlockImporter<C> blockImporter,
final BlockValidator<C> blockValidator,
final BlockHashFunction blockHashFunction,
final TransactionReceiptFactory transactionReceiptFactory,
final DifficultyCalculator<C> difficultyCalculator,
@ -92,6 +97,7 @@ public class ProtocolSpec<C> {
this.blockBodyValidator = blockBodyValidator;
this.blockProcessor = blockProcessor;
this.blockImporter = blockImporter;
this.blockValidator = blockValidator;
this.blockHashFunction = blockHashFunction;
this.transactionReceiptFactory = transactionReceiptFactory;
this.difficultyCalculator = difficultyCalculator;
@ -144,6 +150,15 @@ public class ProtocolSpec<C> {
return blockImporter;
}
/**
* Returns the block validator used in this specification.
*
* @return the block validator
*/
public BlockValidator<C> getBlockValidator() {
return blockValidator;
}
/**
* Returns the block header validator used in this specification.
*

@ -14,6 +14,7 @@ package tech.pegasys.pantheon.ethereum.mainnet;
import static com.google.common.base.Preconditions.checkNotNull;
import tech.pegasys.pantheon.ethereum.BlockValidator;
import tech.pegasys.pantheon.ethereum.core.BlockHashFunction;
import tech.pegasys.pantheon.ethereum.core.BlockImporter;
import tech.pegasys.pantheon.ethereum.core.Wei;
@ -43,6 +44,7 @@ public class ProtocolSpecBuilder<T> {
messageCallProcessorBuilder;
private TransactionProcessorBuilder transactionProcessorBuilder;
private BlockProcessorBuilder blockProcessorBuilder;
private BlockValidatorBuilder<T> blockValidatorBuilder;
private BlockImporterBuilder<T> blockImporterBuilder;
private TransactionReceiptType transactionReceiptType;
private String name;
@ -146,6 +148,12 @@ public class ProtocolSpecBuilder<T> {
return this;
}
public ProtocolSpecBuilder<T> blockValidatorBuilder(
final BlockValidatorBuilder<T> blockValidatorBuilder) {
this.blockValidatorBuilder = blockValidatorBuilder;
return this;
}
public ProtocolSpecBuilder<T> transactionReceiptType(
final TransactionReceiptType transactionReceiptType) {
this.transactionReceiptType = transactionReceiptType;
@ -172,6 +180,7 @@ public class ProtocolSpecBuilder<T> {
final Function<DifficultyCalculator<R>, BlockHeaderValidator<R>> blockHeaderValidatorBuilder,
final Function<DifficultyCalculator<R>, BlockHeaderValidator<R>> ommerHeaderValidatorBuilder,
final Function<ProtocolSchedule<R>, BlockBodyValidator<R>> blockBodyValidatorBuilder,
final BlockValidatorBuilder<R> blockValidatorBuilder,
final BlockImporterBuilder<R> blockImporterBuilder,
final DifficultyCalculator<R> difficultyCalculator) {
return new ProtocolSpecBuilder<R>()
@ -186,6 +195,7 @@ public class ProtocolSpecBuilder<T> {
.ommerHeaderValidatorBuilder(ommerHeaderValidatorBuilder)
.blockBodyValidatorBuilder(blockBodyValidatorBuilder)
.blockProcessorBuilder(blockProcessorBuilder)
.blockValidatorBuilder(blockValidatorBuilder)
.blockImporterBuilder(blockImporterBuilder)
.blockHashFunction(blockHashFunction)
.blockReward(blockReward)
@ -208,6 +218,7 @@ public class ProtocolSpecBuilder<T> {
checkNotNull(blockBodyValidatorBuilder, "Missing block body validator");
checkNotNull(blockProcessorBuilder, "Missing block processor");
checkNotNull(blockImporterBuilder, "Missing block importer");
checkNotNull(blockValidatorBuilder, "Missing block validator");
checkNotNull(blockHashFunction, "Missing block hash function");
checkNotNull(blockReward, "Missing block reward");
checkNotNull(difficultyCalculator, "Missing difficulty calculator");
@ -243,8 +254,9 @@ public class ProtocolSpecBuilder<T> {
transactionReceiptFactory,
blockReward,
miningBeneficiaryCalculator);
final BlockImporter<T> blockImporter =
blockImporterBuilder.apply(blockHeaderValidator, blockBodyValidator, blockProcessor);
final BlockValidator<T> blockValidator =
blockValidatorBuilder.apply(blockHeaderValidator, blockBodyValidator, blockProcessor);
final BlockImporter<T> blockImporter = blockImporterBuilder.apply(blockValidator);
return new ProtocolSpec<>(
name,
evm,
@ -255,6 +267,7 @@ public class ProtocolSpecBuilder<T> {
blockBodyValidator,
blockProcessor,
blockImporter,
blockValidator,
blockHashFunction,
transactionReceiptFactory,
difficultyCalculator,
@ -279,10 +292,14 @@ public class ProtocolSpecBuilder<T> {
MiningBeneficiaryCalculator miningBeneficiaryCalculator);
}
public interface BlockImporterBuilder<T> {
BlockImporter<T> apply(
public interface BlockValidatorBuilder<T> {
BlockValidator<T> apply(
BlockHeaderValidator<T> blockHeaderValidator,
BlockBodyValidator<T> blockBodyValidator,
BlockProcessor blockProcessor);
}
public interface BlockImporterBuilder<T> {
BlockImporter<T> apply(BlockValidator<T> blockValidator);
}
}

@ -89,6 +89,7 @@ public class EthGetTransactionReceiptTest {
null,
null,
null,
null,
TransactionReceiptType.ROOT,
BlockHeader::getCoinbase);
private final ProtocolSpec<Void> statusTransactionTypeSpec =
@ -106,6 +107,7 @@ public class EthGetTransactionReceiptTest {
null,
null,
null,
null,
TransactionReceiptType.STATUS,
BlockHeader::getCoinbase);

Loading…
Cancel
Save