diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/RequestType.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/RequestType.java index 5a8fe97a3..23a54ec1b 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/RequestType.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/RequestType.java @@ -52,8 +52,25 @@ public enum RequestType { case 0x01 -> WITHDRAWAL; case 0x02 -> CONSOLIDATION; default -> - throw new IllegalArgumentException( + throw new InvalidRequestTypeException( String.format("Unsupported request type: 0x%02X", serializedTypeValue)); }; } + + /** + * Exception thrown when an invalid request type is encountered. + * + *

This exception is thrown when a serialized type value does not correspond to any {@link + * RequestType}. + */ + public static class InvalidRequestTypeException extends IllegalArgumentException { + /** + * Constructs an {@link InvalidRequestTypeException} with the specified detail message. + * + * @param message the detail message. + */ + public InvalidRequestTypeException(final String message) { + super(message); + } + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java index c9ad05600..929135ae5 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineNewPayload.java @@ -202,13 +202,15 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet final Optional> maybeRequests; try { maybeRequests = extractRequests(maybeRequestsParam); - } catch (RuntimeException ex) { + } catch (RequestType.InvalidRequestTypeException ex) { return respondWithInvalid( reqId, blockParam, mergeCoordinator.getLatestValidAncestor(blockParam.getParentHash()).orElse(null), INVALID, "Invalid execution requests"); + } catch (Exception ex) { + return new JsonRpcErrorResponse(reqId, RpcErrorType.INVALID_EXECUTION_REQUESTS_PARAMS); } if (!getRequestsValidator( @@ -591,14 +593,17 @@ public abstract class AbstractEngineNewPayload extends ExecutionEngineJsonRpcMet if (maybeRequestsParam.isEmpty()) { return Optional.empty(); } - return maybeRequestsParam.map( requests -> requests.stream() .map( s -> { final Bytes request = Bytes.fromHexString(s); - return new Request(RequestType.of(request.get(0)), request.slice(1)); + final Bytes requestData = request.slice(1); + if (requestData.isEmpty()) { + throw new IllegalArgumentException("Request data cannot be empty"); + } + return new Request(RequestType.of(request.get(0)), requestData); }) .collect(Collectors.toList())); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index 97a8e1f7d..91e179ce3 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -17,6 +17,7 @@ package org.hyperledger.besu.ethereum.mainnet; import static org.hyperledger.besu.ethereum.mainnet.feemarket.ExcessBlobGasCalculator.calculateExcessBlobGasForParent; import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.TransactionType; import org.hyperledger.besu.datatypes.Wei; import org.hyperledger.besu.ethereum.BlockProcessingOutputs; @@ -250,6 +251,19 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { maybeRequests = Optional.of(requestProcessor.get().process(context)); } + if (maybeRequests.isPresent() && blockHeader.getRequestsHash().isPresent()) { + Hash calculatedRequestHash = BodyValidation.requestsHash(maybeRequests.get()); + Hash headerRequestsHash = blockHeader.getRequestsHash().get(); + if (!calculatedRequestHash.equals(headerRequestsHash)) { + return new BlockProcessingResult( + Optional.empty(), + "Requests hash mismatch, calculated: " + + calculatedRequestHash.toHexString() + + " header: " + + headerRequestsHash.toHexString()); + } + } + if (!rewardCoinbase(worldState, blockHeader, ommers, skipZeroBlockRewards)) { // no need to log, rewardCoinbase logs the error. if (worldState instanceof BonsaiWorldState) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidator.java index fb49ba71f..237e5cab4 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidator.java @@ -14,12 +14,14 @@ */ package org.hyperledger.besu.ethereum.mainnet.requests; +import org.hyperledger.besu.datatypes.RequestType; import org.hyperledger.besu.ethereum.core.Request; +import java.util.Comparator; import java.util.List; import java.util.Optional; -import com.google.common.collect.Ordering; +import com.google.common.collect.Comparators; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -46,15 +48,25 @@ public class MainnetRequestsValidator implements RequestsValidator { return false; } - if (!isRequestOrderValid(maybeRequests.get())) { - LOG.warn("Ordering across requests must be ascending by type"); + List requests = maybeRequests.get(); + if (!areRequestTypesUniqueAndOrderValid(requests)) { + LOG.warn("Request types must be unique and ordering must be ascending by type"); return false; } + if (containsRequestWithEmptyData(requests)) { + LOG.warn("Request must not be empty"); + return false; + } return true; } - private static boolean isRequestOrderValid(final List requests) { - return Ordering.natural().onResultOf(Request::getType).isOrdered(requests); + private static boolean areRequestTypesUniqueAndOrderValid(final List requests) { + final List requestTypes = requests.stream().map(Request::type).toList(); + return Comparators.isInStrictOrder(requestTypes, Comparator.naturalOrder()); + } + + private static boolean containsRequestWithEmptyData(final List requests) { + return requests.stream().anyMatch(request -> request.getData().isEmpty()); } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidatorTest.java index 410a72630..4541d43d7 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/requests/MainnetRequestsValidatorTest.java @@ -55,4 +55,21 @@ class MainnetRequestsValidatorTest { new Request(RequestType.CONSOLIDATION, Bytes.of(3))); assertTrue(validator.validate(Optional.of(requests))); } + + @Test + void validateFalseForEmptyRequest() { + MainnetRequestsValidator validator = new MainnetRequestsValidator(); + List requests = List.of(new Request(RequestType.DEPOSIT, Bytes.EMPTY)); + assertFalse(validator.validate(Optional.of(requests))); + } + + @Test + void validateFalseForDuplicatedRequests() { + MainnetRequestsValidator validator = new MainnetRequestsValidator(); + List requests = + List.of( + new Request(RequestType.DEPOSIT, Bytes.of(1)), + new Request(RequestType.DEPOSIT, Bytes.of(1))); + assertFalse(validator.validate(Optional.of(requests))); + } } diff --git a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/block-test/osaka-eof-rjump.json b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/block-test/osaka-eof-rjump.json index b70da91fc..7658ac3a9 100644 --- a/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/block-test/osaka-eof-rjump.json +++ b/ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/block-test/osaka-eof-rjump.json @@ -27,8 +27,8 @@ "blobGasUsed": "0x00", "excessBlobGas": "0x00", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "requestsHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "hash": "0x67315ef3267f6f654068ccbd317423b1028fd5305b94a56d1f27e6651e06d678" + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "hash": "0xf87b21fa838d23ffb4eb990363863611d2cbf707dd2e80cdcdf14bbd506bb369" }, "pre": { "0x00000000219ab540356cbb839cbe05303d7705fa": { @@ -178,7 +178,7 @@ "balance": "0x00", "code": "0x3373fffffffffffffffffffffffffffffffffffffffe14604657602036036042575f35600143038111604257611fff81430311604257611fff9006545f5260205ff35b5f5ffd5b5f35611fff60014303065500", "storage": { - "0x00": "0x67315ef3267f6f654068ccbd317423b1028fd5305b94a56d1f27e6651e06d678" + "0x00": "0xf87b21fa838d23ffb4eb990363863611d2cbf707dd2e80cdcdf14bbd506bb369" } }, "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": { @@ -194,15 +194,15 @@ "storage": {} } }, - "lastblockhash": "0x9ca58820df28ca6d09450fff5fdf93d39976e3aa098c6981ae08f391d44ffb3f", - "genesisRLP": "0xf90262f9025ba00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a06cb1761e069313d13f39d755da011dc921b1f0fe5c4c3e951891639e479b4cfba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808088016345785d8a0000808000a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421c0c0c0c0", + "lastblockhash": "0x63f38b8d65c153dacf94ab767f13d0919671e1d1ac703a377d2387782be31acf", + "genesisRLP": "0xf90262f9025ba00000000000000000000000000000000000000000000000000000000000000000a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347940000000000000000000000000000000000000000a06cb1761e069313d13f39d755da011dc921b1f0fe5c4c3e951891639e479b4cfba056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000808088016345785d8a0000808000a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855c0c0c0c0", "blocks": [ { "blockHeader": { - "parentHash": "0x67315ef3267f6f654068ccbd317423b1028fd5305b94a56d1f27e6651e06d678", + "parentHash": "0xf87b21fa838d23ffb4eb990363863611d2cbf707dd2e80cdcdf14bbd506bb369", "uncleHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba", - "stateRoot": "0x61171b085ffd8d099ca59ba13164e8883d89c89d3298256aa229b03a6e33d246", + "stateRoot": "0x499d3ba7df30b168f8af11e0b02041d802bdd9615ef0f363cb14e4872b9d9422", "transactionsTrie": "0xec9d10cff79619f2df45db8c66526ef3fbd32d283fdd2dcc9b55c0efe643d8c3", "receiptTrie": "0x9593f56abf23bcbb26d27b0c6e46a56415d9103ed6b4d8ac7b4182f9f250cafa", "bloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", @@ -219,8 +219,8 @@ "blobGasUsed": "0x00", "excessBlobGas": "0x00", "parentBeaconBlockRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", - "requestsHash": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421", - "hash": "0x9ca58820df28ca6d09450fff5fdf93d39976e3aa098c6981ae08f391d44ffb3f" + "requestsHash": "0xe3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + "hash": "0x63f38b8d65c153dacf94ab767f13d0919671e1d1ac703a377d2387782be31acf" }, "transactions": [ { @@ -243,7 +243,7 @@ "depositRequests": [], "withdrawalRequests": [], "consolidationRequests": [], - "rlp": "0xf902c8f9025fa067315ef3267f6f654068ccbd317423b1028fd5305b94a56d1f27e6651e06d678a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa061171b085ffd8d099ca59ba13164e8883d89c89d3298256aa229b03a6e33d246a0ec9d10cff79619f2df45db8c66526ef3fbd32d283fdd2dcc9b55c0efe643d8c3a09593f56abf23bcbb26d27b0c6e46a56415d9103ed6b4d8ac7b4182f9f250cafab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800188016345785d8a000082a8648203e800a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421f862f860800a83989680940000000000000000000000000000000000001000808026a0e5d462429669f661291a8dc4c49a092cfd4922b6f3f31c9189a2f4adf5ecd730a001494afaf472fbb80bcb107ffeb918a2b9115f454027840615d6d20d63c69ac0c0c0", + "rlp": "0xf902c8f9025fa0f87b21fa838d23ffb4eb990363863611d2cbf707dd2e80cdcdf14bbd506bb369a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347942adc25665018aa1fe0e6bc666dac8fc2697ff9baa0499d3ba7df30b168f8af11e0b02041d802bdd9615ef0f363cb14e4872b9d9422a0ec9d10cff79619f2df45db8c66526ef3fbd32d283fdd2dcc9b55c0efe643d8c3a09593f56abf23bcbb26d27b0c6e46a56415d9103ed6b4d8ac7b4182f9f250cafab9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800188016345785d8a000082a8648203e800a0000000000000000000000000000000000000000000000000000000000000000088000000000000000007a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855f862f860800a83989680940000000000000000000000000000000000001000808026a0e5d462429669f661291a8dc4c49a092cfd4922b6f3f31c9189a2f4adf5ecd730a001494afaf472fbb80bcb107ffeb918a2b9115f454027840615d6d20d63c69ac0c0c0", "blocknumber": "1" } ], @@ -259,5 +259,5 @@ } } }, - "stdout": "Considering tests/osaka/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_Osaka-blockchain_test]\nBlock 1 (0x9ca58820df28ca6d09450fff5fdf93d39976e3aa098c6981ae08f391d44ffb3f) Imported\nChain import successful - tests/osaka/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_Osaka-blockchain_test]\n" + "stdout": "Considering tests/osaka/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_Osaka-blockchain_test]\nBlock 1 (0x63f38b8d65c153dacf94ab767f13d0919671e1d1ac703a377d2387782be31acf) Imported\nChain import successful - tests/osaka/eip7692_eof_v1/eip4200_relative_jumps/test_rjump.py::test_rjump_zero[fork_Osaka-blockchain_test]\n" }