diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java index 17dcddf1188..4b24071ab33 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/BesuNodeFactory.java @@ -38,6 +38,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.node.Node; import org.hyperledger.besu.tests.acceptance.dsl.node.RunnableNode; import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory; +import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory.CliqueOptions; import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.pki.PkiKeystoreConfigurationFactory; import java.io.File; @@ -368,6 +369,17 @@ public BesuNode createNodeWithNoDiscovery(final String name) throws IOException } public BesuNode createCliqueNode(final String name) throws IOException { + return createCliqueNode(name, CliqueOptions.DEFAULT); + } + + public BesuNode createCliqueNode(final String name, final CliqueOptions cliqueOptions) + throws IOException { + return createCliqueNodeWithExtraCliOptions(name, cliqueOptions, List.of()); + } + + public BesuNode createCliqueNodeWithExtraCliOptions( + final String name, final CliqueOptions cliqueOptions, final List extraCliOptions) + throws IOException { return create( new BesuNodeConfigurationBuilder() .name(name) @@ -375,7 +387,12 @@ public BesuNode createCliqueNode(final String name) throws IOException { .jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig()) .webSocketConfiguration(node.createWebSocketEnabledConfig()) .devMode(false) - .genesisConfigProvider(GenesisConfigurationFactory::createCliqueGenesisConfig) + .jsonRpcTxPool() + .genesisConfigProvider( + validators -> + GenesisConfigurationFactory.createCliqueGenesisConfig( + validators, cliqueOptions)) + .extraCLIOptions(extraCliOptions) .build()); } @@ -567,6 +584,7 @@ public BesuNode createCliqueNodeWithValidators(final String name, final String.. .miningEnabled() .jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig()) .webSocketConfiguration(node.createWebSocketEnabledConfig()) + .jsonRpcTxPool() .devMode(false) .genesisConfigProvider( nodes -> diff --git a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/genesis/GenesisConfigurationFactory.java b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/genesis/GenesisConfigurationFactory.java index c52fbbba0c5..80bbf4c9206 100644 --- a/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/genesis/GenesisConfigurationFactory.java +++ b/acceptance-tests/dsl/src/main/java/org/hyperledger/besu/tests/acceptance/dsl/node/configuration/genesis/GenesisConfigurationFactory.java @@ -46,9 +46,17 @@ private GenesisConfigurationFactory() { public static Optional createCliqueGenesisConfig( final Collection validators) { - final String template = readGenesisFile("/clique/clique.json"); + return createCliqueGenesisConfig(validators, CliqueOptions.DEFAULT); + } + + public static Optional createCliqueGenesisConfig( + final Collection validators, final CliqueOptions cliqueOptions) { + final String template = readGenesisFile("/clique/clique.json.tpl"); + return updateGenesisExtraData( - validators, template, CliqueExtraData::createGenesisExtraDataString); + validators, + updateGenesisCliqueOptions(template, cliqueOptions), + CliqueExtraData::createGenesisExtraDataString); } public static Optional createIbft2GenesisConfig( @@ -138,6 +146,14 @@ private static Optional updateGenesisExtraData( return Optional.of(genesis); } + private static String updateGenesisCliqueOptions( + final String template, final CliqueOptions cliqueOptions) { + return template + .replace("%blockperiodseconds%", String.valueOf(cliqueOptions.blockPeriodSeconds)) + .replace("%epochlength%", String.valueOf(cliqueOptions.epochLength)) + .replace("%createemptyblocks%", String.valueOf(cliqueOptions.createEmptyBlocks)); + } + @SuppressWarnings("UnstableApiUsage") public static String readGenesisFile(final String filepath) { try { @@ -147,4 +163,8 @@ public static String readGenesisFile(final String filepath) { throw new IllegalStateException("Unable to get test genesis config " + filepath); } } + + public record CliqueOptions(int blockPeriodSeconds, int epochLength, boolean createEmptyBlocks) { + public static final CliqueOptions DEFAULT = new CliqueOptions(10, 30000, true); + } } diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java index f7065b623a2..4ac728dec73 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/clique/CliqueMiningAcceptanceTest.java @@ -17,6 +17,7 @@ import org.hyperledger.besu.tests.acceptance.dsl.AcceptanceTestBase; import org.hyperledger.besu.tests.acceptance.dsl.account.Account; import org.hyperledger.besu.tests.acceptance.dsl.node.BesuNode; +import org.hyperledger.besu.tests.acceptance.dsl.node.configuration.genesis.GenesisConfigurationFactory.CliqueOptions; import java.io.IOException; @@ -42,6 +43,35 @@ public void shouldMineTransactionsOnSingleNode() throws IOException { cluster.verify(receiver.balanceEquals(3)); } + @Test + public void shouldNotMineBlocksIfNoTransactionsWhenCreateEmptyBlockIsFalse() throws IOException { + final var cliqueOptionsNoEmptyBlocks = + new CliqueOptions( + CliqueOptions.DEFAULT.blockPeriodSeconds(), CliqueOptions.DEFAULT.epochLength(), false); + final BesuNode minerNode = besu.createCliqueNode("miner1", cliqueOptionsNoEmptyBlocks); + cluster.start(minerNode); + + cluster.verify(clique.noNewBlockCreated(minerNode)); + } + + @Test + public void shouldMineBlocksOnlyWhenTransactionsArePresentWhenCreateEmptyBlockIsFalse() + throws IOException { + final var cliqueOptionsNoEmptyBlocks = + new CliqueOptions( + CliqueOptions.DEFAULT.blockPeriodSeconds(), CliqueOptions.DEFAULT.epochLength(), false); + final BesuNode minerNode = besu.createCliqueNode("miner1", cliqueOptionsNoEmptyBlocks); + cluster.start(minerNode); + + final Account sender = accounts.createAccount("account1"); + + cluster.verify(clique.noNewBlockCreated(minerNode)); + + minerNode.execute(accountTransactions.createTransfer(sender, 50)); + + minerNode.verify(clique.blockIsCreatedByProposer(minerNode)); + } + @Test public void shouldMineTransactionsOnMultipleNodes() throws IOException { final BesuNode minerNode1 = besu.createCliqueNode("miner1"); diff --git a/acceptance-tests/tests/src/test/resources/clique/clique.json b/acceptance-tests/tests/src/test/resources/clique/clique.json.tpl similarity index 92% rename from acceptance-tests/tests/src/test/resources/clique/clique.json rename to acceptance-tests/tests/src/test/resources/clique/clique.json.tpl index 8fb3c6dc331..abed233a776 100644 --- a/acceptance-tests/tests/src/test/resources/clique/clique.json +++ b/acceptance-tests/tests/src/test/resources/clique/clique.json.tpl @@ -8,8 +8,9 @@ "constantinopleBlock": 6, "petersburgBlock": 7, "clique": { - "blockperiodseconds": 10, - "epochlength": 30000 + "blockperiodseconds": %blockperiodseconds%, + "epochlength": %epochlength%, + "createemptyblocks": %createemptyblocks% } }, "nonce": "0x0",