From f27024ba392e2357fec7bd5339bc643dab4399de Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Wed, 25 Oct 2023 16:40:47 +1000 Subject: [PATCH 1/9] Add option to clique to skip creating empty blocks Signed-off-by: Jason Frame --- .../CliqueBesuControllerBuilder.java | 5 +- .../besu/config/CliqueConfigOptions.java | 5 + .../blockcreation/CliqueBlockMiner.java | 16 +- .../blockcreation/CliqueMinerExecutor.java | 8 +- .../blockcreation/CliqueBlockMinerTest.java | 180 ++++++++++++++++++ .../CliqueMinerExecutorTest.java | 9 +- .../ethereum/blockcreation/BlockMiner.java | 8 + 7 files changed, 224 insertions(+), 7 deletions(-) create mode 100644 consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMinerTest.java diff --git a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java index baad246a76b..8c1f9c011ed 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java @@ -53,6 +53,7 @@ public class CliqueBesuControllerBuilder extends BesuControllerBuilder { private Address localAddress; private EpochManager epochManager; private long secondsBetweenBlocks; + private boolean createEmptyBlocks; private final BlockInterface blockInterface = new CliqueBlockInterface(); @Override @@ -61,6 +62,7 @@ protected void prepForBuild() { final CliqueConfigOptions cliqueConfig = configOptionsSupplier.get().getCliqueConfigOptions(); final long blocksPerEpoch = cliqueConfig.getEpochLength(); secondsBetweenBlocks = cliqueConfig.getBlockPeriodSeconds(); + createEmptyBlocks = cliqueConfig.getCreateEmptyBlocks(); epochManager = new EpochManager(blocksPerEpoch); } @@ -91,7 +93,8 @@ protected MiningCoordinator createMiningCoordinator( protocolContext.getConsensusContext(CliqueContext.class).getValidatorProvider(), localAddress, secondsBetweenBlocks), - epochManager); + epochManager, + createEmptyBlocks); final CliqueMiningCoordinator miningCoordinator = new CliqueMiningCoordinator( protocolContext.getBlockchain(), diff --git a/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java index 66acf03e558..7ce29ab1b36 100644 --- a/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java @@ -28,6 +28,7 @@ public class CliqueConfigOptions { private static final long DEFAULT_EPOCH_LENGTH = 30_000; private static final int DEFAULT_BLOCK_PERIOD_SECONDS = 15; + private static final boolean DEFAULT_CREATE_EMPTY_BLOCKS = true; private final ObjectNode cliqueConfigRoot; @@ -59,6 +60,10 @@ public int getBlockPeriodSeconds() { cliqueConfigRoot, "blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS); } + public boolean getCreateEmptyBlocks() { + return JsonUtil.getBoolean(cliqueConfigRoot, "createemptyblocks", DEFAULT_CREATE_EMPTY_BLOCKS); + } + /** * As map. * diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java index d9e4c6f38f1..76af1541259 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.ethereum.blockcreation.AbstractBlockScheduler; import org.hyperledger.besu.ethereum.blockcreation.BlockMiner; import org.hyperledger.besu.ethereum.chain.MinedBlockObserver; +import org.hyperledger.besu.ethereum.core.Block; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.util.Subscribers; @@ -28,8 +29,10 @@ /** The Clique block miner. */ public class CliqueBlockMiner extends BlockMiner { + // private static final Logger LOG = LoggerFactory.getLogger(CliqueBlockMiner.class); private final Address localAddress; + private final boolean createEmptyBlocks; /** * Instantiates a new Clique block miner. @@ -49,9 +52,11 @@ public CliqueBlockMiner( final Subscribers observers, final AbstractBlockScheduler scheduler, final BlockHeader parentHeader, - final Address localAddress) { + final Address localAddress, + final boolean createEmptyBlocks) { super(blockCreator, protocolSchedule, protocolContext, observers, scheduler, parentHeader); this.localAddress = localAddress; + this.createEmptyBlocks = createEmptyBlocks; } @Override @@ -63,4 +68,13 @@ protected boolean mineBlock() throws InterruptedException { return true; // terminate mining. } + + @Override + protected boolean shouldImportBlock(final Block block) { + if (createEmptyBlocks) { + return true; + } + + return !block.getBody().getTransactions().isEmpty(); + } } diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java index 81b754b267c..c0ac9ebeef3 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java @@ -47,6 +47,7 @@ public class CliqueMinerExecutor extends AbstractMinerExecutor private final Address localAddress; private final NodeKey nodeKey; private final EpochManager epochManager; + private final boolean createEmptyBlocks; /** * Instantiates a new Clique miner executor. @@ -66,11 +67,13 @@ public CliqueMinerExecutor( final NodeKey nodeKey, final MiningParameters miningParams, final AbstractBlockScheduler blockScheduler, - final EpochManager epochManager) { + final EpochManager epochManager, + final boolean createEmptyBlocks) { super(protocolContext, protocolSchedule, transactionPool, miningParams, blockScheduler); this.nodeKey = nodeKey; this.localAddress = Util.publicKeyToAddress(nodeKey.getPublicKey()); this.epochManager = epochManager; + this.createEmptyBlocks = createEmptyBlocks; miningParams.setCoinbase(localAddress); } @@ -98,7 +101,8 @@ public CliqueBlockMiner createMiner( observers, blockScheduler, parentHeader, - localAddress); + localAddress, + createEmptyBlocks); } @Override diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMinerTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMinerTest.java new file mode 100644 index 00000000000..ed1cfced5e3 --- /dev/null +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMinerTest.java @@ -0,0 +1,180 @@ +/* + * 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.consensus.clique.blockcreation; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import org.hyperledger.besu.consensus.clique.CliqueContext; +import org.hyperledger.besu.consensus.common.validator.ValidatorProvider; +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.ethereum.ProtocolContext; +import org.hyperledger.besu.ethereum.blockcreation.BlockCreator; +import org.hyperledger.besu.ethereum.blockcreation.DefaultBlockScheduler; +import org.hyperledger.besu.ethereum.blockcreation.txselection.TransactionSelectionResults; +import org.hyperledger.besu.ethereum.chain.MinedBlockObserver; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.BlockImporter; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.TransactionTestFixture; +import org.hyperledger.besu.ethereum.mainnet.BlockImportResult; +import org.hyperledger.besu.ethereum.mainnet.DefaultProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.HeaderValidationMode; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; +import org.hyperledger.besu.util.Subscribers; + +import java.math.BigInteger; +import java.util.List; +import java.util.Optional; +import java.util.function.Function; + +import com.google.common.collect.Lists; +import org.junit.jupiter.api.Test; + +class CliqueBlockMinerTest { + + @Test + void doesNotMineBlockIfNoTransactionsWhenEmptyBlocksNotAllowed() throws InterruptedException { + final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); + + final Block blockToCreate = + new Block( + headerBuilder.buildHeader(), new BlockBody(Lists.newArrayList(), Lists.newArrayList())); + + final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); + when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(List.of(Address.ZERO)); + + final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, null); + final ProtocolContext protocolContext = + new ProtocolContext(null, null, cliqueContext, Optional.empty()); + + final CliqueBlockCreator blockCreator = mock(CliqueBlockCreator.class); + final Function blockCreatorSupplier = + (parentHeader) -> blockCreator; + when(blockCreator.createBlock(anyLong())) + .thenReturn( + new BlockCreator.BlockCreationResult(blockToCreate, new TransactionSelectionResults())); + + final BlockImporter blockImporter = mock(BlockImporter.class); + final ProtocolSpec protocolSpec = mock(ProtocolSpec.class); + + final ProtocolSchedule protocolSchedule = singleSpecSchedule(protocolSpec); + + when(protocolSpec.getBlockImporter()).thenReturn(blockImporter); + when(blockImporter.importBlock(any(), any(), any())).thenReturn(new BlockImportResult(true)); + + final MinedBlockObserver observer = mock(MinedBlockObserver.class); + final DefaultBlockScheduler scheduler = mock(DefaultBlockScheduler.class); + when(scheduler.waitUntilNextBlockCanBeMined(any())).thenReturn(5L); + final CliqueBlockMiner miner = + new CliqueBlockMiner( + blockCreatorSupplier, + protocolSchedule, + protocolContext, + subscribersContaining(observer), + scheduler, + headerBuilder.buildHeader(), + Address.ZERO, + false); // parent header is arbitrary for the test. + + final boolean result = miner.mineBlock(); + assertThat(result).isFalse(); + verify(blockImporter, never()) + .importBlock(protocolContext, blockToCreate, HeaderValidationMode.FULL); + verify(observer, never()).blockMined(blockToCreate); + } + + @Test + void minesBlockIfHasTransactionsWhenEmptyBlocksNotAllowed() throws InterruptedException { + final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); + + final TransactionTestFixture transactionTestFixture = new TransactionTestFixture(); + final KeyPair keyPair = SignatureAlgorithmFactory.getInstance().generateKeyPair(); + final Transaction transaction = transactionTestFixture.createTransaction(keyPair); + + final Block blockToCreate = + new Block( + headerBuilder.buildHeader(), new BlockBody(List.of(transaction), Lists.newArrayList())); + + final ValidatorProvider validatorProvider = mock(ValidatorProvider.class); + when(validatorProvider.getValidatorsAfterBlock(any())).thenReturn(List.of(Address.ZERO)); + + final CliqueContext cliqueContext = new CliqueContext(validatorProvider, null, null); + final ProtocolContext protocolContext = + new ProtocolContext(null, null, cliqueContext, Optional.empty()); + + final CliqueBlockCreator blockCreator = mock(CliqueBlockCreator.class); + final Function blockCreatorSupplier = + (parentHeader) -> blockCreator; + when(blockCreator.createBlock(anyLong())) + .thenReturn( + new BlockCreator.BlockCreationResult(blockToCreate, new TransactionSelectionResults())); + + final BlockImporter blockImporter = mock(BlockImporter.class); + final ProtocolSpec protocolSpec = mock(ProtocolSpec.class); + + final ProtocolSchedule protocolSchedule = singleSpecSchedule(protocolSpec); + + when(protocolSpec.getBlockImporter()).thenReturn(blockImporter); + when(blockImporter.importBlock(any(), any(), any())).thenReturn(new BlockImportResult(true)); + + final MinedBlockObserver observer = mock(MinedBlockObserver.class); + final DefaultBlockScheduler scheduler = mock(DefaultBlockScheduler.class); + when(scheduler.waitUntilNextBlockCanBeMined(any())).thenReturn(5L); + final CliqueBlockMiner miner = + new CliqueBlockMiner( + blockCreatorSupplier, + protocolSchedule, + protocolContext, + subscribersContaining(observer), + scheduler, + headerBuilder.buildHeader(), + Address.ZERO, + false); // parent header is arbitrary for the test. + + final boolean result = miner.mineBlock(); + assertThat(result).isTrue(); + verify(blockImporter).importBlock(protocolContext, blockToCreate, HeaderValidationMode.FULL); + verify(observer).blockMined(blockToCreate); + } + + private static Subscribers subscribersContaining( + final MinedBlockObserver... observers) { + final Subscribers result = Subscribers.create(); + for (final MinedBlockObserver obs : observers) { + result.subscribe(obs); + } + return result; + } + + private ProtocolSchedule singleSpecSchedule(final ProtocolSpec protocolSpec) { + final DefaultProtocolSchedule protocolSchedule = + new DefaultProtocolSchedule(Optional.of(BigInteger.valueOf(1234))); + protocolSchedule.putBlockNumberMilestone(0, protocolSpec); + return protocolSchedule; + } +} diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java index 7a089f2ef37..740962e2a41 100644 --- a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutorTest.java @@ -113,7 +113,8 @@ public void extraDataCreatedOnEpochBlocksContainsValidators() { proposerNodeKey, miningParameters, mock(CliqueBlockScheduler.class), - new EpochManager(EPOCH_LENGTH)); + new EpochManager(EPOCH_LENGTH), + true); // NOTE: Passing in the *parent* block, so must be 1 less than EPOCH final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH - 1).buildHeader(); @@ -147,7 +148,8 @@ public void extraDataForNonEpochBlocksDoesNotContainValidaors() { proposerNodeKey, miningParameters, mock(CliqueBlockScheduler.class), - new EpochManager(EPOCH_LENGTH)); + new EpochManager(EPOCH_LENGTH), + true); // Parent block was epoch, so the next block should contain no validators. final BlockHeader header = blockHeaderBuilder.number(EPOCH_LENGTH).buildHeader(); @@ -181,7 +183,8 @@ public void shouldUseLatestVanityData() { proposerNodeKey, miningParameters, mock(CliqueBlockScheduler.class), - new EpochManager(EPOCH_LENGTH)); + new EpochManager(EPOCH_LENGTH), + true); executor.setExtraData(modifiedVanityData); final Bytes extraDataBytes = executor.calculateExtraData(blockHeaderBuilder.buildHeader()); diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockMiner.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockMiner.java index ccbfb52ab6a..a9b246af0d4 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockMiner.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockMiner.java @@ -126,6 +126,10 @@ public BlockCreationResult createBlock(final BlockHeader parentHeader, final lon return blockCreator.createBlock(Optional.empty(), Optional.empty(), timestamp); } + protected boolean shouldImportBlock(final Block block) { + return true; + } + protected boolean mineBlock() throws InterruptedException { // Ensure the block is allowed to be mined - i.e. the timestamp on the new block is sufficiently // ahead of the parent, and still within allowable clock tolerance. @@ -140,6 +144,10 @@ protected boolean mineBlock() throws InterruptedException { "Block created, importing to local chain, block includes {} transactions", block.getBody().getTransactions().size()); + if (!shouldImportBlock(block)) { + return false; + } + final BlockImporter importer = protocolSchedule.getByBlockHeader(block.getHeader()).getBlockImporter(); final BlockImportResult blockImportResult = From 2aca68faf6a2804b757a19e7f1812409c8c58126 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 27 Oct 2023 14:32:48 +1000 Subject: [PATCH 2/9] Header validation to ensure no empty blocks when createEmptyBlocks is false Signed-off-by: Jason Frame --- .../TransitionControllerBuilderTest.java | 2 +- .../BlockHeaderValidationRulesetFactory.java | 13 ++++- .../clique/CliqueProtocolSchedule.java | 15 +++-- .../CliqueNoEmptyBlockValidationRule.java | 47 +++++++++++++++ .../CliqueNoEmptyBlockValidationRuleTest.java | 57 +++++++++++++++++++ 5 files changed, 128 insertions(+), 6 deletions(-) create mode 100644 consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRule.java create mode 100644 consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRuleTest.java diff --git a/besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java index 0b1a98a210f..0ae3fe6f0fa 100644 --- a/besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java @@ -199,7 +199,7 @@ public void assertPostMergeScheduleForPostMergeExactlyAtTerminalDifficultyIfNotF public void assertCliqueDetachedHeaderValidationPreMerge() { BlockHeaderValidator cliqueValidator = BlockHeaderValidationRulesetFactory.cliqueBlockHeaderValidator( - 5L, new EpochManager(5L), Optional.of(FeeMarket.london(1L)), true) + 5L, false, new EpochManager(5L), Optional.of(FeeMarket.london(1L)), true) .build(); assertDetachedRulesForPostMergeBlocks(cliqueValidator); } diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/BlockHeaderValidationRulesetFactory.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/BlockHeaderValidationRulesetFactory.java index 271f6ea3e2f..a7f359d6a0e 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/BlockHeaderValidationRulesetFactory.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/BlockHeaderValidationRulesetFactory.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.config.MergeConfigOptions; import org.hyperledger.besu.consensus.clique.headervalidationrules.CliqueDifficultyValidationRule; import org.hyperledger.besu.consensus.clique.headervalidationrules.CliqueExtraDataValidationRule; +import org.hyperledger.besu.consensus.clique.headervalidationrules.CliqueNoEmptyBlockValidationRule; import org.hyperledger.besu.consensus.clique.headervalidationrules.CoinbaseHeaderValidationRule; import org.hyperledger.besu.consensus.clique.headervalidationrules.SignerRateLimitValidationRule; import org.hyperledger.besu.consensus.clique.headervalidationrules.VoteValidationRule; @@ -57,10 +58,15 @@ public class BlockHeaderValidationRulesetFactory { */ public static BlockHeaderValidator.Builder cliqueBlockHeaderValidator( final long secondsBetweenBlocks, + final boolean createEmptyBlocks, final EpochManager epochManager, final Optional baseFeeMarket) { return cliqueBlockHeaderValidator( - secondsBetweenBlocks, epochManager, baseFeeMarket, MergeConfigOptions.isMergeEnabled()); + secondsBetweenBlocks, + createEmptyBlocks, + epochManager, + baseFeeMarket, + MergeConfigOptions.isMergeEnabled()); } /** @@ -75,6 +81,7 @@ public static BlockHeaderValidator.Builder cliqueBlockHeaderValidator( @VisibleForTesting public static BlockHeaderValidator.Builder cliqueBlockHeaderValidator( final long secondsBetweenBlocks, + final boolean createEmptyBlocks, final EpochManager epochManager, final Optional baseFeeMarket, final boolean isMergeEnabled) { @@ -99,6 +106,10 @@ public static BlockHeaderValidator.Builder cliqueBlockHeaderValidator( builder.addRule(new BaseFeeMarketBlockHeaderGasPriceValidationRule(baseFeeMarket.get())); } + if (!createEmptyBlocks) { + builder.addRule(new CliqueNoEmptyBlockValidationRule()); + } + var mixHashRule = new ConstantFieldValidationRule<>("MixHash", BlockHeader::getMixHash, Hash.ZERO); var voteValidationRule = new VoteValidationRule(); diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java index 64bca913546..ef2c388cf7e 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/CliqueProtocolSchedule.java @@ -78,6 +78,7 @@ public static ProtocolSchedule create( applyCliqueSpecificModifications( epochManager, cliqueConfig.getBlockPeriodSeconds(), + cliqueConfig.getCreateEmptyBlocks(), localNodeAddress, builder)), privacyParameters, @@ -107,16 +108,19 @@ public static ProtocolSchedule create( private static ProtocolSpecBuilder applyCliqueSpecificModifications( final EpochManager epochManager, final long secondsBetweenBlocks, + final boolean createEmptyBlocks, final Address localNodeAddress, final ProtocolSpecBuilder specBuilder) { return specBuilder .blockHeaderValidatorBuilder( baseFeeMarket -> - getBlockHeaderValidator(epochManager, secondsBetweenBlocks, baseFeeMarket)) + getBlockHeaderValidator( + epochManager, secondsBetweenBlocks, createEmptyBlocks, baseFeeMarket)) .ommerHeaderValidatorBuilder( baseFeeMarket -> - getBlockHeaderValidator(epochManager, secondsBetweenBlocks, baseFeeMarket)) + getBlockHeaderValidator( + epochManager, secondsBetweenBlocks, createEmptyBlocks, baseFeeMarket)) .blockBodyValidatorBuilder(MainnetBlockBodyValidator::new) .blockValidatorBuilder(MainnetProtocolSpecs.blockValidatorBuilder()) .blockImporterBuilder(MainnetBlockImporter::new) @@ -128,11 +132,14 @@ private static ProtocolSpecBuilder applyCliqueSpecificModifications( } private static BlockHeaderValidator.Builder getBlockHeaderValidator( - final EpochManager epochManager, final long secondsBetweenBlocks, final FeeMarket feeMarket) { + final EpochManager epochManager, + final long secondsBetweenBlocks, + final boolean createEmptyBlocks, + final FeeMarket feeMarket) { Optional baseFeeMarket = Optional.of(feeMarket).filter(FeeMarket::implementsBaseFee).map(BaseFeeMarket.class::cast); return BlockHeaderValidationRulesetFactory.cliqueBlockHeaderValidator( - secondsBetweenBlocks, epochManager, baseFeeMarket); + secondsBetweenBlocks, createEmptyBlocks, epochManager, baseFeeMarket); } } diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRule.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRule.java new file mode 100644 index 00000000000..023097ea7a9 --- /dev/null +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRule.java @@ -0,0 +1,47 @@ +/* + * Copyright ConsenSys AG. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.consensus.clique.headervalidationrules; + +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.mainnet.DetachedBlockHeaderValidationRule; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** The No empty block validation rule. */ +public class CliqueNoEmptyBlockValidationRule implements DetachedBlockHeaderValidationRule { + + private static final Logger LOG = LoggerFactory.getLogger(CliqueNoEmptyBlockValidationRule.class); + + /** + * Responsible for ensuring there are no empty transactions. This is used when createEmptyBlocks + * is false, to ensure that no empty blocks are created. + * + * @param header the block header to validate + * @param parent the block header corresponding to the parent of the header being validated. + * @return true if the transactionsRoot in the header is not the empty trie hash. + */ + @Override + public boolean validate(final BlockHeader header, final BlockHeader parent) { + final boolean hasTransactions = !header.getTransactionsRoot().equals(Hash.EMPTY_TRIE_HASH); + if (!hasTransactions) { + LOG.info( + "Invalid block header: {} has no transactions but create empty blocks is not enabled", + header.getNumber()); + } + return hasTransactions; + } +} diff --git a/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRuleTest.java b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRuleTest.java new file mode 100644 index 00000000000..807aef0b1fc --- /dev/null +++ b/consensus/clique/src/test/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRuleTest.java @@ -0,0 +1,57 @@ +/* + * 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.consensus.clique.headervalidationrules; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.hyperledger.besu.crypto.KeyPair; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Hash; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.Transaction; +import org.hyperledger.besu.ethereum.core.TransactionTestFixture; +import org.hyperledger.besu.ethereum.mainnet.BodyValidation; + +import java.util.List; + +import org.junit.jupiter.api.Test; + +class CliqueNoEmptyBlockValidationRuleTest { + + @Test + void headerWithNoTransactionsIsInvalid() { + final BlockHeader blockHeader = new BlockHeaderTestFixture().buildHeader(); + + final CliqueNoEmptyBlockValidationRule noEmptyBlockRule = + new CliqueNoEmptyBlockValidationRule(); + assertThat(noEmptyBlockRule.validate(blockHeader, null)).isFalse(); + } + + @Test + void headerWithTransactionsIsValid() { + final TransactionTestFixture transactionTestFixture = new TransactionTestFixture(); + final KeyPair keyPair = SignatureAlgorithmFactory.getInstance().generateKeyPair(); + final Transaction transaction = transactionTestFixture.createTransaction(keyPair); + final Hash transactionRoot = BodyValidation.transactionsRoot(List.of(transaction)); + final BlockHeader blockHeader = + new BlockHeaderTestFixture().transactionsRoot(transactionRoot).buildHeader(); + + final CliqueNoEmptyBlockValidationRule noEmptyBlockRule = + new CliqueNoEmptyBlockValidationRule(); + assertThat(noEmptyBlockRule.validate(blockHeader, null)).isTrue(); + } +} From cf6b4786609823fe7e940cb2491827db37123049 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 27 Oct 2023 14:49:39 +1000 Subject: [PATCH 3/9] java doc & asMap fixes Signed-off-by: Jason Frame --- .../hyperledger/besu/config/CliqueConfigOptions.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java index 7ce29ab1b36..c0ba496f892 100644 --- a/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/CliqueConfigOptions.java @@ -60,6 +60,11 @@ public int getBlockPeriodSeconds() { cliqueConfigRoot, "blockperiodseconds", DEFAULT_BLOCK_PERIOD_SECONDS); } + /** + * Whether the creation of empty blocks is allowed. + * + * @return the create empty block status + */ public boolean getCreateEmptyBlocks() { return JsonUtil.getBoolean(cliqueConfigRoot, "createemptyblocks", DEFAULT_CREATE_EMPTY_BLOCKS); } @@ -71,6 +76,11 @@ public boolean getCreateEmptyBlocks() { */ Map asMap() { return ImmutableMap.of( - "epochLength", getEpochLength(), "blockPeriodSeconds", getBlockPeriodSeconds()); + "epochLength", + getEpochLength(), + "blockPeriodSeconds", + getBlockPeriodSeconds(), + "createemptyblocks", + getCreateEmptyBlocks()); } } From 0e2de384005b87dc408986698d22e6a6c1c30422 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 27 Oct 2023 14:52:31 +1000 Subject: [PATCH 4/9] fix unit test failure Signed-off-by: Jason Frame --- .../besu/controller/TransitionControllerBuilderTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java b/besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java index 0ae3fe6f0fa..20a182e6ed6 100644 --- a/besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java +++ b/besu/src/test/java/org/hyperledger/besu/controller/TransitionControllerBuilderTest.java @@ -199,7 +199,7 @@ public void assertPostMergeScheduleForPostMergeExactlyAtTerminalDifficultyIfNotF public void assertCliqueDetachedHeaderValidationPreMerge() { BlockHeaderValidator cliqueValidator = BlockHeaderValidationRulesetFactory.cliqueBlockHeaderValidator( - 5L, false, new EpochManager(5L), Optional.of(FeeMarket.london(1L)), true) + 5L, true, new EpochManager(5L), Optional.of(FeeMarket.london(1L)), true) .build(); assertDetachedRulesForPostMergeBlocks(cliqueValidator); } From d50342edf675f3bcedc048f11d271b675de36b76 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 27 Oct 2023 15:32:46 +1000 Subject: [PATCH 5/9] javadoc Signed-off-by: Jason Frame --- .../consensus/clique/BlockHeaderValidationRulesetFactory.java | 2 ++ .../besu/consensus/clique/blockcreation/CliqueBlockMiner.java | 1 + .../consensus/clique/blockcreation/CliqueMinerExecutor.java | 1 + 3 files changed, 4 insertions(+) diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/BlockHeaderValidationRulesetFactory.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/BlockHeaderValidationRulesetFactory.java index a7f359d6a0e..15346fb4911 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/BlockHeaderValidationRulesetFactory.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/BlockHeaderValidationRulesetFactory.java @@ -52,6 +52,7 @@ public class BlockHeaderValidationRulesetFactory { *

Specifically the set of rules provided by this function are to be used for a Clique chain. * * @param secondsBetweenBlocks the minimum number of seconds which must elapse between blocks. + * @param createEmptyBlocks whether clique should allow the creation of empty blocks. * @param epochManager an object which determines if a given block is an epoch block. * @param baseFeeMarket an {@link Optional} wrapping {@link BaseFeeMarket} class if appropriate. * @return the header validator. @@ -73,6 +74,7 @@ public static BlockHeaderValidator.Builder cliqueBlockHeaderValidator( * Clique block header validator. Visible for testing. * * @param secondsBetweenBlocks the seconds between blocks + * @param createEmptyBlocks whether clique should allow the creation of empty blocks. * @param epochManager the epoch manager * @param baseFeeMarket the base fee market * @param isMergeEnabled the is merge enabled diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java index 76af1541259..17569d9725d 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java @@ -44,6 +44,7 @@ public class CliqueBlockMiner extends BlockMiner { * @param scheduler the scheduler * @param parentHeader the parent header * @param localAddress the local address + * @param createEmptyBlocks whether clique should allow the creation of empty blocks. */ public CliqueBlockMiner( final Function blockCreator, diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java index c0ac9ebeef3..060e262dd22 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueMinerExecutor.java @@ -59,6 +59,7 @@ public class CliqueMinerExecutor extends AbstractMinerExecutor * @param miningParams the mining params * @param blockScheduler the block scheduler * @param epochManager the epoch manager + * @param createEmptyBlocks whether clique should allow the creation of empty blocks. */ public CliqueMinerExecutor( final ProtocolContext protocolContext, From 00e400735837d0dd4ab51a16ade7f80f392e4523 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Fri, 27 Oct 2023 16:01:23 +1000 Subject: [PATCH 6/9] changelog entry Signed-off-by: Jason Frame --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 787ff1d137d..3cb998cd0c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Deprecations ### Additions and Improvements +- Clique option to not create empty blocks [#6082] ### Bug Fixes From 86b682411f260b574bdb5bd4ad38ec121668aec3 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Tue, 31 Oct 2023 14:55:35 +1000 Subject: [PATCH 7/9] Add 1 second delay between empty block build attempts Signed-off-by: Jason Frame --- .../clique/blockcreation/CliqueBlockMiner.java | 15 ++++++++++++--- .../besu/ethereum/blockcreation/BlockMiner.java | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java index 17569d9725d..b6650b5b1d0 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java @@ -27,9 +27,13 @@ import java.util.function.Function; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** The Clique block miner. */ public class CliqueBlockMiner extends BlockMiner { - // private static final Logger LOG = LoggerFactory.getLogger(CliqueBlockMiner.class); + private static final Logger LOG = LoggerFactory.getLogger(CliqueBlockMiner.class); + private static final int WAIT_BETWEEN_EMPTY_BUILD_ATTEMPTS = 1_000; private final Address localAddress; private final boolean createEmptyBlocks; @@ -71,11 +75,16 @@ protected boolean mineBlock() throws InterruptedException { } @Override - protected boolean shouldImportBlock(final Block block) { + protected boolean shouldImportBlock(final Block block) throws InterruptedException { if (createEmptyBlocks) { return true; } - return !block.getBody().getTransactions().isEmpty(); + final boolean isEmpty = block.getBody().getTransactions().isEmpty(); + if (isEmpty) { + LOG.debug("Skipping empty block"); + Thread.sleep(WAIT_BETWEEN_EMPTY_BUILD_ATTEMPTS); + } + return !isEmpty; } } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockMiner.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockMiner.java index a9b246af0d4..34ac04bf880 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockMiner.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/BlockMiner.java @@ -126,7 +126,7 @@ public BlockCreationResult createBlock(final BlockHeader parentHeader, final lon return blockCreator.createBlock(Optional.empty(), Optional.empty(), timestamp); } - protected boolean shouldImportBlock(final Block block) { + protected boolean shouldImportBlock(final Block block) throws InterruptedException { return true; } From 2991aca16cd4c1d2b715a6fdd8f745080e550a52 Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Thu, 2 Nov 2023 12:29:50 +1000 Subject: [PATCH 8/9] Test to ensure the BlockMiner pre-block validation will stop import of block Signed-off-by: Jason Frame --- .../blockcreation/BlockMinerTest.java | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java index 4222ad476c0..21f08b37699 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java @@ -14,6 +14,7 @@ */ package org.hyperledger.besu.ethereum.blockcreation; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; @@ -39,6 +40,7 @@ import java.math.BigInteger; import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import com.google.common.collect.Lists; @@ -132,6 +134,54 @@ public void failureToImportDoesNotTriggerObservers() throws InterruptedException verify(observer, times(1)).blockMined(blockToCreate); } + @Test + public void preImportValidationFailureDoesNotImportBlock() throws InterruptedException { + final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); + + final Block blockToCreate = + new Block( + headerBuilder.buildHeader(), new BlockBody(Lists.newArrayList(), Lists.newArrayList())); + + final ProtocolContext protocolContext = new ProtocolContext(null, null, null, Optional.empty()); + + final PoWBlockCreator blockCreator = mock(PoWBlockCreator.class); + final Function blockCreatorSupplier = + (parentHeader) -> blockCreator; + when(blockCreator.createBlock(anyLong())) + .thenReturn(new BlockCreationResult(blockToCreate, new TransactionSelectionResults())); + + final BlockImporter blockImporter = mock(BlockImporter.class); + final ProtocolSpec protocolSpec = mock(ProtocolSpec.class); + final ProtocolSchedule protocolSchedule = singleSpecSchedule(protocolSpec); + + when(protocolSpec.getBlockImporter()).thenReturn(blockImporter); + when(blockImporter.importBlock(any(), any(), any())).thenReturn(new BlockImportResult(true)); + + final MinedBlockObserver observer = mock(MinedBlockObserver.class); + final DefaultBlockScheduler scheduler = mock(DefaultBlockScheduler.class); + when(scheduler.waitUntilNextBlockCanBeMined(any())).thenReturn(5L); + final AtomicInteger importValidationCount = new AtomicInteger(); + final BlockMiner miner = + new BlockMiner<>( + blockCreatorSupplier, + protocolSchedule, + protocolContext, + subscribersContaining(observer), + scheduler, + headerBuilder.buildHeader()) { + @Override + protected boolean shouldImportBlock(final Block block) { + return importValidationCount.getAndIncrement() > 0; + } + }; + + miner.run(); + assertThat(importValidationCount.get()).isEqualTo(2); + verify(blockImporter, times(1)) + .importBlock(protocolContext, blockToCreate, HeaderValidationMode.FULL); + verify(observer, times(1)).blockMined(blockToCreate); + } + private static Subscribers subscribersContaining( final MinedBlockObserver... observers) { final Subscribers result = Subscribers.create(); From 1098f46d3963a44f19c69b025dd6e3fc8698ccef Mon Sep 17 00:00:00 2001 From: Jason Frame Date: Thu, 2 Nov 2023 14:44:14 +1000 Subject: [PATCH 9/9] PR suggestions Signed-off-by: Jason Frame --- CHANGELOG.md | 2 +- .../besu/controller/CliqueBesuControllerBuilder.java | 2 +- .../consensus/clique/blockcreation/CliqueBlockMiner.java | 6 +++--- .../CliqueNoEmptyBlockValidationRule.java | 2 +- .../besu/ethereum/blockcreation/BlockMinerTest.java | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5105243480..6d902e55df3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ - Accept `input` and `data` field for the payload of transaction-related RPC methods [#6094](https://github.com/hyperledger/besu/pull/6094) - Add APIs to set and get the min gas price a transaction must pay for being selected during block creation [#6097](https://github.com/hyperledger/besu/pull/6097) - TraceService: return results for transactions in block [#6086](https://github.com/hyperledger/besu/pull/6086) -- Clique option to not create empty blocks [#6082] +- Clique option `createemptyblocks` to not create empty blocks [#6082] ### Bug fixes diff --git a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java index 8c1f9c011ed..555b3c8a2fb 100644 --- a/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java +++ b/besu/src/main/java/org/hyperledger/besu/controller/CliqueBesuControllerBuilder.java @@ -53,7 +53,7 @@ public class CliqueBesuControllerBuilder extends BesuControllerBuilder { private Address localAddress; private EpochManager epochManager; private long secondsBetweenBlocks; - private boolean createEmptyBlocks; + private boolean createEmptyBlocks = true; private final BlockInterface blockInterface = new CliqueBlockInterface(); @Override diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java index b6650b5b1d0..698ff77eccb 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/blockcreation/CliqueBlockMiner.java @@ -33,7 +33,7 @@ /** The Clique block miner. */ public class CliqueBlockMiner extends BlockMiner { private static final Logger LOG = LoggerFactory.getLogger(CliqueBlockMiner.class); - private static final int WAIT_BETWEEN_EMPTY_BUILD_ATTEMPTS = 1_000; + private static final int WAIT_IN_MS_BETWEEN_EMPTY_BUILD_ATTEMPTS = 1_000; private final Address localAddress; private final boolean createEmptyBlocks; @@ -82,8 +82,8 @@ protected boolean shouldImportBlock(final Block block) throws InterruptedExcepti final boolean isEmpty = block.getBody().getTransactions().isEmpty(); if (isEmpty) { - LOG.debug("Skipping empty block"); - Thread.sleep(WAIT_BETWEEN_EMPTY_BUILD_ATTEMPTS); + LOG.debug("Skipping creating empty block {}", block.toLogString()); + Thread.sleep(WAIT_IN_MS_BETWEEN_EMPTY_BUILD_ATTEMPTS); } return !isEmpty; } diff --git a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRule.java b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRule.java index 023097ea7a9..c179a08495a 100644 --- a/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRule.java +++ b/consensus/clique/src/main/java/org/hyperledger/besu/consensus/clique/headervalidationrules/CliqueNoEmptyBlockValidationRule.java @@ -40,7 +40,7 @@ public boolean validate(final BlockHeader header, final BlockHeader parent) { if (!hasTransactions) { LOG.info( "Invalid block header: {} has no transactions but create empty blocks is not enabled", - header.getNumber()); + header.toLogString()); } return hasTransactions; } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java index 21f08b37699..681991ee75c 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/BlockMinerTest.java @@ -135,7 +135,7 @@ public void failureToImportDoesNotTriggerObservers() throws InterruptedException } @Test - public void preImportValidationFailureDoesNotImportBlock() throws InterruptedException { + public void blockValidationFailureBeforeImportDoesNotImportBlock() throws InterruptedException { final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); final Block blockToCreate =