From 1c18e7df9eda9889d14ef71d3ba2b17bacf924fb Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Tue, 21 Nov 2023 12:32:47 +0100 Subject: [PATCH 1/2] Acceptance test for Clique when configured to not create empty blocks Signed-off-by: Fabio Di Fabio --- .../node/configuration/BesuNodeFactory.java | 13 ++++++ .../genesis/GenesisConfigurationFactory.java | 7 +++ .../clique/CliqueMiningAcceptanceTest.java | 8 ++++ .../clique/clique-no-empty-blocks.json | 43 +++++++++++++++++++ 4 files changed, 71 insertions(+) create mode 100644 acceptance-tests/tests/src/test/resources/clique/clique-no-empty-blocks.json 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..62a637de5c1 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 @@ -379,6 +379,19 @@ public BesuNode createCliqueNode(final String name) throws IOException { .build()); } + public BesuNode createCliqueNoEmptyBlockNode(final String name) throws IOException { + return create( + new BesuNodeConfigurationBuilder() + .name(name) + .miningEnabled() + .jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig()) + .webSocketConfiguration(node.createWebSocketEnabledConfig()) + .devMode(false) + .genesisConfigProvider( + GenesisConfigurationFactory::createCliqueNoEmptyBlocksGenesisConfig) + .build()); + } + public BesuNode createIbft2NonValidatorBootnode(final String name, final String genesisFile) throws IOException { return create( 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..dcacc487a82 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 @@ -51,6 +51,13 @@ public static Optional createCliqueGenesisConfig( validators, template, CliqueExtraData::createGenesisExtraDataString); } + public static Optional createCliqueNoEmptyBlocksGenesisConfig( + final Collection validators) { + final String template = readGenesisFile("/clique/clique-no-empty-blocks.json"); + return updateGenesisExtraData( + validators, template, CliqueExtraData::createGenesisExtraDataString); + } + public static Optional createIbft2GenesisConfig( final Collection validators) { return createIbft2GenesisConfig(validators, "/ibft/ibft.json"); 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..36d9fad07b4 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 @@ -42,6 +42,14 @@ public void shouldMineTransactionsOnSingleNode() throws IOException { cluster.verify(receiver.balanceEquals(3)); } + @Test + public void shouldNotMineBlocksIfNoTransactionsOnSingleNode() throws IOException { + final BesuNode minerNode = besu.createCliqueNoEmptyBlockNode("miner1"); + cluster.start(minerNode); + + cluster.verify(clique.noNewBlockCreated(minerNode)); + } + @Test public void shouldMineTransactionsOnMultipleNodes() throws IOException { final BesuNode minerNode1 = besu.createCliqueNode("miner1"); diff --git a/acceptance-tests/tests/src/test/resources/clique/clique-no-empty-blocks.json b/acceptance-tests/tests/src/test/resources/clique/clique-no-empty-blocks.json new file mode 100644 index 00000000000..6521b5a7948 --- /dev/null +++ b/acceptance-tests/tests/src/test/resources/clique/clique-no-empty-blocks.json @@ -0,0 +1,43 @@ +{ + "config": { + "chainId": 4, + "homesteadBlock": 1, + "eip150Block": 2, + "eip158Block": 4, + "byzantiumBlock": 5, + "constantinopleBlock": 6, + "petersburgBlock": 7, + "clique": { + "blockperiodseconds": 10, + "epochlength": 30000, + "createemptyblocks": false + } + }, + "nonce": "0x0", + "timestamp": "0x58ee40ba", + "extraData": "%extraData%", + "gasLimit": "0x47b760", + "difficulty": "0x1", + "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", + "coinbase": "0x0000000000000000000000000000000000000000", + "alloc": { + "fe3b557e8fb62b89f4916b721be55ceb828dbd73": { + "privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", + "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", + "balance": "0xad78ebc5ac6200000" + }, + "627306090abaB3A6e1400e9345bC60c78a8BEf57": { + "privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", + "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", + "balance": "90000000000000000000000" + }, + "f17f52151EbEF6C7334FAD080c5704D77216b732": { + "privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f", + "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", + "balance": "90000000000000000000000" + } + }, + "number": "0x0", + "gasUsed": "0x0", + "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" +} From fb19439952290adcd131eb8acba2a31bac76994c Mon Sep 17 00:00:00 2001 From: Fabio Di Fabio Date: Wed, 22 Nov 2023 11:44:54 +0100 Subject: [PATCH 2/2] Test Clique mines a block only when txs are present, if create empty blocks is false Signed-off-by: Fabio Di Fabio --- .../node/configuration/BesuNodeFactory.java | 27 +++++++----- .../genesis/GenesisConfigurationFactory.java | 27 ++++++++---- .../clique/CliqueMiningAcceptanceTest.java | 26 +++++++++++- .../src/test/resources/clique/clique.json | 42 ------------------- ...e-no-empty-blocks.json => clique.json.tpl} | 6 +-- 5 files changed, 63 insertions(+), 65 deletions(-) delete mode 100644 acceptance-tests/tests/src/test/resources/clique/clique.json rename acceptance-tests/tests/src/test/resources/clique/{clique-no-empty-blocks.json => clique.json.tpl} (92%) 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 62a637de5c1..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,18 +369,17 @@ public BesuNode createNodeWithNoDiscovery(final String name) throws IOException } public BesuNode createCliqueNode(final String name) throws IOException { - return create( - new BesuNodeConfigurationBuilder() - .name(name) - .miningEnabled() - .jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig()) - .webSocketConfiguration(node.createWebSocketEnabledConfig()) - .devMode(false) - .genesisConfigProvider(GenesisConfigurationFactory::createCliqueGenesisConfig) - .build()); + return createCliqueNode(name, CliqueOptions.DEFAULT); } - public BesuNode createCliqueNoEmptyBlockNode(final String name) throws IOException { + 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) @@ -387,8 +387,12 @@ public BesuNode createCliqueNoEmptyBlockNode(final String name) throws IOExcepti .jsonRpcConfiguration(node.createJsonRpcWithCliqueEnabledConfig()) .webSocketConfiguration(node.createWebSocketEnabledConfig()) .devMode(false) + .jsonRpcTxPool() .genesisConfigProvider( - GenesisConfigurationFactory::createCliqueNoEmptyBlocksGenesisConfig) + validators -> + GenesisConfigurationFactory.createCliqueGenesisConfig( + validators, cliqueOptions)) + .extraCLIOptions(extraCliOptions) .build()); } @@ -580,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 dcacc487a82..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,16 +46,17 @@ private GenesisConfigurationFactory() { public static Optional createCliqueGenesisConfig( final Collection validators) { - final String template = readGenesisFile("/clique/clique.json"); - return updateGenesisExtraData( - validators, template, CliqueExtraData::createGenesisExtraDataString); + return createCliqueGenesisConfig(validators, CliqueOptions.DEFAULT); } - public static Optional createCliqueNoEmptyBlocksGenesisConfig( - final Collection validators) { - final String template = readGenesisFile("/clique/clique-no-empty-blocks.json"); + 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( @@ -145,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 { @@ -154,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 36d9fad07b4..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; @@ -43,13 +44,34 @@ public void shouldMineTransactionsOnSingleNode() throws IOException { } @Test - public void shouldNotMineBlocksIfNoTransactionsOnSingleNode() throws IOException { - final BesuNode minerNode = besu.createCliqueNoEmptyBlockNode("miner1"); + 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 deleted file mode 100644 index 8fb3c6dc331..00000000000 --- a/acceptance-tests/tests/src/test/resources/clique/clique.json +++ /dev/null @@ -1,42 +0,0 @@ -{ - "config": { - "chainId": 4, - "homesteadBlock": 1, - "eip150Block": 2, - "eip158Block": 4, - "byzantiumBlock": 5, - "constantinopleBlock": 6, - "petersburgBlock": 7, - "clique": { - "blockperiodseconds": 10, - "epochlength": 30000 - } - }, - "nonce": "0x0", - "timestamp": "0x58ee40ba", - "extraData": "%extraData%", - "gasLimit": "0x47b760", - "difficulty": "0x1", - "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", - "coinbase": "0x0000000000000000000000000000000000000000", - "alloc": { - "fe3b557e8fb62b89f4916b721be55ceb828dbd73": { - "privateKey": "8f2a55949038a9610f50fb23b5883af3b4ecb3c3bb792cbcefbd1542c692be63", - "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", - "balance": "0xad78ebc5ac6200000" - }, - "627306090abaB3A6e1400e9345bC60c78a8BEf57": { - "privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3", - "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", - "balance": "90000000000000000000000" - }, - "f17f52151EbEF6C7334FAD080c5704D77216b732": { - "privateKey": "ae6ae8e5ccbfb04590405997ee2d52d2b330726137b875053c36d94e974d162f", - "comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored", - "balance": "90000000000000000000000" - } - }, - "number": "0x0", - "gasUsed": "0x0", - "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000" -} diff --git a/acceptance-tests/tests/src/test/resources/clique/clique-no-empty-blocks.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-no-empty-blocks.json rename to acceptance-tests/tests/src/test/resources/clique/clique.json.tpl index 6521b5a7948..abed233a776 100644 --- a/acceptance-tests/tests/src/test/resources/clique/clique-no-empty-blocks.json +++ b/acceptance-tests/tests/src/test/resources/clique/clique.json.tpl @@ -8,9 +8,9 @@ "constantinopleBlock": 6, "petersburgBlock": 7, "clique": { - "blockperiodseconds": 10, - "epochlength": 30000, - "createemptyblocks": false + "blockperiodseconds": %blockperiodseconds%, + "epochlength": %epochlength%, + "createemptyblocks": %createemptyblocks% } }, "nonce": "0x0",