4512 invalid badblock (#4554)

* don't add to bad blocks manager on StorageException
* support for MerklePatriciaTrie exceptions

Signed-off-by: Justin Florentine <justin+github@florentine.us>
pull/4576/head
Justin Florentine 2 years ago committed by GitHub
parent b322ef6ae1
commit fe79c02102
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      CHANGELOG.md
  2. 2
      consensus/ibft/src/main/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidator.java
  3. 5
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/statemachine/RoundChangeManagerTest.java
  4. 7
      consensus/ibft/src/test/java/org/hyperledger/besu/consensus/ibft/validation/MessageValidatorTest.java
  5. 18
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinator.java
  6. 6
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeMiningCoordinator.java
  7. 6
      consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/blockcreation/TransitionCoordinator.java
  8. 4
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeCoordinatorTest.java
  9. 18
      consensus/merge/src/test/java/org/hyperledger/besu/consensus/merge/blockcreation/MergeReorgTest.java
  10. 2
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validation/ProposalPayloadValidator.java
  11. 2
      consensus/qbft/src/main/java/org/hyperledger/besu/consensus/qbft/validation/RoundChangeMessageValidator.java
  12. 17
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalPayloadValidatorTest.java
  13. 7
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/ProposalValidatorTest.java
  14. 27
      consensus/qbft/src/test/java/org/hyperledger/besu/consensus/qbft/validation/RoundChangeMessageValidatorTest.java
  15. 4
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayload.java
  16. 12
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadTest.java
  17. 47
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockProcessingOutputs.java
  18. 87
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockProcessingResult.java
  19. 72
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockValidationResult.java
  20. 45
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/BlockValidator.java
  21. 161
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/MainnetBlockValidator.java
  22. 44
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockProcessingResult.java
  23. 30
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockProcessor.java
  24. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/goquorum/GoQuorumBlockValidator.java
  25. 106
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java
  26. 26
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/BlockProcessor.java
  27. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockImporter.java
  28. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java
  29. 5
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PrivacyBlockProcessor.java
  30. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/PrivateGroupRehydrationBlockProcessor.java
  31. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/privacy/storage/migration/PrivateMigrationBlockProcessor.java
  32. 253
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/BlockImportExceptionHandlingTest.java
  33. 117
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/MainnetBlockValidatorTest.java
  34. 5
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/bonsai/BonsaiSnapshotIsolationTests.java
  35. 6
      ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContext.java
  36. 13
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/BackwardSyncContextTest.java
  37. 12
      ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/backwardsync/ForwardSyncStepTest.java

@ -9,7 +9,7 @@
### Bug Fixes
- Fixed default fromBlock value and improved parameter interpetation in eth_getLogs RPC handler [#4513](https://github.com/hyperledger/besu/pull/4513)
- Corrects treating a block as bad on internal error during either validation or processing [#4512](https://github.com/hyperledger/besu/issues/4512)
## 22.10.0-RC2
### Breaking Changes

@ -81,7 +81,7 @@ public class MessageValidator {
blockValidator.validateAndProcessBlock(
protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL);
if (validationResult.blockProcessingOutputs.isEmpty()) {
if (validationResult.isFailed()) {
LOG.info(
"Invalid Proposal message, block did not pass validation. Reason {}",
validationResult.errorMessage);

@ -36,9 +36,8 @@ import org.hyperledger.besu.consensus.ibft.validation.SignedDataValidator;
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.crypto.NodeKeyUtils;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockValidator.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.Util;
@ -79,7 +78,7 @@ public class RoundChangeManagerTest {
final BlockValidator blockValidator = mock(BlockValidator.class);
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any()))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
final RoundChangePayloadValidator.MessageValidatorForHeightFactory messageValidatorFactory =
mock(RoundChangePayloadValidator.MessageValidatorForHeightFactory.class);

@ -35,9 +35,8 @@ import org.hyperledger.besu.consensus.ibft.payload.RoundChangeCertificate;
import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.crypto.NodeKeyUtils;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockValidator.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.AddressHelpers;
@ -99,7 +98,7 @@ public class MessageValidatorTest {
mock(MutableBlockchain.class), mock(WorldStateArchive.class), mockBftCtx);
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any()))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(roundChangeCertificateValidator.validateProposalMessageMatchesLatestPrepareCertificate(
any(), any()))
@ -154,7 +153,7 @@ public class MessageValidatorTest {
@Test
public void blockValidationFailureFailsValidation() {
when(blockValidator.validateAndProcessBlock(any(), any(), any(), any()))
.thenReturn(new Result("Failed"));
.thenReturn(new BlockProcessingResult("Failed"));
final Proposal proposalMsg =
messageFactory.createProposal(roundIdentifier, block, Optional.empty());

@ -23,7 +23,7 @@ import org.hyperledger.besu.consensus.merge.MergeContext;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.BlockCreator.BlockCreationResult;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
@ -210,8 +210,8 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
.createBlock(Optional.of(Collections.emptyList()), prevRandao, timestamp)
.getBlock();
Result result = validateBlock(emptyBlock);
if (result.blockProcessingOutputs.isPresent()) {
BlockProcessingResult result = validateBlock(emptyBlock);
if (result.getYield().isPresent()) {
mergeContext.putPayloadById(payloadIdentifier, emptyBlock);
debugLambda(
LOG,
@ -327,7 +327,7 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
if (isBlockCreationCancelled(payloadIdentifier)) return;
final var resultBest = validateBlock(bestBlock);
if (resultBest.blockProcessingOutputs.isPresent()) {
if (resultBest.getYield().isPresent()) {
if (isBlockCreationCancelled(payloadIdentifier)) return;
@ -395,7 +395,8 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
}
@Override
public Result validateBlock(final Block block) {
public BlockProcessingResult validateBlock(final Block block) {
final var chain = protocolContext.getBlockchain();
chain
.getBlockHeader(block.getHeader().getParentHash())
@ -415,18 +416,15 @@ public class MergeCoordinator implements MergeMiningCoordinator, BadChainListene
HeaderValidationMode.NONE,
false);
validationResult.errorMessage.ifPresent(errMsg -> addBadBlock(block));
return validationResult;
}
@Override
public Result rememberBlock(final Block block) {
public BlockProcessingResult rememberBlock(final Block block) {
debugLambda(LOG, "Remember block {}", block::toLogString);
final var chain = protocolContext.getBlockchain();
final var validationResult = validateBlock(block);
validationResult.blockProcessingOutputs.ifPresent(
result -> chain.storeBlock(block, result.receipts));
validationResult.getYield().ifPresent(result -> chain.storeBlock(block, result.getReceipts()));
return validationResult;
}

@ -19,7 +19,7 @@ import static org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoor
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -41,9 +41,9 @@ public interface MergeMiningCoordinator extends MiningCoordinator {
return true;
}
Result rememberBlock(final Block block);
BlockProcessingResult rememberBlock(final Block block);
Result validateBlock(final Block block);
BlockProcessingResult validateBlock(final Block block);
ForkchoiceResult updateForkChoice(
final BlockHeader newHead,

@ -18,7 +18,7 @@ import org.hyperledger.besu.consensus.merge.TransitionUtils;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.blockcreation.MiningCoordinator;
import org.hyperledger.besu.ethereum.blockcreation.PoWMiningCoordinator;
import org.hyperledger.besu.ethereum.chain.PoWObserver;
@ -137,12 +137,12 @@ public class TransitionCoordinator extends TransitionUtils<MiningCoordinator>
}
@Override
public Result rememberBlock(final Block block) {
public BlockProcessingResult rememberBlock(final Block block) {
return mergeCoordinator.rememberBlock(block);
}
@Override
public Result validateBlock(final Block block) {
public BlockProcessingResult validateBlock(final Block block) {
return mergeCoordinator.validateBlock(block);
}

@ -778,7 +778,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
BlockHeader headBlockHeader = nextBlockHeader(lastFinalizedHeader);
Block headBlock = new Block(headBlockHeader, BlockBody.empty());
assertThat(coordinator.rememberBlock(headBlock).blockProcessingOutputs).isPresent();
assertThat(coordinator.rememberBlock(headBlock).getYield()).isPresent();
var res =
coordinator.updateForkChoice(
@ -832,7 +832,7 @@ public class MergeCoordinatorTest implements MergeGenesisConfigHelper {
private void sendNewPayloadAndForkchoiceUpdate(
final Block block, final Optional<BlockHeader> finalizedHeader, final Hash safeHash) {
assertThat(coordinator.rememberBlock(block).blockProcessingOutputs).isPresent();
assertThat(coordinator.rememberBlock(block).getYield()).isPresent();
assertThat(
coordinator
.updateForkChoice(

@ -24,7 +24,7 @@ import org.hyperledger.besu.consensus.merge.MergeContext;
import org.hyperledger.besu.consensus.merge.PostMergeContext;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.GenesisState;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
@ -147,13 +147,15 @@ public class MergeReorgTest implements MergeGenesisConfigHelper {
}
private void appendBlock(final Block block) {
final Result result = coordinator.validateBlock(block);
result.blockProcessingOutputs.ifPresentOrElse(
outputs -> blockchain.appendBlock(block, outputs.receipts),
() -> {
throw new RuntimeException(result.errorMessage.get());
});
final BlockProcessingResult result = coordinator.validateBlock(block);
result
.getYield()
.ifPresentOrElse(
outputs -> blockchain.appendBlock(block, outputs.getReceipts()),
() -> {
throw new RuntimeException(result.errorMessage.get());
});
}
private List<Block> subChain(

@ -122,7 +122,7 @@ public class ProposalPayloadValidator {
blockValidator.validateAndProcessBlock(
protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL);
if (validationResult.blockProcessingOutputs.isEmpty()) {
if (!validationResult.isSuccessful()) {
LOG.info(
"{}: block did not pass validation. Reason {}",
ERROR_PREFIX,

@ -81,7 +81,7 @@ public class RoundChangeMessageValidator {
blockValidator.validateAndProcessBlock(
protocolContext, block, HeaderValidationMode.LIGHT, HeaderValidationMode.FULL);
if (validationResult.blockProcessingOutputs.isEmpty()) {
if (!validationResult.isSuccessful()) {
LOG.info(
"{}: block did not pass validation. Reason {}",
ERROR_PREFIX,

@ -1,5 +1,5 @@
/*
* Copyright 2020 ConsenSys AG.
* Copyright 2022 Hyperledger Besu Contributors.
*
* 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
@ -37,9 +37,8 @@ import org.hyperledger.besu.crypto.NodeKey;
import org.hyperledger.besu.crypto.NodeKeyUtils;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockValidator.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
@ -105,7 +104,7 @@ public class ProposalPayloadValidatorTest {
eq(block),
eq(HeaderValidationMode.LIGHT),
eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
assertThat(payloadValidator.validate(proposal.getSignedPayload())).isTrue();
}
@ -129,7 +128,7 @@ public class ProposalPayloadValidatorTest {
eq(block),
eq(HeaderValidationMode.LIGHT),
eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
assertThat(payloadValidator.validate(proposal.getSignedPayload())).isTrue();
}
@ -152,7 +151,7 @@ public class ProposalPayloadValidatorTest {
eq(block),
eq(HeaderValidationMode.LIGHT),
eq(HeaderValidationMode.FULL)))
.thenReturn(new Result("Failed"));
.thenReturn(new BlockProcessingResult("Failed"));
assertThat(payloadValidator.validate(proposal.getSignedPayload())).isFalse();
}
@ -228,7 +227,7 @@ public class ProposalPayloadValidatorTest {
eq(block),
eq(HeaderValidationMode.LIGHT),
eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
assertThat(payloadValidator.validate(proposal.getSignedPayload())).isFalse();
}
@ -262,7 +261,7 @@ public class ProposalPayloadValidatorTest {
eq(block),
eq(HeaderValidationMode.LIGHT),
eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(cmsValidator.validate(eq(cms), eq(hashWithoutCms))).thenReturn(false);
assertThat(payloadValidator.validate(proposal.getSignedPayload())).isFalse();
@ -297,7 +296,7 @@ public class ProposalPayloadValidatorTest {
eq(block),
eq(HeaderValidationMode.LIGHT),
eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(cmsValidator.validate(eq(cms), eq(hashWithoutCms))).thenReturn(true);
assertThat(payloadValidator.validate(proposal.getSignedPayload())).isTrue();

@ -37,9 +37,8 @@ import org.hyperledger.besu.consensus.qbft.payload.PreparePayload;
import org.hyperledger.besu.consensus.qbft.payload.PreparedRoundMetadata;
import org.hyperledger.besu.consensus.qbft.payload.RoundChangePayload;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockValidator.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
@ -106,7 +105,7 @@ public class ProposalValidatorTest {
any(),
eq(HeaderValidationMode.LIGHT),
eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
roundItems.put(ROUND_ID.ZERO, createRoundSpecificItems(0));
roundItems.put(ROUND_ID.ONE, createRoundSpecificItems(1));
@ -158,7 +157,7 @@ public class ProposalValidatorTest {
any(),
eq(HeaderValidationMode.LIGHT),
eq(HeaderValidationMode.FULL)))
.thenReturn(new Result("Failed"));
.thenReturn(new BlockProcessingResult("Failed"));
assertThat(roundItem.messageValidator.validate(proposal)).isFalse();
}

@ -1,5 +1,5 @@
/*
* Copyright 2020 ConsenSys AG.
* Copyright 2022 Hyperledger Besu Contributors.
*
* 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
@ -37,9 +37,8 @@ import org.hyperledger.besu.consensus.qbft.payload.RoundChangePayload;
import org.hyperledger.besu.consensus.qbft.statemachine.PreparedCertificate;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockValidator.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
@ -108,7 +107,7 @@ public class RoundChangeMessageValidatorTest {
public void roundChangeWithValidPiggyBackDataIsValid() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -135,7 +134,7 @@ public class RoundChangeMessageValidatorTest {
public void roundChangeWithBlockRoundMismatchingPreparesIsValid() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -164,7 +163,7 @@ public class RoundChangeMessageValidatorTest {
public void blockIsInvalidFailsValidation() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result("Failed"));
.thenReturn(new BlockProcessingResult("Failed"));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -207,7 +206,7 @@ public class RoundChangeMessageValidatorTest {
public void insufficientPiggyBackedPrepareMessagesIsInvalid() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -234,7 +233,7 @@ public class RoundChangeMessageValidatorTest {
public void prepareFromNonValidatorFails() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -263,7 +262,7 @@ public class RoundChangeMessageValidatorTest {
public void validationFailsIfPreparedMetadataContainsDifferentRoundToBlock() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -298,7 +297,7 @@ public class RoundChangeMessageValidatorTest {
public void validationFailsIfPreparesContainsDifferentRoundToBlock() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -335,7 +334,7 @@ public class RoundChangeMessageValidatorTest {
public void validationFailsIfPreparesContainsWrongHeight() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -372,7 +371,7 @@ public class RoundChangeMessageValidatorTest {
public void validationFailsIfPreparesHaveDuplicateAuthors() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -404,7 +403,7 @@ public class RoundChangeMessageValidatorTest {
public void validationFailsIfBlockExistsButNotPreparedMetadata() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(
@ -432,7 +431,7 @@ public class RoundChangeMessageValidatorTest {
public void validationFailsIfBlockHashDoesNotMatchPreparedMetadata() {
when(blockValidator.validateAndProcessBlock(
any(), any(), eq(HeaderValidationMode.LIGHT), eq(HeaderValidationMode.FULL)))
.thenReturn(new Result(new BlockProcessingOutputs(null, null)));
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(payloadValidator.validate(any())).thenReturn(true);
messageValidator =
new RoundChangeMessageValidator(

@ -25,7 +25,7 @@ import static org.hyperledger.besu.util.Slf4jLambdaHelper.traceLambda;
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
@ -217,7 +217,7 @@ public class EngineNewPayload extends ExecutionEngineJsonRpcMethod {
// execute block and return result response
final long startTimeMs = System.currentTimeMillis();
final BlockValidator.Result executionResult = mergeCoordinator.rememberBlock(block);
final BlockProcessingResult executionResult = mergeCoordinator.rememberBlock(block);
if (executionResult.errorMessage.isEmpty()) {
logImportedBlockInfo(block, (System.currentTimeMillis() - startTimeMs) / 1000.0);

@ -32,8 +32,8 @@ import org.hyperledger.besu.consensus.merge.MergeContext;
import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockValidator.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
@ -113,7 +113,8 @@ public class EngineNewPayloadTest {
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
when(mergeCoordinator.rememberBlock(any()))
.thenReturn(new Result(new BlockProcessingOutputs(null, List.of())));
.thenReturn(
new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(null, List.of()))));
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
@ -134,7 +135,7 @@ public class EngineNewPayloadTest {
.thenReturn(Optional.of(mockHash));
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
when(mergeCoordinator.rememberBlock(any())).thenReturn(new Result("error 42"));
when(mergeCoordinator.rememberBlock(any())).thenReturn(new BlockProcessingResult("error 42"));
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));
@ -229,7 +230,8 @@ public class EngineNewPayloadTest {
when(mergeCoordinator.latestValidAncestorDescendsFromTerminal(any(BlockHeader.class)))
.thenReturn(true);
when(mergeCoordinator.rememberBlock(any()))
.thenReturn(new Result("kablooey", new StorageException(new Exception())));
.thenReturn(
new BlockProcessingResult(Optional.empty(), new StorageException("database bedlam")));
var resp = resp(mockPayload(mockHeader, Collections.emptyList()));

@ -0,0 +1,47 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.ethereum;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import java.util.ArrayList;
import java.util.List;
public class BlockProcessingOutputs {
private final MutableWorldState worldState;
private final List<TransactionReceipt> receipts;
public BlockProcessingOutputs(
final MutableWorldState worldState, final List<TransactionReceipt> receipts) {
this.worldState = worldState;
this.receipts = receipts;
}
public static BlockProcessingOutputs empty() {
return new BlockProcessingOutputs(null, new ArrayList<>());
}
public MutableWorldState getWorldState() {
return worldState;
}
public List<TransactionReceipt> getReceipts() {
return receipts;
}
}

@ -0,0 +1,87 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.ethereum;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class BlockProcessingResult extends BlockValidationResult {
private final Optional<BlockProcessingOutputs> yield;
private final boolean isPartial;
public static final BlockProcessingResult FAILED = new BlockProcessingResult("processing failed");
public BlockProcessingResult(final Optional<BlockProcessingOutputs> yield) {
this.yield = yield;
this.isPartial = false;
}
public BlockProcessingResult(
final Optional<BlockProcessingOutputs> yield, final boolean isPartial) {
this.yield = yield;
this.isPartial = isPartial;
}
public BlockProcessingResult(
final Optional<BlockProcessingOutputs> yield, final String errorMessage) {
super(errorMessage);
this.yield = yield;
this.isPartial = false;
}
public BlockProcessingResult(
final Optional<BlockProcessingOutputs> yield, final Throwable cause) {
super(cause.getLocalizedMessage(), cause);
this.yield = yield;
this.isPartial = false;
}
public BlockProcessingResult(
final Optional<BlockProcessingOutputs> yield,
final String errorMessage,
final boolean isPartial) {
super(errorMessage);
this.yield = yield;
this.isPartial = isPartial;
}
public BlockProcessingResult(final String errorMessage) {
super(errorMessage);
this.isPartial = false;
this.yield = Optional.empty();
}
public Optional<BlockProcessingOutputs> getYield() {
return yield;
}
public boolean isPartial() {
return isPartial;
}
public List<TransactionReceipt> getReceipts() {
if (yield.isEmpty()) {
return new ArrayList<>();
} else {
return yield.get().getReceipts();
}
}
}

@ -0,0 +1,72 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.ethereum;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import java.util.Optional;
public class BlockValidationResult {
public final Optional<String> errorMessage;
public final Optional<Throwable> cause;
public final boolean success;
public BlockValidationResult() {
this.success = true;
this.errorMessage = Optional.empty();
this.cause = Optional.empty();
}
public BlockValidationResult(final String errorMessage) {
this.success = false;
this.errorMessage = Optional.of(errorMessage);
this.cause = Optional.empty();
}
public BlockValidationResult(final String errorMessage, final Throwable cause) {
this.success = false;
this.errorMessage = Optional.of(errorMessage);
this.cause = Optional.of(cause);
}
public boolean isSuccessful() {
return this.success;
}
public boolean isFailed() {
return !isSuccessful();
}
public Optional<Throwable> causedBy() {
return cause;
}
public boolean isInternalError() {
if (causedBy().isPresent()) {
Throwable t = causedBy().get();
// As new "internal only" types of exception are discovered, add them here.
if (t instanceof StorageException) {
return true;
} else if (t instanceof MerkleTrieException) {
return true;
}
}
return false;
}
}

@ -15,61 +15,20 @@
package org.hyperledger.besu.ethereum;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import java.util.List;
import java.util.Optional;
public interface BlockValidator {
class Result {
public final Optional<BlockProcessingOutputs> blockProcessingOutputs;
public final Optional<String> errorMessage;
public final Optional<Throwable> cause;
public final boolean isValid;
public Result(final BlockProcessingOutputs blockProcessingOutputs) {
this.blockProcessingOutputs = Optional.of(blockProcessingOutputs);
this.errorMessage = Optional.empty();
this.cause = Optional.empty();
this.isValid = true;
}
public Result(final String errorMessage) {
this.blockProcessingOutputs = Optional.empty();
this.errorMessage = Optional.of(errorMessage);
this.cause = Optional.empty();
this.isValid = false;
}
public Result(final String errorMessage, final Throwable cause) {
this.blockProcessingOutputs = Optional.empty();
this.errorMessage = Optional.of(errorMessage);
this.cause = Optional.of(cause);
this.isValid = false;
}
}
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;
}
}
Result validateAndProcessBlock(
BlockProcessingResult validateAndProcessBlock(
final ProtocolContext context,
final Block block,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode);
Result validateAndProcessBlock(
BlockProcessingResult validateAndProcessBlock(
final ProtocolContext context,
final Block block,
final HeaderValidationMode headerValidationMode,

@ -20,10 +20,13 @@ import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.goquorum.GoQuorumBlockProcessingResult;
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import java.util.ArrayList;
import java.util.Collections;
@ -63,7 +66,7 @@ public class MainnetBlockValidator implements BlockValidator {
* the block, empty if the block was deemed invalid or couldn't be processed
*/
@Override
public BlockValidator.Result validateAndProcessBlock(
public BlockProcessingResult validateAndProcessBlock(
final ProtocolContext context,
final Block block,
final HeaderValidationMode headerValidationMode,
@ -72,92 +75,110 @@ public class MainnetBlockValidator implements BlockValidator {
}
@Override
public BlockValidator.Result validateAndProcessBlock(
public BlockProcessingResult validateAndProcessBlock(
final ProtocolContext context,
final Block block,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode,
final boolean shouldPersist) {
final BlockHeader header = block.getHeader();
final MutableBlockchain blockchain = context.getBlockchain();
final Optional<BlockHeader> maybeParentHeader =
blockchain.getBlockHeader(header.getParentHash());
if (maybeParentHeader.isEmpty()) {
return handleAndReportFailure(
block, "Parent block with hash " + header.getParentHash() + " not present");
}
final BlockHeader parentHeader = maybeParentHeader.get();
if (!blockHeaderValidator.validateHeader(header, parentHeader, context, headerValidationMode)) {
return handleAndReportFailure(block, "Invalid block header");
}
final Optional<MutableWorldState> maybeWorldState =
context
.getWorldStateArchive()
.getMutable(parentHeader.getStateRoot(), parentHeader.getHash());
if (maybeWorldState.isEmpty()) {
return handleAndReportFailure(
block,
"Unable to process block because parent world state "
+ parentHeader.getStateRoot()
+ " is not available");
}
final MutableWorldState worldState =
shouldPersist ? maybeWorldState.get() : maybeWorldState.get().copy();
final BlockProcessor.Result result = processBlock(context, worldState, block);
if (result.isFailed() && result.causedBy().isPresent()) {
return handleAndReportFailure(block, "Error processing block", result);
} else if (result.isFailed()) {
return handleAndReportFailure(block, "Error processing block");
}
try {
final BlockHeader header = block.getHeader();
final MutableBlockchain blockchain = context.getBlockchain();
final Optional<BlockHeader> maybeParentHeader =
blockchain.getBlockHeader(header.getParentHash());
if (maybeParentHeader.isEmpty()) {
var retval =
new BlockProcessingResult(
"Parent block with hash " + header.getParentHash() + " not present");
handleAndLogImportFailure(block, retval);
return retval;
}
final BlockHeader parentHeader = maybeParentHeader.get();
List<TransactionReceipt> receipts = result.getReceipts();
if (!blockBodyValidator.validateBody(
context, block, receipts, worldState.rootHash(), ommerValidationMode)) {
return handleAndReportFailure(block, "Block body not valid");
}
if (!blockHeaderValidator.validateHeader(
header, parentHeader, context, headerValidationMode)) {
var retval = new BlockProcessingResult("header validation rule violated, see logs");
handleAndLogImportFailure(block, retval);
return retval;
}
if (!result.getPrivateReceipts().isEmpty()) {
// replace the public receipts for marker transactions with the private receipts if we are in
// goQuorumCompatibilityMode. That can be done now because we have validated the block.
final List<TransactionReceipt> privateTransactionReceipts = result.getPrivateReceipts();
final ArrayList<TransactionReceipt> resultingList = new ArrayList<>(receipts.size());
for (int i = 0; i < receipts.size(); i++) {
if (privateTransactionReceipts.get(i) != null) {
resultingList.add(privateTransactionReceipts.get(i));
} else {
resultingList.add(receipts.get(i));
final Optional<MutableWorldState> maybeWorldState =
context
.getWorldStateArchive()
.getMutable(parentHeader.getStateRoot(), parentHeader.getHash());
if (maybeWorldState.isEmpty()) {
var retval =
new BlockProcessingResult(
"Unable to process block because parent world state "
+ parentHeader.getStateRoot()
+ " is not available");
handleAndLogImportFailure(block, retval);
return retval;
}
final MutableWorldState worldState =
shouldPersist ? maybeWorldState.get() : maybeWorldState.get().copy();
var result = processBlock(context, worldState, block);
if (result.isFailed()) {
handleAndLogImportFailure(block, result);
return result;
} else {
List<TransactionReceipt> receipts =
result.getYield().map(BlockProcessingOutputs::getReceipts).orElse(new ArrayList<>());
if (!blockBodyValidator.validateBody(
context, block, receipts, worldState.rootHash(), ommerValidationMode)) {
handleAndLogImportFailure(block, result);
return new BlockProcessingResult("failed to validate output of imported block");
}
if (result instanceof GoQuorumBlockProcessingResult) {
var privateOutput = (GoQuorumBlockProcessingResult) result;
if (!privateOutput.getPrivateReceipts().isEmpty()) {
// replace the public receipts for marker transactions with the private receipts if we
// are in goQuorumCompatibilityMode. That can be done now because we have validated the
// block.
final List<TransactionReceipt> privateTransactionReceipts =
privateOutput.getPrivateReceipts();
final ArrayList<TransactionReceipt> resultingList = new ArrayList<>(receipts.size());
for (int i = 0; i < receipts.size(); i++) {
if (privateTransactionReceipts.get(i) != null) {
resultingList.add(privateTransactionReceipts.get(i));
} else {
resultingList.add(receipts.get(i));
}
}
receipts = Collections.unmodifiableList(resultingList);
}
}
return new BlockProcessingResult(
Optional.of(new BlockProcessingOutputs(worldState, receipts)));
}
receipts = Collections.unmodifiableList(resultingList);
} catch (StorageException | MerkleTrieException ex) {
var retval = new BlockProcessingResult(Optional.empty(), ex);
handleAndLogImportFailure(block, retval);
return retval;
}
return new Result(new BlockProcessingOutputs(worldState, receipts));
}
private Result handleAndReportFailure(final Block invalidBlock, final String reason) {
badBlockManager.addBadBlock(invalidBlock);
LOG.info("{}. Block {}", reason, invalidBlock.toLogString());
return new Result(reason);
}
private Result handleAndReportFailure(
final Block invalidBlock, final String reason, final BlockProcessor.Result result) {
private void handleAndLogImportFailure(
final Block invalidBlock, final BlockValidationResult result) {
if (result.causedBy().isPresent()) {
LOG.info("{}. Block {}, caused by {}", reason, invalidBlock.toLogString(), result.causedBy());
if (!result.internalError()) {
LOG.info(
"{}. Block {}, caused by {}",
result.errorMessage,
invalidBlock.toLogString(),
result.causedBy().get());
LOG.debug("with stack", result.causedBy().get());
if (!result.isInternalError()) {
badBlockManager.addBadBlock(invalidBlock);
}
return new Result(reason, result.causedBy().get());
} else {
LOG.info("{}. Block {}", reason, invalidBlock.toLogString());
LOG.info("{}. Block {}", result.errorMessage, invalidBlock.toLogString());
badBlockManager.addBadBlock(invalidBlock);
return new Result(reason);
}
}
@ -169,7 +190,7 @@ public class MainnetBlockValidator implements BlockValidator {
* @param block the block to be processed
* @return the result of processing the block
*/
protected BlockProcessor.Result processBlock(
protected BlockProcessingResult processBlock(
final ProtocolContext context, final MutableWorldState worldState, final Block block) {
return blockProcessor.processBlock(context.getBlockchain(), worldState, block);

@ -0,0 +1,44 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.ethereum.goquorum;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class GoQuorumBlockProcessingResult extends BlockProcessingResult {
public final Optional<BlockProcessingOutputs> privateYield;
public GoQuorumBlockProcessingResult(
final BlockProcessingOutputs mainnetYield, final BlockProcessingOutputs privateYield) {
super(Optional.of(mainnetYield));
this.privateYield = Optional.ofNullable(privateYield);
}
public List<TransactionReceipt> getPrivateReceipts() {
if (privateYield.isEmpty()) {
return new ArrayList<>();
} else {
return privateYield.get().getReceipts();
}
}
}

@ -19,6 +19,8 @@ import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.GoQuorumEnclave;
import org.hyperledger.besu.enclave.types.GoQuorumReceiveResponse;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -26,7 +28,6 @@ import org.hyperledger.besu.ethereum.core.GoQuorumPrivacyParameters;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionValidator;
@ -42,6 +43,7 @@ import org.hyperledger.besu.evm.log.LogsBloomFilter;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -78,7 +80,7 @@ public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
}
@Override
public Result processBlock(
public BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState publicWorldState,
final MutableWorldState privateWorldState,
@ -95,7 +97,7 @@ public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
for (final Transaction transaction : transactions) {
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
return AbstractBlockProcessor.Result.failed();
return new BlockProcessingResult(Optional.empty(), "insufficient gas");
}
final WorldUpdater publicWorldStateUpdater = publicWorldState.updater();
@ -124,7 +126,7 @@ public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
final ValidationResult<TransactionInvalidReason> validationResult =
validateTransaction(blockHeader, transaction, publicWorldStateUpdater);
if (!validationResult.isValid()) {
return AbstractBlockProcessor.Result.failed();
return new BlockProcessingResult(Optional.empty(), e);
}
}
} else { // public Transaction
@ -148,12 +150,14 @@ public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
null);
if (result.isInvalid()) {
LOG.info(
"Block processing error: transaction invalid '{}'. Block {} Transaction {}",
result.getValidationResult().getInvalidReason(),
blockHeader.getHash().toHexString(),
transaction.getHash().toHexString());
return AbstractBlockProcessor.Result.failed();
String errorMessage =
MessageFormat.format(
"Block processing error: transaction invalid '{}'. Block {} Transaction {}",
result.getValidationResult().getErrorMessage(),
blockHeader.getHash().toHexString(),
transaction.getHash().toHexString());
LOG.info(errorMessage);
return new BlockProcessingResult(Optional.empty(), errorMessage);
}
if (isGoQuorumPrivateTransaction) { // private transaction we are party to
@ -197,7 +201,7 @@ public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
if (!rewardCoinbase(publicWorldState, blockHeader, ommers, skipZeroBlockRewards)) {
// no need to log, rewardCoinbase logs the error.
return AbstractBlockProcessor.Result.failed();
return new BlockProcessingResult(Optional.empty(), "ommer too old");
}
// create the bloom for the private transactions in the block and store it
@ -214,7 +218,9 @@ public class GoQuorumBlockProcessor extends MainnetBlockProcessor {
publicWorldState.rootHash(), privateWorldState.rootHash());
privateStorageUpdater.commit();
return Result.successful(publicTxReceipts, privateTxReceipts);
var mainnetYield = new BlockProcessingOutputs(publicWorldState, publicTxReceipts);
var privateYield = new BlockProcessingOutputs(privateWorldState, privateTxReceipts);
return new GoQuorumBlockProcessingResult(mainnetYield, privateYield);
}
private ValidationResult<TransactionInvalidReason> validateTransaction(

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.goquorum;
import static org.hyperledger.besu.ethereum.goquorum.GoQuorumPrivateStateUtil.getPrivateWorldState;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
@ -49,7 +50,7 @@ public class GoQuorumBlockValidator extends MainnetBlockValidator {
}
@Override
protected BlockProcessor.Result processBlock(
protected BlockProcessingResult processBlock(
final ProtocolContext context, final MutableWorldState worldState, final Block block) {
final MutableWorldState privateWorldState =
getPrivateWorldState(goQuorumPrivacyParameters, worldState.rootHash(), block.getHash());

@ -16,6 +16,8 @@ package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.bonsai.BonsaiPersistedWorldState;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateUpdater;
import org.hyperledger.besu.ethereum.chain.Blockchain;
@ -31,12 +33,11 @@ import org.hyperledger.besu.evm.worldstate.WorldState;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import org.hyperledger.besu.plugin.data.TransactionType;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import com.google.common.collect.ImmutableList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -56,83 +57,6 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
static final int MAX_GENERATION = 6;
public static class Result implements BlockProcessor.Result {
private static final AbstractBlockProcessor.Result FAILED =
new AbstractBlockProcessor.Result(false, null);
private final boolean successful;
private Optional<Throwable> causedBy = Optional.empty();
private final List<TransactionReceipt> receipts;
private final List<TransactionReceipt> privateReceipts;
public static AbstractBlockProcessor.Result successful(
final List<TransactionReceipt> receipts) {
return new AbstractBlockProcessor.Result(true, ImmutableList.copyOf(receipts));
}
public static Result successful(
final List<TransactionReceipt> publicTxReceipts,
final List<TransactionReceipt> privateTxReceipts) {
return new AbstractBlockProcessor.Result(
true,
ImmutableList.copyOf(publicTxReceipts),
Collections.unmodifiableList(privateTxReceipts));
}
public static AbstractBlockProcessor.Result failed() {
return FAILED;
}
public static AbstractBlockProcessor.Result failedBecause(final Throwable t) {
return new AbstractBlockProcessor.Result(t);
}
Result(final Throwable t) {
this.successful = false;
this.receipts = null;
this.privateReceipts = Collections.emptyList();
this.causedBy = Optional.of(t);
}
Result(final boolean successful, final List<TransactionReceipt> receipts) {
this.successful = successful;
this.receipts = receipts;
this.privateReceipts = Collections.emptyList();
}
public Result(
final boolean successful,
final ImmutableList<TransactionReceipt> publicReceipts,
final List<TransactionReceipt> privateReceipts) {
this.successful = successful;
this.receipts = publicReceipts;
this.privateReceipts = privateReceipts;
}
@Override
public List<TransactionReceipt> getReceipts() {
return receipts;
}
@Override
public List<TransactionReceipt> getPrivateReceipts() {
return privateReceipts;
}
@Override
public boolean isSuccessful() {
return successful;
}
@Override
public Optional<Throwable> causedBy() {
return this.causedBy;
}
}
protected final MainnetTransactionProcessor transactionProcessor;
protected final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory;
@ -157,7 +81,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
}
@Override
public AbstractBlockProcessor.Result processBlock(
public BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final BlockHeader blockHeader,
@ -168,7 +92,7 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
long currentGasUsed = 0;
for (final Transaction transaction : transactions) {
if (!hasAvailableBlockBudget(blockHeader, transaction, currentGasUsed)) {
return AbstractBlockProcessor.Result.failed();
return new BlockProcessingResult(Optional.empty(), "provided gas insufficient");
}
final WorldUpdater worldStateUpdater = worldState.updater();
@ -189,15 +113,17 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
TransactionValidationParams.processingBlock(),
privateMetadataUpdater);
if (result.isInvalid()) {
LOG.info(
"Block processing error: transaction invalid '{}'. Block {} Transaction {}",
result.getValidationResult().getInvalidReason(),
blockHeader.getHash().toHexString(),
transaction.getHash().toHexString());
String errorMessage =
MessageFormat.format(
"Block processing error: transaction invalid {0}. Block {1} Transaction {2}",
result.getValidationResult().getErrorMessage(),
blockHeader.getHash().toHexString(),
transaction.getHash().toHexString());
LOG.info(errorMessage);
if (worldState instanceof BonsaiPersistedWorldState) {
((BonsaiWorldStateUpdater) worldStateUpdater).reset();
}
return AbstractBlockProcessor.Result.failed();
return new BlockProcessingResult(Optional.empty(), errorMessage);
}
worldStateUpdater.commit();
@ -213,17 +139,17 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
if (worldState instanceof BonsaiPersistedWorldState) {
((BonsaiWorldStateUpdater) worldState.updater()).reset();
}
return AbstractBlockProcessor.Result.failed();
return new BlockProcessingResult(Optional.empty(), "ommer too old");
}
try {
worldState.persist(blockHeader);
} catch (Exception e) {
LOG.error("failed persisting block", e);
return AbstractBlockProcessor.Result.failedBecause(e);
return new BlockProcessingResult(Optional.empty(), e);
}
return AbstractBlockProcessor.Result.successful(receipts);
return new BlockProcessingResult(Optional.of(new BlockProcessingOutputs(worldState, receipts)));
}
protected boolean hasAvailableBlockBudget(

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -22,10 +23,8 @@ import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.privacy.storage.PrivateMetadataUpdater;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import java.util.List;
import java.util.Optional;
/** Processes a block. */
public interface BlockProcessor {
@ -62,21 +61,6 @@ public interface BlockProcessor {
default boolean isFailed() {
return !isSuccessful();
}
default Optional<Throwable> causedBy() {
return Optional.empty();
}
default boolean internalError() {
if (causedBy().isPresent()) {
Throwable t = causedBy().get();
// As new "internal only" types of exception are discovered, add them here.
if (t instanceof StorageException) {
return true;
}
}
return false;
}
}
/**
@ -87,7 +71,7 @@ public interface BlockProcessor {
* @param block the block to process
* @return the block processing result
*/
default Result processBlock(
default BlockProcessingResult processBlock(
final Blockchain blockchain, final MutableWorldState worldState, final Block block) {
return processBlock(
blockchain,
@ -108,7 +92,7 @@ public interface BlockProcessor {
* @param ommers the block ommers
* @return the block processing result
*/
default Result processBlock(
default BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final BlockHeader blockHeader,
@ -128,7 +112,7 @@ public interface BlockProcessor {
* @param privateMetadataUpdater the updater used to update the private metadata for the block
* @return the block processing result
*/
Result processBlock(
BlockProcessingResult processBlock(
Blockchain blockchain,
MutableWorldState worldState,
BlockHeader blockHeader,
@ -145,7 +129,7 @@ public interface BlockProcessor {
* @param block the block to process
* @return the block processing result
*/
default Result processBlock(
default BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final MutableWorldState privateWorldState,

@ -45,12 +45,14 @@ public class MainnetBlockImporter implements BlockImporter {
blockValidator.validateAndProcessBlock(
context, block, headerValidationMode, ommerValidationMode);
result.blockProcessingOutputs.ifPresent(
processingOutputs ->
context.getBlockchain().appendBlock(block, processingOutputs.receipts));
result
.getYield()
.ifPresent(
processingOutputs ->
context.getBlockchain().appendBlock(block, processingOutputs.getReceipts()));
return result
.blockProcessingOutputs
.getYield()
.map(blockProcessingOutputs -> new BlockImportResult(true))
.orElseGet(() -> new BlockImportResult(false));
}

@ -18,6 +18,7 @@ import org.hyperledger.besu.config.GenesisConfigOptions;
import org.hyperledger.besu.config.PowAlgorithm;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.MainnetBlockValidator;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -698,7 +699,7 @@ public abstract class MainnetProtocolSpecs {
}
@Override
public Result processBlock(
public BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final BlockHeader blockHeader,

@ -20,6 +20,7 @@ import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.enclave.Enclave;
import org.hyperledger.besu.enclave.EnclaveClientException;
import org.hyperledger.besu.enclave.types.ReceiveResponse;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
@ -80,7 +81,7 @@ public class PrivacyBlockProcessor implements BlockProcessor {
}
@Override
public Result processBlock(
public BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final BlockHeader blockHeader,
@ -97,7 +98,7 @@ public class PrivacyBlockProcessor implements BlockProcessor {
final PrivateMetadataUpdater metadataUpdater =
new PrivateMetadataUpdater(blockHeader, privateStateStorage);
final Result result =
final BlockProcessingResult result =
blockProcessor.processBlock(
blockchain, worldState, blockHeader, transactions, ommers, metadataUpdater);
metadataUpdater.commit();

@ -19,6 +19,8 @@ import static org.hyperledger.besu.ethereum.privacy.PrivateStateRootResolver.EMP
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -43,6 +45,7 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes32;
import org.slf4j.Logger;
@ -80,7 +83,7 @@ public class PrivateGroupRehydrationBlockProcessor {
this.privateStateGenesisAllocator = privateStateGenesisAllocator;
}
public AbstractBlockProcessor.Result processBlock(
public BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final WorldStateArchive privateWorldStateArchive,
@ -106,7 +109,7 @@ public class PrivateGroupRehydrationBlockProcessor {
+ " remaining {}",
transaction.getGasLimit(),
remainingGasBudget);
return AbstractBlockProcessor.Result.failed();
return BlockProcessingResult.FAILED;
}
final WorldUpdater worldStateUpdater = worldState.updater();
@ -176,7 +179,7 @@ public class PrivateGroupRehydrationBlockProcessor {
false,
TransactionValidationParams.processingBlock());
if (result.isInvalid()) {
return AbstractBlockProcessor.Result.failed();
return BlockProcessingResult.FAILED;
}
gasUsed = transaction.getGasLimit() - result.getGasRemaining() + gasUsed;
@ -186,12 +189,12 @@ public class PrivateGroupRehydrationBlockProcessor {
}
if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) {
return AbstractBlockProcessor.Result.failed();
return BlockProcessingResult.FAILED;
}
metadataUpdater.commit();
return AbstractBlockProcessor.Result.successful(receipts);
BlockProcessingOutputs yield = new BlockProcessingOutputs(worldState, receipts);
return new BlockProcessingResult(Optional.of(yield));
}
void storePrivateMetadata(

@ -16,6 +16,8 @@ package org.hyperledger.besu.ethereum.privacy.storage.migration;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.chain.Blockchain;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
@ -34,6 +36,7 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -72,7 +75,7 @@ public class PrivateMigrationBlockProcessor {
protocolSpec.isSkipZeroBlockRewards());
}
public AbstractBlockProcessor.Result processBlock(
public BlockProcessingResult processBlock(
final Blockchain blockchain,
final MutableWorldState worldState,
final BlockHeader blockHeader,
@ -89,7 +92,7 @@ public class PrivateMigrationBlockProcessor {
+ " remaining {}",
transaction.getGasLimit(),
remainingGasBudget);
return AbstractBlockProcessor.Result.failed();
return BlockProcessingResult.FAILED;
}
final WorldUpdater worldStateUpdater = worldState.updater();
@ -108,7 +111,7 @@ public class PrivateMigrationBlockProcessor {
true,
TransactionValidationParams.processingBlock());
if (result.isInvalid()) {
return AbstractBlockProcessor.Result.failed();
return BlockProcessingResult.FAILED;
}
worldStateUpdater.commit();
@ -119,10 +122,10 @@ public class PrivateMigrationBlockProcessor {
}
if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) {
return AbstractBlockProcessor.Result.failed();
return BlockProcessingResult.FAILED;
}
return AbstractBlockProcessor.Result.successful(receipts);
BlockProcessingOutputs yield = new BlockProcessingOutputs(worldState, receipts);
return new BlockProcessingResult(Optional.of(yield));
}
private boolean rewardCoinbase(

@ -0,0 +1,253 @@
/*
* Copyright Hyperledger Besu Contributors.
*
* 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.ethereum;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.bonsai.BonsaiPersistedWorldState;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateArchive;
import org.hyperledger.besu.ethereum.bonsai.BonsaiWorldStateKeyValueStorage;
import org.hyperledger.besu.ethereum.bonsai.TrieLogManager;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider;
import org.hyperledger.besu.ethereum.mainnet.AbstractBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionProcessor;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.ethereum.worldstate.WorldStateStorage;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
public class BlockImportExceptionHandlingTest {
private final MainnetTransactionProcessor transactionProcessor =
mock(MainnetTransactionProcessor.class);
private final AbstractBlockProcessor.TransactionReceiptFactory transactionReceiptFactory =
mock(AbstractBlockProcessor.TransactionReceiptFactory.class);
private final BlockProcessor blockProcessor =
new MainnetBlockProcessor(
transactionProcessor,
transactionReceiptFactory,
Wei.ZERO,
BlockHeader::getCoinbase,
true,
Optional.empty());
private final BlockHeaderValidator blockHeaderValidator = mock(BlockHeaderValidator.class);
private final BlockBodyValidator blockBodyValidator = mock(BlockBodyValidator.class);
private final ProtocolContext protocolContext = mock(ProtocolContext.class);
protected final MutableBlockchain blockchain = mock(MutableBlockchain.class);
private final StorageProvider storageProvider = new InMemoryKeyValueStorageProvider();
private final WorldStateStorage worldStateStorage =
new BonsaiWorldStateKeyValueStorage(storageProvider);
private final WorldStateArchive worldStateArchive =
// contains a BonsaiPersistedWorldState which we need to spy on.
// do we need to also test with a DefaultWorldStateArchive?
spy(
new BonsaiWorldStateArchive(
new TrieLogManager(
blockchain, (BonsaiWorldStateKeyValueStorage) worldStateStorage, 1),
storageProvider,
blockchain));
private final BonsaiPersistedWorldState persisted =
spy(
new BonsaiPersistedWorldState(
(BonsaiWorldStateArchive) worldStateArchive,
(BonsaiWorldStateKeyValueStorage) worldStateStorage));
private final BadBlockManager badBlockManager = new BadBlockManager();
private MainnetBlockValidator mainnetBlockValidator;
@Before
public void setup() {
when(protocolContext.getBlockchain()).thenReturn(blockchain);
when(protocolContext.getWorldStateArchive()).thenReturn(worldStateArchive);
mainnetBlockValidator =
new MainnetBlockValidator(
blockHeaderValidator, blockBodyValidator, blockProcessor, badBlockManager);
}
@Test
public void shouldNotBadBlockWhenInternalErrorDuringPersisting() {
Mockito.doThrow(new StorageException("database problem")).when(persisted).persist(any());
Mockito.doReturn(persisted).when(worldStateArchive).getMutable();
Mockito.doReturn(Optional.of(persisted)).when(worldStateArchive).getMutable(any(), any());
Block goodBlock =
new BlockDataGenerator()
.block(
BlockDataGenerator.BlockOptions.create()
.setBlockNumber(0)
.hasTransactions(false)
.setBlockHeaderFunctions(new MainnetBlockHeaderFunctions()));
when(blockchain.getBlockHeader(any(Hash.class)))
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader()));
when(blockHeaderValidator.validateHeader(
any(BlockHeader.class),
any(BlockHeader.class),
eq(protocolContext),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(true);
when(blockBodyValidator.validateBody(
eq(protocolContext),
eq(goodBlock),
any(),
any(),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(true);
assertThat(badBlockManager.getBadBlocks()).isEmpty();
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
goodBlock,
HeaderValidationMode.DETACHED_ONLY,
HeaderValidationMode.DETACHED_ONLY);
assertThat(badBlockManager.getBadBlocks()).isEmpty();
}
@Test
public void shouldNotBadBlockWhenInternalErrorOnBlockLookup() {
Block goodBlock =
new BlockDataGenerator()
.block(
BlockDataGenerator.BlockOptions.create()
.setBlockNumber(0)
.hasTransactions(false)
.setBlockHeaderFunctions(new MainnetBlockHeaderFunctions()));
when(blockchain.getBlockHeader(any(Hash.class)))
.thenThrow(new StorageException("database problem"));
when(blockHeaderValidator.validateHeader(
any(BlockHeader.class),
any(BlockHeader.class),
eq(protocolContext),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(true);
when(blockBodyValidator.validateBody(
eq(protocolContext),
eq(goodBlock),
any(),
any(),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(true);
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0);
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
goodBlock,
HeaderValidationMode.DETACHED_ONLY,
HeaderValidationMode.DETACHED_ONLY);
assertThat(badBlockManager.getBadBlocks()).isEmpty();
}
@Test
public void shouldNotBadBlockWhenInternalErrorDuringValidateHeader() {
Block goodBlock =
new BlockDataGenerator()
.block(
BlockDataGenerator.BlockOptions.create()
.setBlockNumber(0)
.hasTransactions(false)
.setBlockHeaderFunctions(new MainnetBlockHeaderFunctions()));
when(blockchain.getBlockHeader(any(Hash.class)))
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader()));
when(blockHeaderValidator.validateHeader(
any(BlockHeader.class),
any(BlockHeader.class),
eq(protocolContext),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenThrow(new StorageException("database problem"));
assertThat(badBlockManager.getBadBlocks()).isEmpty();
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
goodBlock,
HeaderValidationMode.DETACHED_ONLY,
HeaderValidationMode.DETACHED_ONLY);
assertThat(badBlockManager.getBadBlocks()).isEmpty();
}
@Test
public void shouldNotBadBlockWhenInternalErrorDuringValidateBody() {
Mockito.doNothing().when(persisted).persist(any());
Mockito.doReturn(persisted).when(worldStateArchive).getMutable();
Mockito.doReturn(Optional.of(persisted)).when(worldStateArchive).getMutable(any(), any());
Block goodBlock =
new BlockDataGenerator()
.block(
BlockDataGenerator.BlockOptions.create()
.setBlockNumber(0)
.hasTransactions(false)
.setBlockHeaderFunctions(new MainnetBlockHeaderFunctions()));
when(blockchain.getBlockHeader(any(Hash.class)))
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader()));
when(blockHeaderValidator.validateHeader(
any(BlockHeader.class),
any(BlockHeader.class),
eq(protocolContext),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(true);
when(blockBodyValidator.validateBody(
eq(protocolContext),
eq(goodBlock),
any(),
any(),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenThrow(new StorageException("database problem"));
assertThat(badBlockManager.getBadBlocks()).isEmpty();
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
goodBlock,
HeaderValidationMode.DETACHED_ONLY,
HeaderValidationMode.DETACHED_ONLY);
assertThat(badBlockManager.getBadBlocks()).isEmpty();
}
// cover validate body failing on bad receipts
}

@ -29,17 +29,13 @@ import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.BlockBodyValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockHeaderValidator;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.plugin.services.exception.StorageException;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import org.junit.Before;
@ -141,25 +137,7 @@ public class MainnetBlockValidatorTest {
when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class)))
.thenReturn(Optional.of(mock(MutableWorldState.class)));
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(badBlock)))
.thenReturn(
new BlockProcessor.Result() {
@SuppressWarnings("unchecked")
@Override
public List<TransactionReceipt> getReceipts() {
return Collections.EMPTY_LIST;
}
@SuppressWarnings("unchecked")
@Override
public List<TransactionReceipt> getPrivateReceipts() {
return Collections.EMPTY_LIST;
}
@Override
public boolean isSuccessful() {
return false;
}
});
.thenReturn(new BlockProcessingResult(Optional.empty()));
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0);
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
@ -182,25 +160,7 @@ public class MainnetBlockValidatorTest {
when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class)))
.thenReturn(Optional.of(mock(MutableWorldState.class)));
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(badBlock)))
.thenReturn(
new BlockProcessor.Result() {
@SuppressWarnings("unchecked")
@Override
public List<TransactionReceipt> getReceipts() {
return Collections.EMPTY_LIST;
}
@SuppressWarnings("unchecked")
@Override
public List<TransactionReceipt> getPrivateReceipts() {
return Collections.EMPTY_LIST;
}
@Override
public boolean isSuccessful() {
return true;
}
});
.thenReturn(new BlockProcessingResult(Optional.empty()));
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0);
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
@ -223,78 +183,7 @@ public class MainnetBlockValidatorTest {
when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class)))
.thenReturn(Optional.of(mock(MutableWorldState.class)));
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(badBlock)))
.thenReturn(
new BlockProcessor.Result() {
@SuppressWarnings("unchecked")
@Override
public List<TransactionReceipt> getReceipts() {
return Collections.EMPTY_LIST;
}
@SuppressWarnings("unchecked")
@Override
public List<TransactionReceipt> getPrivateReceipts() {
return Collections.EMPTY_LIST;
}
@Override
public boolean isSuccessful() {
return true;
}
});
when(blockBodyValidator.validateBody(
eq(protocolContext),
eq(badBlock),
any(),
any(),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(true);
assertThat(badBlockManager.getBadBlocks().size()).isEqualTo(0);
mainnetBlockValidator.validateAndProcessBlock(
protocolContext,
badBlock,
HeaderValidationMode.DETACHED_ONLY,
HeaderValidationMode.DETACHED_ONLY);
assertThat(badBlockManager.getBadBlocks()).isEmpty();
}
@Test
public void shouldNotCacheWhenInternalError() {
when(blockchain.getBlockHeader(any(Hash.class)))
.thenReturn(Optional.of(new BlockHeaderTestFixture().buildHeader()));
when(blockHeaderValidator.validateHeader(
any(BlockHeader.class),
any(BlockHeader.class),
eq(protocolContext),
eq(HeaderValidationMode.DETACHED_ONLY)))
.thenReturn(true);
when(worldStateArchive.getMutable(any(Hash.class), any(Hash.class)))
.thenReturn(Optional.of(mock(MutableWorldState.class)));
when(blockProcessor.processBlock(eq(blockchain), any(MutableWorldState.class), eq(badBlock)))
.thenReturn(
new BlockProcessor.Result() {
@SuppressWarnings("unchecked")
@Override
public List<TransactionReceipt> getReceipts() {
return Collections.EMPTY_LIST;
}
@SuppressWarnings("unchecked")
@Override
public List<TransactionReceipt> getPrivateReceipts() {
return Collections.EMPTY_LIST;
}
@Override
public boolean isSuccessful() {
return false;
}
@Override
public Optional<Throwable> causedBy() {
return Optional.of(new StorageException("database bedlam"));
}
});
.thenReturn(new BlockProcessingResult(Optional.empty()));
when(blockBodyValidator.validateBody(
eq(protocolContext),
eq(badBlock),

@ -27,6 +27,7 @@ import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.blockcreation.AbstractBlockCreator;
import org.hyperledger.besu.ethereum.chain.GenesisState;
@ -42,8 +43,8 @@ import org.hyperledger.besu.ethereum.core.TransactionTestFixture;
import org.hyperledger.besu.ethereum.eth.transactions.ImmutableTransactionPoolConfiguration;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.GasPricePendingTransactionsSorter;
import org.hyperledger.besu.ethereum.mainnet.BlockProcessor;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.MiningBeneficiaryCalculator;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
import org.hyperledger.besu.ethereum.storage.StorageProvider;
import org.hyperledger.besu.ethereum.storage.keyvalue.KeyValueSegmentIdentifier;
@ -340,7 +341,7 @@ public class BonsaiSnapshotIsolationTests {
.getBlock();
}
private BlockProcessor.Result executeBlock(final MutableWorldState ws, final Block block) {
private BlockProcessingResult executeBlock(final MutableWorldState ws, final Block block) {
var res =
protocolSchedule
.getByBlockNumber(0)

@ -309,12 +309,12 @@ public class BackwardSyncContext {
block,
HeaderValidationMode.FULL,
HeaderValidationMode.NONE);
if (optResult.blockProcessingOutputs.isPresent()) {
if (optResult.getYield().isPresent()) {
traceLambda(LOG, "Block {} was validated, going to import it", block::toLogString);
optResult.blockProcessingOutputs.get().worldState.persist(block.getHeader());
optResult.getYield().get().getWorldState().persist(block.getHeader());
this.getProtocolContext()
.getBlockchain()
.appendBlock(block, optResult.blockProcessingOutputs.get().receipts);
.appendBlock(block, optResult.getYield().get().getReceipts());
possiblyMoveHead(block);
} else {
emitBadChainEvent(block);

@ -28,8 +28,9 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.config.StubGenesisConfigOptions;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
@ -52,6 +53,7 @@ import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import javax.annotation.Nonnull;
@ -137,9 +139,10 @@ public class BackwardSyncContextTest {
invocation -> {
final Object[] arguments = invocation.getArguments();
Block block = (Block) arguments[1];
return new Result(
new BlockValidator.BlockProcessingOutputs(
new ReferenceTestWorldState(), blockDataGenerator.receipts(block)));
return new BlockProcessingResult(
Optional.of(
new BlockProcessingOutputs(
new ReferenceTestWorldState(), blockDataGenerator.receipts(block))));
});
backwardChain = inMemoryBackwardChain();
@ -295,7 +298,7 @@ public class BackwardSyncContextTest {
backwardChain.prependAncestorsHeader(block.getHeader());
doReturn(blockValidator).when(context).getBlockValidatorForBlock(any());
Result result = new Result("custom error");
BlockProcessingResult result = new BlockProcessingResult("custom error");
doReturn(result).when(blockValidator).validateAndProcessBlock(any(), any(), any(), any());
assertThatThrownBy(() -> context.saveBlock(block))

@ -24,8 +24,8 @@ import static org.mockito.Mockito.when;
import org.hyperledger.besu.config.StubGenesisConfigOptions;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.BlockValidator;
import org.hyperledger.besu.ethereum.BlockValidator.Result;
import org.hyperledger.besu.ethereum.BlockProcessingOutputs;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.chain.MutableBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockDataGenerator;
@ -43,6 +43,7 @@ import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import javax.annotation.Nonnull;
@ -125,9 +126,10 @@ public class ForwardSyncStepTest {
invocation -> {
final Object[] arguments = invocation.getArguments();
Block block = (Block) arguments[1];
return new Result(
new BlockValidator.BlockProcessingOutputs(
new ReferenceTestWorldState(), blockDataGenerator.receipts(block)));
return new BlockProcessingResult(
Optional.of(
new BlockProcessingOutputs(
new ReferenceTestWorldState(), blockDataGenerator.receipts(block))));
});
}

Loading…
Cancel
Save