Skip to content

Commit

Permalink
Disable body validation for POS networks during sync (hyperledger#7646)
Browse files Browse the repository at this point in the history
Signed-off-by: Jason Frame <[email protected]>
Co-authored-by: Stefan Pingel <[email protected]>
  • Loading branch information
jframe and pinges authored Sep 20, 2024
1 parent 637ebcb commit e1f4489
Show file tree
Hide file tree
Showing 14 changed files with 255 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.Request;
import org.hyperledger.besu.ethereum.core.TransactionReceipt;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;

import java.util.List;
Expand Down Expand Up @@ -83,22 +84,24 @@ BlockProcessingResult validateAndProcessBlock(
final boolean shouldRecordBadBlock);

/**
* Performs fast block validation with the given context, block, transaction receipts, requests,
* header validation mode, and ommer validation mode.
* Performs fast block validation appropriate for use during syncing skipping transaction receipt
* roots and receipts roots as these are done during the download of the blocks.
*
* @param context the protocol context
* @param block the block to validate
* @param receipts the transaction receipts
* @param requests the requests
* @param headerValidationMode the header validation mode
* @param ommerValidationMode the ommer validation mode
* @param bodyValidationMode the body validation mode
* @return true if the block is valid, false otherwise
*/
boolean fastBlockValidation(
boolean validateBlockForSyncing(
final ProtocolContext context,
final Block block,
final List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode);
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.plugin.services.exception.StorageException;
Expand Down Expand Up @@ -247,13 +248,14 @@ protected BlockProcessingResult processBlock(
}

@Override
public boolean fastBlockValidation(
public boolean validateBlockForSyncing(
final ProtocolContext context,
final Block block,
final List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode) {
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {
final BlockHeader header = block.getHeader();
if (!blockHeaderValidator.validateHeader(header, context, headerValidationMode)) {
String description = String.format("Failed header validation (%s)", headerValidationMode);
Expand All @@ -262,7 +264,7 @@ public boolean fastBlockValidation(
}

if (!blockBodyValidator.validateBodyLight(
context, block, receipts, requests, ommerValidationMode)) {
context, block, receipts, requests, ommerValidationMode, bodyValidationMode)) {
badBlockManager.addBadBlock(
block, BadBlockCause.fromValidationFailure("Failed body validation (light)"));
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import org.hyperledger.besu.ethereum.ProtocolContext;
import org.hyperledger.besu.ethereum.mainnet.BlockImportResult;
import org.hyperledger.besu.ethereum.mainnet.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;

import java.util.List;
Expand Down Expand Up @@ -73,10 +74,11 @@ BlockImportResult importBlock(
* @return {@code BlockImportResult}
* @see BlockImportResult
*/
BlockImportResult fastImportBlock(
BlockImportResult importBlockForSyncing(
ProtocolContext context,
Block block,
List<TransactionReceipt> receipts,
HeaderValidationMode headerValidationMode,
HeaderValidationMode ommerValidationMode);
HeaderValidationMode ommerValidationMode,
BodyValidationMode bodyValidationMode);
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,11 @@ public boolean validateBodyLight(
final Block block,
final List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode ommerValidationMode) {
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {

return super.validateBodyLight(context, block, receipts, requests, ommerValidationMode)
return super.validateBodyLight(
context, block, receipts, requests, ommerValidationMode, bodyValidationMode)
&& validateTransactionGasPrice(block);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,5 +59,6 @@ boolean validateBodyLight(
Block block,
List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode ommerValidationMode);
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet;

public enum BodyValidationMode {
/** No Validation. data must be pre-validated */
NONE,

/** Skip receipts and transactions root validation */
LIGHT,

/** Fully validate the body */
FULL;
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import java.util.Optional;
import java.util.Set;

import com.google.common.annotations.VisibleForTesting;
import org.apache.tuweni.bytes.Bytes32;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand All @@ -55,7 +56,8 @@ public boolean validateBody(
final Hash worldStateRootHash,
final HeaderValidationMode ommerValidationMode) {

if (!validateBodyLight(context, block, receipts, requests, ommerValidationMode)) {
if (!validateBodyLight(
context, block, receipts, requests, ommerValidationMode, BodyValidationMode.FULL)) {
return false;
}

Expand All @@ -77,18 +79,26 @@ public boolean validateBodyLight(
final Block block,
final List<TransactionReceipt> receipts,
final Optional<List<Request>> requests,
final HeaderValidationMode ommerValidationMode) {
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {
if (bodyValidationMode == BodyValidationMode.NONE) {
return true;
}

final BlockHeader header = block.getHeader();
final BlockBody body = block.getBody();

final Bytes32 transactionsRoot = BodyValidation.transactionsRoot(body.getTransactions());
if (!validateTransactionsRoot(header, header.getTransactionsRoot(), transactionsRoot)) {
return false;
}
// these checks are only needed for full validation and can be skipped for light validation
if (bodyValidationMode == BodyValidationMode.FULL) {
final Bytes32 transactionsRoot = BodyValidation.transactionsRoot(body.getTransactions());
if (!validateTransactionsRoot(header, header.getTransactionsRoot(), transactionsRoot)) {
return false;
}

final Bytes32 receiptsRoot = BodyValidation.receiptsRoot(receipts);
if (!validateReceiptsRoot(header, header.getReceiptsRoot(), receiptsRoot)) {
return false;
final Bytes32 receiptsRoot = BodyValidation.receiptsRoot(receipts);
if (!validateReceiptsRoot(header, header.getReceiptsRoot(), receiptsRoot)) {
return false;
}
}

final long gasUsed =
Expand All @@ -115,7 +125,8 @@ public boolean validateBodyLight(
return true;
}

private static boolean validateTransactionsRoot(
@VisibleForTesting
protected boolean validateTransactionsRoot(
final BlockHeader header, final Bytes32 expected, final Bytes32 actual) {
if (!expected.equals(actual)) {
LOG.info(
Expand Down Expand Up @@ -157,7 +168,8 @@ private static boolean validateGasUsed(
return true;
}

private static boolean validateReceiptsRoot(
@VisibleForTesting
protected boolean validateReceiptsRoot(
final BlockHeader header, final Bytes32 expected, final Bytes32 actual) {
if (!expected.equals(actual)) {
LOG.warn(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,20 +57,22 @@ public synchronized BlockImportResult importBlock(
}

@Override
public BlockImportResult fastImportBlock(
public BlockImportResult importBlockForSyncing(
final ProtocolContext context,
final Block block,
final List<TransactionReceipt> receipts,
final HeaderValidationMode headerValidationMode,
final HeaderValidationMode ommerValidationMode) {
final HeaderValidationMode ommerValidationMode,
final BodyValidationMode bodyValidationMode) {

if (blockValidator.fastBlockValidation(
if (blockValidator.validateBlockForSyncing(
context,
block,
receipts,
block.getBody().getRequests(),
headerValidationMode,
ommerValidationMode)) {
ommerValidationMode,
bodyValidationMode)) {
context.getBlockchain().appendBlock(block, receipts);
return new BlockImportResult(true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
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.BodyValidationMode;
import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode;
import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
Expand Down Expand Up @@ -98,7 +99,8 @@ public void setup() {
when(blockHeaderValidator.validateHeader(any(), any(), any(), any())).thenReturn(true);
when(blockBodyValidator.validateBody(any(), any(), any(), any(), any(), any()))
.thenReturn(true);
when(blockBodyValidator.validateBodyLight(any(), any(), any(), any(), any())).thenReturn(true);
when(blockBodyValidator.validateBodyLight(any(), any(), any(), any(), any(), any()))
.thenReturn(true);
when(blockProcessor.processBlock(any(), any(), any())).thenReturn(successfulProcessingResult);
when(blockProcessor.processBlock(any(), any(), any(), any()))
.thenReturn(successfulProcessingResult);
Expand Down Expand Up @@ -343,55 +345,65 @@ public void validateAndProcessBlock_withShouldRecordBadBlockNotSet() {
}

@Test
public void fastBlockValidation_onSuccess() {
public void validateBlockForSyncing_onSuccess() {
final boolean isValid =
mainnetBlockValidator.fastBlockValidation(
mainnetBlockValidator.validateBlockForSyncing(
protocolContext,
block,
Collections.emptyList(),
block.getBody().getRequests(),
HeaderValidationMode.FULL,
HeaderValidationMode.FULL);
HeaderValidationMode.FULL,
BodyValidationMode.FULL);

assertThat(isValid).isTrue();
assertNoBadBlocks();
}

@Test
public void fastBlockValidation_onFailedHeaderValidation() {
final HeaderValidationMode validationMode = HeaderValidationMode.FULL;
public void validateBlockValidation_onFailedHeaderForSyncing() {
final HeaderValidationMode headerValidationMode = HeaderValidationMode.FULL;
when(blockHeaderValidator.validateHeader(
any(BlockHeader.class), eq(protocolContext), eq(validationMode)))
any(BlockHeader.class), eq(protocolContext), eq(headerValidationMode)))
.thenReturn(false);
final BodyValidationMode bodyValidationMode = BodyValidationMode.FULL;

final boolean isValid =
mainnetBlockValidator.fastBlockValidation(
mainnetBlockValidator.validateBlockForSyncing(
protocolContext,
block,
Collections.emptyList(),
block.getBody().getRequests(),
validationMode,
validationMode);
headerValidationMode,
headerValidationMode,
bodyValidationMode);

assertThat(isValid).isFalse();
assertBadBlockIsTracked(block);
}

@Test
public void fastBlockValidation_onFailedBodyValidation() {
final HeaderValidationMode validationMode = HeaderValidationMode.FULL;
public void validateBlockValidation_onFailedBodyForSyncing() {
final HeaderValidationMode headerValidationMode = HeaderValidationMode.FULL;
final BodyValidationMode bodyValidationMode = BodyValidationMode.FULL;
when(blockBodyValidator.validateBodyLight(
eq(protocolContext), eq(block), any(), any(), eq(validationMode)))
eq(protocolContext),
eq(block),
any(),
any(),
eq(headerValidationMode),
eq(bodyValidationMode)))
.thenReturn(false);

final boolean isValid =
mainnetBlockValidator.fastBlockValidation(
mainnetBlockValidator.validateBlockForSyncing(
protocolContext,
block,
Collections.emptyList(),
block.getBody().getRequests(),
validationMode,
validationMode);
headerValidationMode,
headerValidationMode,
bodyValidationMode);

assertThat(isValid).isFalse();
assertBadBlockIsTracked(block);
Expand Down
Loading

0 comments on commit e1f4489

Please sign in to comment.