diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f0661e3cf..7c9b70891d 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 e0e64087c0..eea989cc95 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 f699321efb..17958bdbd4 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 a9eb54acb8..9e4a7b7b42 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 7e9a36719d..ca3e6d95df 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 class OperatorSubCommandTest extends CommandTestAbstract { false, singletonList("key.pub"), Optional.empty(), - Optional.of( - "0xf853a00000000000000000000000000000000000000000000000000000000000000000ea94d5feb0fc5a54a89f97aeb34c3df15397c19f6dd294d6a9a4c886eb008ac307abdc1f38745c1dd13a88808400000000c0")); + List.of( + new Field( + "extraData", + "0xf853a00000000000000000000000000000000000000000000000000000000000000000ea94d5feb0fc5a54a89f97aeb34c3df15397c19f6dd294d6a9a4c886eb008ac307abdc1f38745c1dd13a88808400000000c0"))); } @Test @@ -296,8 +298,49 @@ public class OperatorSubCommandTest extends CommandTestAbstract { 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 @@ public class OperatorSubCommandTest extends CommandTestAbstract { generate, expectedKeyFiles, Optional.empty(), - Optional.empty()); + List.of()); } private void runCmdAndCheckOutput( @@ -336,9 +379,11 @@ public class OperatorSubCommandTest extends CommandTestAbstract { 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 @@ public class OperatorSubCommandTest extends CommandTestAbstract { 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 @@ public class OperatorSubCommandTest extends CommandTestAbstract { 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 3723fdc3bc..212bb943ea 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 1483c45f38..3699740aba 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 f66f588ffe..bbfe9f0c6a 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 db4e57b545..78e4cd3343 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 3fc4561e3a..b9c20be753 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 fe757326b4..449ba343ee 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 bd189d0235..b56654b136 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 4f59b9ebd7..0870f9e872 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 2841aea9c4..5dfa454797 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 316aa73d04..6b42c3c2ed 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 @@ interface 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 bcb89c64ed..430c74efb9 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.OptionalInt; 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.ObjectMapper; 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 @@ public class 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 5d73a2829f..f8327174a3 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 class GenesisReaderTest { .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 1e1df382e7..fa6b4ec146 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 class JsonUtilTest { 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 689cb44e6a..149728186b 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 class EngineGetClientVersionV1 extends ExecutionEngineJsonRpcMethod { @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 e07b43b990..5370d2ec46 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.data.TransactionSelectionResult; 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 class 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 class BlockTransactionSelector { } 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 @@ public class BlockTransactionSelector { 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 @@ public class BlockTransactionSelector { } } + 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 @@ public class BlockTransactionSelector { final TransactionEvaluationContext evaluationContext = createTransactionEvaluationContext(pendingTransaction); + currTxEvaluationContext = evaluationContext; TransactionSelectionResult selectionResult = evaluatePreProcessing(evaluationContext); if (!selectionResult.selected()) { @@ -337,7 +382,7 @@ public class BlockTransactionSelector { blockSelectionContext.pendingBlockHeader(), pendingTransaction.getTransaction(), blockSelectionContext.miningBeneficiary(), - pluginOperationTracer, + operationTracer, blockHashLookup, false, TransactionValidationParams.mining(), @@ -422,14 +467,10 @@ public class BlockTransactionSelector { 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 @@ public class BlockTransactionSelector { 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 0000000000..eb7f34bd9a --- /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 9eed2bff09..5a9fd6030c 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 @@ public class ProcessingResultTransactionSelector extends AbstractTransactionSele 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 adcc3ee1e2..6eb03ece94 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.services.kvstore.InMemoryKeyValueStorage; 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 abstract class AbstractBlockTransactionSelectorTest { 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 @@ public abstract class AbstractBlockTransactionSelectorTest { .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 abstract class AbstractBlockTransactionSelectorTest { 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 abstract class AbstractBlockTransactionSelectorTest { processingTooLate, postProcessingTooLate, 900, - INVALID_TX_EVALUATION_TOO_LONG, - true, + TX_EVALUATION_TOO_LONG, + false, NONCE_TOO_LOW); } @@ -1128,9 +1148,17 @@ public abstract class AbstractBlockTransactionSelectorTest { .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 @@ public abstract class AbstractBlockTransactionSelectorTest { 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 @@ public abstract class AbstractBlockTransactionSelectorTest { .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 @@ public abstract class AbstractBlockTransactionSelectorTest { .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 91c964525e..30a1ea8e76 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 class MainnetTransactionProcessor { 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 8273c556d9..f20cc8fce7 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 070c1dcf77..29459ba3da 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 class TransactionSimulator { : 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 21c75b364a..a79c05e3e6 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 class LayeredPendingTransactions implements PendingTransactions { } }); 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 bee57ab5ae..6d3a1f93a9 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 66b1c1f2d6..1c35972a58 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 @@ public class TransactionSelectionResult { 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 class TransactionSelectionResult { 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);