diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f0661e3cfb..7c9b70891d1 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,8 +14,9 @@ jobs: steps: - name: Pre-process Release Name id: pre_process_release_name + env: + RELEASE_NAME: "${{ github.event.release.name }}" run: | - RELEASE_NAME="${{ github.event.release.name }}" # strip all whitespace RELEASE_NAME="${RELEASE_NAME//[[:space:]]/}" if [[ ! "$RELEASE_NAME" =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?(-.*)?$ ]]; then diff --git a/CHANGELOG.md b/CHANGELOG.md index e0e64087c04..eea989cc957 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,6 @@ # Changelog ## [Unreleased] -- Add configuration of Consolidation Request Contract Address via genesis configuration [#7647](https://github.com/hyperledger/besu/pull/7647) - ### Upcoming Breaking Changes - k8s (KUBERNETES) Nat method is now deprecated and will be removed in a future release @@ -14,7 +12,8 @@ - Remove privacy test classes support [#7569](https://github.com/hyperledger/besu/pull/7569) - Add Blob Transaction Metrics [#7622](https://github.com/hyperledger/besu/pull/7622) - Implemented support for emptyBlockPeriodSeconds in QBFT [#6965](https://github.com/hyperledger/besu/pull/6965) - +- Add configuration of Consolidation Request Contract Address via genesis configuration [#7647](https://github.com/hyperledger/besu/pull/7647) +- Interrupt pending transaction processing on block creation timeout [#7673](https://github.com/hyperledger/besu/pull/7673) ### Bug fixes - Fix mounted data path directory permissions for besu user [#7575](https://github.com/hyperledger/besu/pull/7575) diff --git a/MAINTAINERS.md b/MAINTAINERS.md index f699321efbd..17958bdbd43 100644 --- a/MAINTAINERS.md +++ b/MAINTAINERS.md @@ -82,7 +82,7 @@ The following steps must occur for a contributor to be "upgraded" as a maintaine - The proposed maintainer accepts the nomination and expresses a willingness to be a long-term (more than 6 month) committer by adding a comment in the proposal PR. - The PR will be communicated in all appropriate communication channels - including at least [besu-contributors channel on Hyperledger Discord](https://discord.gg/hyperledger), + including at least [besu-contributors channel on Discord](https://discord.gg/hyperledger), the [mailing list](https://lists.hyperledger.org/g/besu) and any maintainer/community call. - Approval by at least 3 current maintainers within two weeks of the proposal or diff --git a/SUPPORT.md b/SUPPORT.md index a9eb54acb86..9e4a7b7b42b 100644 --- a/SUPPORT.md +++ b/SUPPORT.md @@ -1,4 +1,4 @@ -# Hyperledger Besu Support +# Besu Support Welcome to the Besu repository! The following links are a set of guidelines for contributing to this repo and its packages. These are mostly guidelines, not rules. Use your best judgement, and feel free to propose changes to this document in a pull request. Contributions come in the form of code submissions, writing documentation, raising issues, helping others in chat, and any other actions that help develop Besu. @@ -6,13 +6,13 @@ Welcome to the Besu repository! The following links are a set of guidelines for Having Github, Discord, and Linux Foundation accounts is necessary for obtaining support for Besu through the community channels, wiki and issue management. * If you want to raise an issue, you can do so [on the github issue tab](https://github.com/hyperledger/besu/issues). -* Hyperledger Discord requires a [Discord account]. -* The Hyperledger wiki also requires a [Linux Foundation (LF) account] in order to edit pages. +* Discord requires a [Discord account]. +* The Besu wiki also requires a [Linux Foundation (LF) account] in order to edit pages. ### Useful support links * [Besu User Documentation] -* [Besu channel on Hyperledger Discord] +* [Besu channel on Discord] * [I just have a quick question](https://wiki.hyperledger.org/display/BESU/I+just+have+a+quick+question) * [Did you find a bug?](https://wiki.hyperledger.org/display/BESU/Reporting+Bugs) * [Issues](https://wiki.hyperledger.org/display/BESU/Issues) @@ -20,5 +20,5 @@ Having Github, Discord, and Linux Foundation accounts is necessary for obtaining [Besu User Documentation]: https://besu.hyperledger.org -[Besu channel on Hyperledger Discord]: https://discord.gg/hyperledger +[Besu channel on Discord]: https://discord.gg/hyperledger [Contributing Guidelines]: CONTRIBUTING.md diff --git a/besu/src/test/java/org/hyperledger/besu/cli/subcommands/operator/OperatorSubCommandTest.java b/besu/src/test/java/org/hyperledger/besu/cli/subcommands/operator/OperatorSubCommandTest.java index 7e9a36719d7..ca3e6d95df3 100644 --- a/besu/src/test/java/org/hyperledger/besu/cli/subcommands/operator/OperatorSubCommandTest.java +++ b/besu/src/test/java/org/hyperledger/besu/cli/subcommands/operator/OperatorSubCommandTest.java @@ -282,8 +282,10 @@ public void shouldCreateIbft2ExtraData() throws IOException { false, singletonList("key.pub"), Optional.empty(), - Optional.of( - "0xf853a00000000000000000000000000000000000000000000000000000000000000000ea94d5feb0fc5a54a89f97aeb34c3df15397c19f6dd294d6a9a4c886eb008ac307abdc1f38745c1dd13a88808400000000c0")); + List.of( + new Field( + "extraData", + "0xf853a00000000000000000000000000000000000000000000000000000000000000000ea94d5feb0fc5a54a89f97aeb34c3df15397c19f6dd294d6a9a4c886eb008ac307abdc1f38745c1dd13a88808400000000c0"))); } @Test @@ -296,8 +298,49 @@ public void shouldCreateQbftExtraData() throws IOException { false, singletonList("key.pub"), Optional.empty(), - Optional.of( - "0xf84fa00000000000000000000000000000000000000000000000000000000000000000ea94d5feb0fc5a54a89f97aeb34c3df15397c19f6dd294d6a9a4c886eb008ac307abdc1f38745c1dd13a88c080c0")); + List.of( + new Field( + "extraData", + "0xf84fa00000000000000000000000000000000000000000000000000000000000000000ea94d5feb0fc5a54a89f97aeb34c3df15397c19f6dd294d6a9a4c886eb008ac307abdc1f38745c1dd13a88c080c0"))); + } + + @Test + public void generatedGenesisFileShouldContainAllOriginalFieldsExcludingExtraData() + throws IOException { + final JsonObject alloc = + new JsonObject( + """ + { + "24defc2d149861d3d245749b81fe0e6b28e04f31": { + "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" + }, + "2a813d7db3de19b07f92268b6d4125ed295cbe00": { + "balance": "0x446c3b15f9926687d2c40534fdb542000000000000" + } + }"""); + final List fields = + List.of( + new Field("nonce", "0x0"), + new Field("timestamp", "0x5b3c3d18"), + new Field("gasUsed", "0x0"), + new Field( + "parentHash", "0x0000000000000000000000000000000000000000000000000000000000000000"), + new Field("gasLimit", "0x47b760"), + new Field("difficulty", "0x1"), + new Field( + "mixHash", "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365"), + new Field("coinbase", "0x0000000000000000000000000000000000000000"), + new Field("alloc", alloc.getMap().toString())); + + runCmdAndCheckOutput( + cmd(), + "/operator/config_generate_keys.json", + tmpOutputDirectoryPath, + "genesis.json", + false, + singletonList("key.pub"), + Optional.empty(), + fields); } private void runCmdAndCheckOutput( @@ -316,7 +359,7 @@ private void runCmdAndCheckOutput( generate, expectedKeyFiles, Optional.empty(), - Optional.empty()); + List.of()); } private void runCmdAndCheckOutput( @@ -336,9 +379,11 @@ private void runCmdAndCheckOutput( generate, expectedKeyFiles, signatureAlgorithm, - Optional.empty()); + List.of()); } + private record Field(String key, String value) {} + private void runCmdAndCheckOutput( final Cmd cmd, final String configFile, @@ -347,7 +392,7 @@ private void runCmdAndCheckOutput( final boolean generate, final Collection expectedKeyFiles, final Optional signatureAlgorithm, - final Optional expectedExtraData) + final List expectedFields) throws IOException { final URL configFilePath = this.getClass().getResource(configFile); parseCommand( @@ -368,8 +413,9 @@ private void runCmdAndCheckOutput( final String genesisString = contentOf(outputGenesisFile, UTF_8); final JsonObject genesisContent = new JsonObject(genesisString); assertThat(genesisContent.containsKey("extraData")).isTrue(); - expectedExtraData.ifPresent( - extraData -> assertThat(genesisContent.getString("extraData")).isEqualTo(extraData)); + + expectedFields.forEach( + field -> assertThat(genesisContent.getString(field.key)).isEqualTo(field.value)); final Path expectedKeysPath = outputDirectoryPath.resolve("keys"); final File keysDirectory = new File(expectedKeysPath.toUri()); diff --git a/besu/src/test/resources/operator/config_generate_keys.json b/besu/src/test/resources/operator/config_generate_keys.json index 3723fdc3bce..212bb943eac 100644 --- a/besu/src/test/resources/operator/config_generate_keys.json +++ b/besu/src/test/resources/operator/config_generate_keys.json @@ -4,7 +4,9 @@ "chainId": 2017, "eip150Block": 0, "ibft2": { - + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 } }, "nonce": "0x0", @@ -16,11 +18,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/besu/src/test/resources/operator/config_generate_keys_ec_invalid.json b/besu/src/test/resources/operator/config_generate_keys_ec_invalid.json index 1483c45f381..3699740aba2 100644 --- a/besu/src/test/resources/operator/config_generate_keys_ec_invalid.json +++ b/besu/src/test/resources/operator/config_generate_keys_ec_invalid.json @@ -5,7 +5,9 @@ "eip150Block": 0, "ecCurve": "abcd", "ibft2": { - + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 } }, "nonce": "0x0", @@ -17,11 +19,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/besu/src/test/resources/operator/config_generate_keys_secp256r1.json b/besu/src/test/resources/operator/config_generate_keys_secp256r1.json index f66f588ffe8..bbfe9f0c6ab 100644 --- a/besu/src/test/resources/operator/config_generate_keys_secp256r1.json +++ b/besu/src/test/resources/operator/config_generate_keys_secp256r1.json @@ -5,7 +5,9 @@ "eip150Block": 0, "ecCurve": "secp256r1", "ibft2": { - + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 } }, "nonce": "0x0", @@ -17,11 +19,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/besu/src/test/resources/operator/config_import_keys.json b/besu/src/test/resources/operator/config_import_keys.json index db4e57b5455..78e4cd3343e 100644 --- a/besu/src/test/resources/operator/config_import_keys.json +++ b/besu/src/test/resources/operator/config_import_keys.json @@ -4,7 +4,9 @@ "chainId": 2017, "petersburgBlock": 0, "ibft2": { - + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 } }, "nonce": "0x0", @@ -16,11 +18,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/besu/src/test/resources/operator/config_import_keys_invalid_keys.json b/besu/src/test/resources/operator/config_import_keys_invalid_keys.json index 3fc4561e3a1..b9c20be753f 100644 --- a/besu/src/test/resources/operator/config_import_keys_invalid_keys.json +++ b/besu/src/test/resources/operator/config_import_keys_invalid_keys.json @@ -4,7 +4,9 @@ "chainId": 2017, "petersburgBlock": 0, "ibft2": { - + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 } }, "nonce": "0x0", @@ -16,11 +18,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/besu/src/test/resources/operator/config_import_keys_qbft.json b/besu/src/test/resources/operator/config_import_keys_qbft.json index fe757326b40..449ba343ee5 100644 --- a/besu/src/test/resources/operator/config_import_keys_qbft.json +++ b/besu/src/test/resources/operator/config_import_keys_qbft.json @@ -4,7 +4,9 @@ "chainId": 2017, "petersburgBlock": 0, "qbft": { - + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 } }, "nonce": "0x0", @@ -16,11 +18,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/besu/src/test/resources/operator/config_import_keys_secp256r1.json b/besu/src/test/resources/operator/config_import_keys_secp256r1.json index bd189d0235c..b56654b1363 100644 --- a/besu/src/test/resources/operator/config_import_keys_secp256r1.json +++ b/besu/src/test/resources/operator/config_import_keys_secp256r1.json @@ -5,7 +5,9 @@ "petersburgBlock": 0, "ecCurve": "secp256r1", "ibft2": { - + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 } }, "nonce": "0x0", @@ -17,11 +19,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/besu/src/test/resources/operator/config_import_keys_secp256r1_invalid_keys.json b/besu/src/test/resources/operator/config_import_keys_secp256r1_invalid_keys.json index 4f59b9ebd7f..0870f9e872b 100644 --- a/besu/src/test/resources/operator/config_import_keys_secp256r1_invalid_keys.json +++ b/besu/src/test/resources/operator/config_import_keys_secp256r1_invalid_keys.json @@ -5,7 +5,9 @@ "petersburgBlock": 0, "ecCurve": "secp256r1", "ibft2": { - + "blockperiodseconds": 2, + "epochlength": 30000, + "requesttimeoutseconds": 10 } }, "nonce": "0x0", @@ -17,11 +19,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/besu/src/test/resources/operator/config_no_config_section.json b/besu/src/test/resources/operator/config_no_config_section.json index 2841aea9c47..5dfa4547974 100644 --- a/besu/src/test/resources/operator/config_no_config_section.json +++ b/besu/src/test/resources/operator/config_no_config_section.json @@ -9,11 +9,6 @@ "difficulty": "0x1", "mixHash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365", "coinbase": "0x0000000000000000000000000000000000000000", - "ibft2": { - "blockperiodseconds": 2, - "epochlength": 30000, - "requesttimeoutseconds": 10 - }, "alloc": { "24defc2d149861d3d245749b81fe0e6b28e04f31": { "balance": "0x446c3b15f9926687d2c40534fdb564000000000000" diff --git a/config/src/main/java/org/hyperledger/besu/config/GenesisReader.java b/config/src/main/java/org/hyperledger/besu/config/GenesisReader.java index 316aa73d043..6b42c3c2edf 100644 --- a/config/src/main/java/org/hyperledger/besu/config/GenesisReader.java +++ b/config/src/main/java/org/hyperledger/besu/config/GenesisReader.java @@ -53,12 +53,12 @@ class FromObjectNode implements GenesisReader { private final ObjectNode rootWithoutAllocations; public FromObjectNode(final ObjectNode root) { - final var removedAllocations = root.remove(ALLOCATION_FIELD); this.allocations = - removedAllocations != null - ? (ObjectNode) removedAllocations + root.get(ALLOCATION_FIELD) != null + ? (ObjectNode) root.get(ALLOCATION_FIELD) : JsonUtil.createEmptyObjectNode(); - this.rootWithoutAllocations = normalizeKeys(root); + this.rootWithoutAllocations = + normalizeKeys(root, field -> !field.getKey().equals(ALLOCATION_FIELD)); } @Override diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonUtil.java b/config/src/main/java/org/hyperledger/besu/config/JsonUtil.java index bcb89c64edb..430c74efb94 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonUtil.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonUtil.java @@ -25,6 +25,7 @@ import java.util.OptionalLong; import java.util.Set; import java.util.function.Function; +import java.util.function.Predicate; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonParser; @@ -37,6 +38,7 @@ import com.fasterxml.jackson.databind.node.ArrayNode; import com.fasterxml.jackson.databind.node.JsonNodeType; import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.base.Predicates; import org.apache.tuweni.bytes.Bytes; /** The Json util class. */ @@ -59,11 +61,29 @@ private JsonUtil() {} * @return a copy of the json object with all keys in lower case. */ public static ObjectNode normalizeKeys(final ObjectNode objectNode) { + return normalizeKeys(objectNode, Predicates.alwaysTrue()); + } + + /** + * Converts all the object keys (but none of the string values) to lowercase for easier lookup. + * This is useful in cases such as the 'genesis.json' file where all keys are assumed to be case + * insensitive. + * + * @param objectNode The ObjectNode to be normalized + * @param fieldPredicate The predicate to filter the fields to normalize + * @return a copy of the json object with all keys in lower case. + */ + public static ObjectNode normalizeKeys( + final ObjectNode objectNode, final Predicate> fieldPredicate) { final ObjectNode normalized = JsonUtil.createEmptyObjectNode(); objectNode .fields() .forEachRemaining( entry -> { + if (!fieldPredicate.test(entry)) { + return; + } + final String key = entry.getKey(); final JsonNode value = entry.getValue(); final String normalizedKey = normalizeKey(key); diff --git a/config/src/test/java/org/hyperledger/besu/config/GenesisReaderTest.java b/config/src/test/java/org/hyperledger/besu/config/GenesisReaderTest.java index 5d73a2829f5..f8327174a33 100644 --- a/config/src/test/java/org/hyperledger/besu/config/GenesisReaderTest.java +++ b/config/src/test/java/org/hyperledger/besu/config/GenesisReaderTest.java @@ -53,6 +53,22 @@ public void readGenesisFromObjectNode() { .containsExactly(new GenesisAccount(Address.BLS12_G2MUL, 0, Wei.ONE, null, Map.of(), null)); } + @Test + public void readGenesisFromObjectDoesNotModifyObjectNodeArg() { + final var configNode = mapper.createObjectNode(); + configNode.put("londonBlock", 1); + final var allocNode = mapper.createObjectNode(); + allocNode.put(Address.BLS12_G2MUL.toUnprefixedHexString(), generateAllocation(Wei.ONE)); + final var rootNode = mapper.createObjectNode(); + rootNode.put("chainId", 12); + rootNode.put(CONFIG_FIELD, configNode); + rootNode.put(ALLOCATION_FIELD, allocNode); + var rootNodeCopy = rootNode.deepCopy(); + new GenesisReader.FromObjectNode(rootNode); + + assertThat(rootNode).isEqualTo(rootNodeCopy); + } + @Test public void readGenesisFromURL(@TempDir final Path folder) throws IOException { final String jsonStr = diff --git a/config/src/test/java/org/hyperledger/besu/config/JsonUtilTest.java b/config/src/test/java/org/hyperledger/besu/config/JsonUtilTest.java index 1e1df382e71..fa6b4ec1467 100644 --- a/config/src/test/java/org/hyperledger/besu/config/JsonUtilTest.java +++ b/config/src/test/java/org/hyperledger/besu/config/JsonUtilTest.java @@ -147,6 +147,36 @@ public void normalizeKeys_arrayNode_withString() { assertThat(normalizedObj).isEqualTo(expectedObj); } + @Test + public void normalizeKeys_predicate() { + final ObjectNode originalObj = + mapper + .createObjectNode() + .put("Ant", "Tiny") + .put("Ape", "Smart") + .put("Armadillo", "Armored") + .put("Cat", "Meow") + .put("Bat", "Flying") + .put("Cow", "Moo") + .put("Crocodile", "Snap") + .put("Bear", "Strong") + .put("Cheetah", "Fast") + .put("Beaver", "Builder"); + + final ObjectNode expectedObj = + mapper + .createObjectNode() + .put("cat", "Meow") + .put("cow", "Moo") + .put("cheetah", "Fast") + .put("crocodile", "Snap"); + + final ObjectNode normalizedObj = + JsonUtil.normalizeKeys(originalObj, s -> s.getKey().startsWith("C")); + + assertThat(normalizedObj).isEqualTo(expectedObj); + } + @Test public void getLong_nonExistentKey() { final ObjectNode node = mapper.createObjectNode(); diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetClientVersionV1.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetClientVersionV1.java index 689cb44e6a3..149728186bc 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetClientVersionV1.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetClientVersionV1.java @@ -49,9 +49,11 @@ public String getName() { @Override public JsonRpcResponse syncResponse(final JsonRpcRequestContext request) { + String safeCommit = + (commit != null && commit.length() >= 8) ? commit.substring(0, 8) : "unknown"; return new JsonRpcSuccessResponse( request.getRequest().getId(), new EngineGetClientVersionResultV1( - ENGINE_CLIENT_CODE, ENGINE_CLIENT_NAME, clientVersion, commit.substring(0, 8))); + ENGINE_CLIENT_CODE, ENGINE_CLIENT_NAME, clientVersion, safeCommit)); } } diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java index e07b43b9904..5370d2ec46c 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/BlockTransactionSelector.java @@ -15,6 +15,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT_INVALID_TX; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.INVALID_TX_EVALUATION_TOO_LONG; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_EVALUATION_TOO_LONG; @@ -52,9 +53,11 @@ import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer; import org.hyperledger.besu.plugin.services.txselection.PluginTransactionSelector; +import java.time.Duration; import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; @@ -97,11 +100,12 @@ public class BlockTransactionSelector { new TransactionSelectionResults(); private final List transactionSelectors; private final PluginTransactionSelector pluginTransactionSelector; - private final BlockAwareOperationTracer pluginOperationTracer; + private final BlockAwareOperationTracer operationTracer; private final EthScheduler ethScheduler; private final AtomicBoolean isTimeout = new AtomicBoolean(false); private final long blockTxsSelectionMaxTime; private WorldUpdater blockWorldStateUpdater; + private volatile TransactionEvaluationContext currTxEvaluationContext; public BlockTransactionSelector( final MiningParameters miningParameters, @@ -139,7 +143,8 @@ public BlockTransactionSelector( transactionPool); transactionSelectors = createTransactionSelectors(blockSelectionContext); this.pluginTransactionSelector = pluginTransactionSelector; - this.pluginOperationTracer = pluginTransactionSelector.getOperationTracer(); + this.operationTracer = + new InterruptibleOperationTracer(pluginTransactionSelector.getOperationTracer()); blockWorldStateUpdater = worldState.updater(); blockTxsSelectionMaxTime = miningParameters.getBlockTxsSelectionMaxTime(); } @@ -178,15 +183,17 @@ public TransactionSelectionResults buildTransactionListForBlock() { } private void timeLimitedSelection() { - final var txSelection = - ethScheduler.scheduleBlockCreationTask( + final var txSelectionTask = + new FutureTask( () -> blockSelectionContext .transactionPool() - .selectTransactions(this::evaluateTransaction)); + .selectTransactions(this::evaluateTransaction), + null); + ethScheduler.scheduleBlockCreationTask(txSelectionTask); try { - txSelection.get(blockTxsSelectionMaxTime, TimeUnit.MILLISECONDS); + txSelectionTask.get(blockTxsSelectionMaxTime, TimeUnit.MILLISECONDS); } catch (InterruptedException | ExecutionException e) { if (isCancelled.get()) { throw new CancellationException("Cancelled during transaction selection"); @@ -197,6 +204,9 @@ private void timeLimitedSelection() { synchronized (isTimeout) { isTimeout.set(true); } + + cancelEvaluatingTxWithGraceTime(txSelectionTask); + LOG.warn( "Interrupting the selection of transactions for block inclusion as it exceeds the maximum configured duration of " + blockTxsSelectionMaxTime @@ -205,6 +215,40 @@ private void timeLimitedSelection() { } } + private void cancelEvaluatingTxWithGraceTime(final FutureTask txSelectionTask) { + final long elapsedTime = + currTxEvaluationContext.getEvaluationTimer().elapsed(TimeUnit.MILLISECONDS); + // adding 100ms so we are sure it take strictly more than the block selection max time + final long txRemainingTime = (blockTxsSelectionMaxTime - elapsedTime) + 100; + + LOG.atDebug() + .setMessage( + "Transaction {} is processing for {}ms, giving it {}ms grace time, before considering it taking too much time to execute") + .addArgument(currTxEvaluationContext.getPendingTransaction()::toTraceLog) + .addArgument(elapsedTime) + .addArgument(txRemainingTime) + .log(); + + ethScheduler.scheduleFutureTask( + () -> { + if (!txSelectionTask.isDone()) { + LOG.atDebug() + .setMessage( + "Transaction {} is still processing after the grace time, total processing time {}ms," + + " greater than max block selection time of {}ms, forcing an interrupt") + .addArgument(currTxEvaluationContext.getPendingTransaction()::toTraceLog) + .addArgument( + () -> + currTxEvaluationContext.getEvaluationTimer().elapsed(TimeUnit.MILLISECONDS)) + .addArgument(blockTxsSelectionMaxTime) + .log(); + + txSelectionTask.cancel(true); + } + }, + Duration.ofMillis(txRemainingTime)); + } + /** * Evaluates a list of transactions and updates the selection results accordingly. If a * transaction is not selected during the evaluation, it is updated as not selected in the @@ -236,6 +280,7 @@ private TransactionSelectionResult evaluateTransaction( final TransactionEvaluationContext evaluationContext = createTransactionEvaluationContext(pendingTransaction); + currTxEvaluationContext = evaluationContext; TransactionSelectionResult selectionResult = evaluatePreProcessing(evaluationContext); if (!selectionResult.selected()) { @@ -337,7 +382,7 @@ private TransactionProcessingResult processTransaction( blockSelectionContext.pendingBlockHeader(), pendingTransaction.getTransaction(), blockSelectionContext.miningBeneficiary(), - pluginOperationTracer, + operationTracer, blockHashLookup, false, TransactionValidationParams.mining(), @@ -422,14 +467,10 @@ private TransactionSelectionResult handleTransactionNotSelected( final var pendingTransaction = evaluationContext.getPendingTransaction(); // check if this tx took too much to evaluate, and in case it was invalid remove it from the - // pool, otherwise penalize it. + // pool, otherwise penalize it. Not synchronized since there is no state change here. final TransactionSelectionResult actualResult = isTimeout.get() - ? transactionTookTooLong(evaluationContext, selectionResult) - ? selectionResult.discard() - ? INVALID_TX_EVALUATION_TOO_LONG - : TX_EVALUATION_TOO_LONG - : BLOCK_SELECTION_TIMEOUT + ? rewriteSelectionResultForTimeout(evaluationContext, selectionResult) : selectionResult; transactionSelectionResults.updateNotSelected(evaluationContext.getTransaction(), actualResult); @@ -446,6 +487,34 @@ private TransactionSelectionResult handleTransactionNotSelected( return actualResult; } + /** + * In case of a block creation timeout, we rewrite the selection result, so we can easily spot + * what happened looking at the transaction selection results. + * + * @param evaluationContext The current selection session data. + * @param selectionResult The result of the transaction selection process. + * @return the rewritten selection result + */ + private TransactionSelectionResult rewriteSelectionResultForTimeout( + final TransactionEvaluationContext evaluationContext, + final TransactionSelectionResult selectionResult) { + + if (transactionTookTooLong(evaluationContext, selectionResult)) { + return selectionResult.discard() ? INVALID_TX_EVALUATION_TOO_LONG : TX_EVALUATION_TOO_LONG; + } + + return selectionResult.discard() ? BLOCK_SELECTION_TIMEOUT_INVALID_TX : BLOCK_SELECTION_TIMEOUT; + } + + /** + * Check if the evaluation of this tx took more than the block creation max time, because if true + * we want to penalize it. We penalize it, instead of directly removing, because it could happen + * that the tx will evaluate in time next time. Invalid txs are always removed. + * + * @param evaluationContext The current selection session data. + * @param selectionResult The result of the transaction selection process. + * @return true if the evaluation of this tx took more than the block creation max time + */ private boolean transactionTookTooLong( final TransactionEvaluationContext evaluationContext, final TransactionSelectionResult selectionResult) { diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/InterruptibleOperationTracer.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/InterruptibleOperationTracer.java new file mode 100644 index 00000000000..eb7f34bd9a0 --- /dev/null +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/InterruptibleOperationTracer.java @@ -0,0 +1,143 @@ +/* + * 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.blockcreation.txselection; + +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Transaction; +import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.evm.frame.ExceptionalHaltReason; +import org.hyperledger.besu.evm.frame.MessageFrame; +import org.hyperledger.besu.evm.log.Log; +import org.hyperledger.besu.evm.operation.Operation; +import org.hyperledger.besu.evm.worldstate.WorldView; +import org.hyperledger.besu.plugin.data.BlockBody; +import org.hyperledger.besu.plugin.data.BlockHeader; +import org.hyperledger.besu.plugin.data.ProcessableBlockHeader; +import org.hyperledger.besu.plugin.services.tracer.BlockAwareOperationTracer; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import org.apache.tuweni.bytes.Bytes; + +public class InterruptibleOperationTracer implements BlockAwareOperationTracer { + private final BlockAwareOperationTracer delegate; + + public InterruptibleOperationTracer(final BlockAwareOperationTracer delegate) { + this.delegate = delegate; + } + + @Override + public void traceStartBlock(final BlockHeader blockHeader, final BlockBody blockBody) { + delegate.traceStartBlock(blockHeader, blockBody); + } + + @Override + public void traceEndBlock(final BlockHeader blockHeader, final BlockBody blockBody) { + delegate.traceEndBlock(blockHeader, blockBody); + } + + @Override + public void traceStartBlock(final ProcessableBlockHeader processableBlockHeader) { + delegate.traceStartBlock(processableBlockHeader); + } + + @Override + public boolean isExtendedTracing() { + return delegate.isExtendedTracing(); + } + + @Override + public void tracePreExecution(final MessageFrame frame) { + checkInterrupt(); + delegate.tracePreExecution(frame); + } + + @Override + public void tracePostExecution( + final MessageFrame frame, final Operation.OperationResult operationResult) { + checkInterrupt(); + delegate.tracePostExecution(frame, operationResult); + } + + @Override + public void tracePrecompileCall( + final MessageFrame frame, final long gasRequirement, final Bytes output) { + checkInterrupt(); + delegate.tracePrecompileCall(frame, gasRequirement, output); + } + + @Override + public void traceAccountCreationResult( + final MessageFrame frame, final Optional haltReason) { + checkInterrupt(); + delegate.traceAccountCreationResult(frame, haltReason); + } + + @Override + public void tracePrepareTransaction(final WorldView worldView, final Transaction transaction) { + delegate.tracePrepareTransaction(worldView, transaction); + } + + @Override + public void traceStartTransaction(final WorldView worldView, final Transaction transaction) { + delegate.traceStartTransaction(worldView, transaction); + } + + @Override + public void traceBeforeRewardTransaction( + final WorldView worldView, final Transaction tx, final Wei miningReward) { + delegate.traceBeforeRewardTransaction(worldView, tx, miningReward); + } + + @Override + public void traceEndTransaction( + final WorldView worldView, + final Transaction tx, + final boolean status, + final Bytes output, + final List logs, + final long gasUsed, + final Set
selfDestructs, + final long timeNs) { + delegate.traceEndTransaction( + worldView, tx, status, output, logs, gasUsed, selfDestructs, timeNs); + } + + @Override + public void traceContextEnter(final MessageFrame frame) { + checkInterrupt(); + delegate.traceContextEnter(frame); + } + + @Override + public void traceContextReEnter(final MessageFrame frame) { + checkInterrupt(); + delegate.traceContextReEnter(frame); + } + + @Override + public void traceContextExit(final MessageFrame frame) { + checkInterrupt(); + delegate.traceContextExit(frame); + } + + private void checkInterrupt() { + if (Thread.interrupted()) { + throw new RuntimeException(new InterruptedException("Transaction execution interrupted")); + } + } +} diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java index 9eed2bff09e..5a9fd6030cd 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/ProcessingResultTransactionSelector.java @@ -112,6 +112,7 @@ private TransactionSelectionResult transactionSelectionResultForInvalidResult( private boolean isTransientValidationError(final TransactionInvalidReason invalidReason) { return invalidReason.equals(TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE) || invalidReason.equals(TransactionInvalidReason.GAS_PRICE_BELOW_CURRENT_BASE_FEE) - || invalidReason.equals(TransactionInvalidReason.NONCE_TOO_HIGH); + || invalidReason.equals(TransactionInvalidReason.NONCE_TOO_HIGH) + || invalidReason.equals(TransactionInvalidReason.EXECUTION_INTERRUPTED); } } diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java index adcc3ee1e27..6eb03ece947 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/AbstractBlockTransactionSelectorTest.java @@ -14,13 +14,15 @@ */ package org.hyperledger.besu.ethereum.blockcreation; +import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; import static org.awaitility.Awaitility.await; import static org.hyperledger.besu.ethereum.core.MiningParameters.DEFAULT_NON_POA_BLOCK_TXS_SELECTION_MAX_TIME; +import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.EXECUTION_INTERRUPTED; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.NONCE_TOO_LOW; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT; -import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.INVALID_TX_EVALUATION_TOO_LONG; +import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.BLOCK_SELECTION_TIMEOUT_INVALID_TX; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.PRIORITY_FEE_PER_GAS_BELOW_CURRENT_MIN; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.SELECTED; import static org.hyperledger.besu.plugin.data.TransactionSelectionResult.TX_EVALUATION_TOO_LONG; @@ -90,6 +92,7 @@ import org.hyperledger.besu.util.number.PositiveNumber; import java.math.BigInteger; +import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; @@ -185,6 +188,14 @@ public void setup() { when(ethContext.getEthPeers().subscribeConnect(any())).thenReturn(1L); when(ethScheduler.scheduleBlockCreationTask(any(Runnable.class))) .thenAnswer(invocation -> CompletableFuture.runAsync(invocation.getArgument(0))); + when(ethScheduler.scheduleFutureTask(any(Runnable.class), any(Duration.class))) + .thenAnswer( + invocation -> { + final Duration delay = invocation.getArgument(1); + CompletableFuture.delayedExecutor(delay.toMillis(), MILLISECONDS) + .execute(invocation.getArgument(0)); + return null; + }); } protected abstract GenesisConfigFile getGenesisConfigFile(); @@ -982,9 +993,17 @@ private void internalBlockSelectionTimeoutSimulation( .TransactionEvaluationContext ctx = invocation.getArgument(0); if (ctx.getTransaction().equals(p)) { - Thread.sleep(t); + try { + Thread.sleep(t); + } catch (final InterruptedException e) { + return TransactionSelectionResult.invalidTransient(EXECUTION_INTERRUPTED.name()); + } } else { - Thread.sleep(fastProcessingTxTime); + try { + Thread.sleep(fastProcessingTxTime); + } catch (final InterruptedException e) { + return TransactionSelectionResult.invalidTransient(EXECUTION_INTERRUPTED.name()); + } } return SELECTED; }; @@ -1081,18 +1100,19 @@ public void subsetOfInvalidPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOv processingTooLate, postProcessingTooLate, 500, - BLOCK_SELECTION_TIMEOUT, - false, + BLOCK_SELECTION_TIMEOUT_INVALID_TX, + true, NONCE_TOO_LOW); } @ParameterizedTest @MethodSource("subsetOfPendingTransactionsIncludedWhenTxSelectionMaxTimeIsOver") - public void invalidPendingTransactionsThatTakesTooLongToEvaluateIsDroppedFromThePool( - final boolean isPoa, - final boolean preProcessingTooLate, - final boolean processingTooLate, - final boolean postProcessingTooLate) { + public void + evaluationOfInvalidPendingTransactionThatTakesTooLongToEvaluateIsInterruptedAndPenalized( + final boolean isPoa, + final boolean preProcessingTooLate, + final boolean processingTooLate, + final boolean postProcessingTooLate) { internalBlockSelectionTimeoutSimulationInvalidTxs( isPoa, @@ -1100,8 +1120,8 @@ public void invalidPendingTransactionsThatTakesTooLongToEvaluateIsDroppedFromThe processingTooLate, postProcessingTooLate, 900, - INVALID_TX_EVALUATION_TOO_LONG, - true, + TX_EVALUATION_TOO_LONG, + false, NONCE_TOO_LOW); } @@ -1128,9 +1148,17 @@ private void internalBlockSelectionTimeoutSimulationInvalidTxs( .TransactionEvaluationContext ctx = invocation.getArgument(0); if (ctx.getTransaction().equals(p)) { - Thread.sleep(t); + try { + Thread.sleep(t); + } catch (final InterruptedException e) { + return TransactionSelectionResult.invalidTransient(EXECUTION_INTERRUPTED.name()); + } } else { - Thread.sleep(fastProcessingTxTime); + try { + Thread.sleep(fastProcessingTxTime); + } catch (final InterruptedException e) { + return TransactionSelectionResult.invalidTransient(EXECUTION_INTERRUPTED.name()); + } } return invalidSelectionResult; }; @@ -1199,7 +1227,7 @@ private void internalBlockSelectionTimeoutSimulationInvalidTxs( final TransactionSelectionResults results = selector.buildTransactionListForBlock(); - // no tx is selected since all are invalid + // no tx is selected since all are invalid or late assertThat(results.getSelectedTransactions()).isEmpty(); // all txs are not selected so wait until all are evaluated @@ -1350,7 +1378,12 @@ protected void ensureTransactionIsValid( .thenAnswer( invocation -> { if (processingTime > 0) { - Thread.sleep(processingTime); + try { + Thread.sleep(processingTime); + } catch (final InterruptedException e) { + return TransactionProcessingResult.invalid( + ValidationResult.invalid(EXECUTION_INTERRUPTED)); + } } return TransactionProcessingResult.successful( new ArrayList<>(), @@ -1375,7 +1408,12 @@ protected void ensureTransactionIsInvalid( .thenAnswer( invocation -> { if (processingTime > 0) { - Thread.sleep(processingTime); + try { + Thread.sleep(processingTime); + } catch (final InterruptedException e) { + return TransactionProcessingResult.invalid( + ValidationResult.invalid(EXECUTION_INTERRUPTED)); + } } return TransactionProcessingResult.invalid(ValidationResult.invalid(invalidReason)); }); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index 91c964525e0..30a1ea8e762 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -599,6 +599,12 @@ public TransactionProcessingResult processTransaction( EMPTY_ADDRESS_SET, 0L); + final var cause = re.getCause(); + if (cause != null && cause instanceof InterruptedException) { + return TransactionProcessingResult.invalid( + ValidationResult.invalid(TransactionInvalidReason.EXECUTION_INTERRUPTED)); + } + LOG.error("Critical Exception Processing Transaction", re); return TransactionProcessingResult.invalid( ValidationResult.invalid( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java index 8273c556d91..f20cc8fce79 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java @@ -45,6 +45,7 @@ public enum TransactionInvalidReason { MAX_FEE_PER_GAS_BELOW_CURRENT_BASE_FEE, TX_FEECAP_EXCEEDED, INTERNAL_ERROR, + EXECUTION_INTERRUPTED, TX_POOL_DISABLED, INVALID_BLOBS, PLUGIN_TX_POOL_VALIDATOR, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java index 070c1dcf771..29459ba3da4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionSimulator.java @@ -236,7 +236,9 @@ public Optional processWithWorldUpdater( : blockHeaderToProcess.getGasLimit(); if (rpcGasCap > 0) { gasLimit = rpcGasCap; - LOG.info("Capping gasLimit to " + rpcGasCap); + LOG.trace( + "Gas limit capped at {} for transaction simulation due to provided RPC gas cap.", + rpcGasCap); } final Wei value = callParams.getValue() != null ? callParams.getValue() : Wei.ZERO; final Bytes payload = callParams.getPayload() != null ? callParams.getPayload() : Bytes.EMPTY; diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java index 21c75b364a0..a79c05e3e6e 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/layered/LayeredPendingTransactions.java @@ -351,9 +351,7 @@ public void selectTransactions(final PendingTransactions.TransactionSelector sel } }); logDiscardedTransaction(candidatePendingTx, selectionResult); - } - - if (selectionResult.penalize()) { + } else if (selectionResult.penalize()) { ethScheduler.scheduleTxWorkerTask( () -> { synchronized (this) { diff --git a/plugin-api/build.gradle b/plugin-api/build.gradle index bee57ab5aef..6d3a1f93a9a 100644 --- a/plugin-api/build.gradle +++ b/plugin-api/build.gradle @@ -71,7 +71,7 @@ Calculated : ${currentHash} tasks.register('checkAPIChanges', FileStateChecker) { description = "Checks that the API for the Plugin-API project does not change without deliberate thought" files = sourceSets.main.allJava.files - knownHash = 'V/bdVbzJLjdwch266dHHuxIGwiCRhS4w3jDwHt4TWqg=' + knownHash = '5H+3gUzCwZtLByfnk11kf+kAPwykQ+WR+n3xWgyfsyY=' } check.dependsOn('checkAPIChanges') diff --git a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java index 66b1c1f2d65..1c35972a582 100644 --- a/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java +++ b/plugin-api/src/main/java/org/hyperledger/besu/plugin/data/TransactionSelectionResult.java @@ -63,6 +63,7 @@ private enum BaseStatus implements Status { BLOBS_FULL(false, false, false), BLOCK_OCCUPANCY_ABOVE_THRESHOLD(true, false, false), BLOCK_SELECTION_TIMEOUT(true, false, false), + BLOCK_SELECTION_TIMEOUT_INVALID_TX(true, true, true), TX_EVALUATION_TOO_LONG(true, false, true), INVALID_TX_EVALUATION_TOO_LONG(true, true, true), INVALID_TRANSIENT(false, false, true), @@ -121,7 +122,11 @@ public boolean penalize() { public static final TransactionSelectionResult BLOCK_SELECTION_TIMEOUT = new TransactionSelectionResult(BaseStatus.BLOCK_SELECTION_TIMEOUT); - /** Transaction took too much to evaluate, but it was not invalid */ + /** There was no more time to add transaction to the block, and the transaction is invalid */ + public static final TransactionSelectionResult BLOCK_SELECTION_TIMEOUT_INVALID_TX = + new TransactionSelectionResult(BaseStatus.BLOCK_SELECTION_TIMEOUT_INVALID_TX); + + /** Transaction took too much to evaluate, but it was valid */ public static final TransactionSelectionResult TX_EVALUATION_TOO_LONG = new TransactionSelectionResult(BaseStatus.TX_EVALUATION_TOO_LONG);