From 2158a6829676c75931ef5522f06332965fa6c4b6 Mon Sep 17 00:00:00 2001 From: daniellehrner Date: Mon, 12 Aug 2024 11:38:59 +0200 Subject: [PATCH] Fix protocol schedule for devnets (#7429) * add `ProtocolSchedule::milestoneFor` to retrieve milestones for every hardfork in the genesis file * add `setMilestones` and `milestoneFor` to TransitionProtocolSchedule * refactored all checks for hardforks in the engine API to use hard fork ids * added tests to test that the engine API v2 endpoints return UNSUPPORTED_FORK past Cancun Signed-off-by: Daniel Lehrner --------- Signed-off-by: Daniel Lehrner Signed-off-by: Sally MacFarlane Co-authored-by: Sally MacFarlane --- CHANGELOG.md | 1 + .../merge/TransitionProtocolSchedule.java | 8 + .../AbstractEngineForkchoiceUpdated.java | 8 +- .../engine/EngineForkchoiceUpdatedV2.java | 27 +- .../engine/EngineForkchoiceUpdatedV3.java | 34 +-- .../methods/engine/EngineGetPayloadV2.java | 25 +- .../methods/engine/EngineGetPayloadV3.java | 20 +- .../methods/engine/EngineGetPayloadV4.java | 20 +- .../methods/engine/EngineNewPayloadV2.java | 13 + .../methods/engine/EngineNewPayloadV3.java | 21 +- .../methods/engine/EngineNewPayloadV4.java | 20 +- .../methods/engine/ForkSupportHelper.java | 42 ++++ .../AbstractEngineForkchoiceUpdatedTest.java | 29 ++- .../engine/AbstractScheduledApiTest.java | 31 ++- .../engine/EngineForkchoiceUpdatedV2Test.java | 46 ++++ .../engine/EngineGetPayloadV2Test.java | 33 +++ .../engine/EngineNewPayloadV2Test.java | 19 ++ .../engine/EngineNewPayloadV3Test.java | 7 + .../mainnet/DefaultProtocolSchedule.java | 15 ++ .../besu/ethereum/mainnet/HardforkId.java | 61 +++++ .../ethereum/mainnet/ProtocolSchedule.java | 9 + .../mainnet/ProtocolScheduleBuilder.java | 234 +++++++++++++----- .../mainnet/ProtocolScheduleBuilderTest.java | 44 ++++ 23 files changed, 566 insertions(+), 201 deletions(-) create mode 100644 ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/ForkSupportHelper.java create mode 100644 ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/HardforkId.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a97946652d..e7921fac5f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ ### Bug fixes - Correct entrypoint in Docker evmtool [#7430](https://github.com/hyperledger/besu/pull/7430) +- Fix protocol schedule check for devnets [#7429](https://github.com/hyperledger/besu/pull/7429) - Fix behaviour when starting in a pre-merge network [#7431](https://github.com/hyperledger/besu/pull/7431) ## 24.7.1 diff --git a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java index 8600344cefe..f24e11bd8b7 100644 --- a/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java +++ b/consensus/merge/src/main/java/org/hyperledger/besu/consensus/merge/TransitionProtocolSchedule.java @@ -22,6 +22,7 @@ import org.hyperledger.besu.ethereum.core.MiningParameters; import org.hyperledger.besu.ethereum.core.PermissionTransactionFilter; import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; +import org.hyperledger.besu.ethereum.mainnet.HardforkId; import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec; @@ -241,6 +242,13 @@ public String listMilestones() { return transitionUtils.dispatchFunctionAccordingToMergeState(ProtocolSchedule::listMilestones); } + @Override + public Optional milestoneFor(final HardforkId hardforkId) { + return mergeContext.isPostMerge() + ? transitionUtils.getPostMergeObject().milestoneFor(hardforkId) + : transitionUtils.getPreMergeObject().milestoneFor(hardforkId); + } + /** * Sets transaction filter. * diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java index ec65ebe7a9e..eec79e9a7cd 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdated.java @@ -19,6 +19,7 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.SYNCING; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.VALID; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine.WithdrawalsValidatorProvider.getWithdrawalsValidator; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator.ForkchoiceResult; @@ -38,7 +39,6 @@ import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.Withdrawal; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.util.List; @@ -54,7 +54,7 @@ public abstract class AbstractEngineForkchoiceUpdated extends ExecutionEngineJsonRpcMethod { private static final Logger LOG = LoggerFactory.getLogger(AbstractEngineForkchoiceUpdated.class); private final MergeMiningCoordinator mergeCoordinator; - protected final Long cancunTimestamp; + protected final Optional cancunMilestone; public AbstractEngineForkchoiceUpdated( final Vertx vertx, @@ -65,9 +65,7 @@ public AbstractEngineForkchoiceUpdated( super(vertx, protocolSchedule, protocolContext, engineCallListener); this.mergeCoordinator = mergeCoordinator; - Optional cancun = - protocolSchedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun")); - cancunTimestamp = cancun.map(ScheduledProtocolSpec.Hardfork::milestone).orElse(Long.MAX_VALUE); + cancunMilestone = protocolSchedule.milestoneFor(CANCUN); } protected ValidationResult validateParameter( diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2.java index 747c63c162f..1dedf2919a3 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2.java @@ -21,6 +21,7 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; +import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.util.Optional; @@ -51,20 +52,22 @@ public String getName() { @Override protected Optional isPayloadAttributesValid( final Object requestId, final EnginePayloadAttributesParameter payloadAttributes) { - if (payloadAttributes.getTimestamp() >= cancunTimestamp) { - if (payloadAttributes.getParentBeaconBlockRoot() == null - || payloadAttributes.getParentBeaconBlockRoot().isEmpty()) { - return Optional.of(new JsonRpcErrorResponse(requestId, RpcErrorType.UNSUPPORTED_FORK)); - } else { - return Optional.of( - new JsonRpcErrorResponse(requestId, RpcErrorType.INVALID_PAYLOAD_ATTRIBUTES)); - } - } else if (payloadAttributes.getParentBeaconBlockRoot() != null) { + + if (payloadAttributes.getParentBeaconBlockRoot() != null) { LOG.error( - "Parent beacon block root hash present in payload attributes before cancun hardfork"); + "Parent beacon block root hash present in payload attributes before Cancun hardfork"); return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadAttributesError())); - } else { - return Optional.empty(); } + + return Optional.empty(); + } + + @Override + protected ValidationResult validateForkSupported(final long blockTimestamp) { + if (cancunMilestone.isPresent() && blockTimestamp >= cancunMilestone.get()) { + return ValidationResult.invalid(RpcErrorType.UNSUPPORTED_FORK); + } + + return ValidationResult.valid(); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java index b5aebf4f5d7..414d4625b2a 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV3.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; + import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; @@ -22,7 +24,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.util.Optional; @@ -33,7 +34,6 @@ public class EngineForkchoiceUpdatedV3 extends AbstractEngineForkchoiceUpdated { - private final Optional supportedHardFork; private static final Logger LOG = LoggerFactory.getLogger(EngineForkchoiceUpdatedV3.class); public EngineForkchoiceUpdatedV3( @@ -43,11 +43,6 @@ public EngineForkchoiceUpdatedV3( final MergeMiningCoordinator mergeCoordinator, final EngineCallListener engineCallListener) { super(vertx, protocolSchedule, protocolContext, mergeCoordinator, engineCallListener); - this.supportedHardFork = - protocolSchedule.hardforkFor( - s -> - s.fork().name().equalsIgnoreCase("Cancun") - || s.fork().name().equalsIgnoreCase("Prague")); } @Override @@ -80,18 +75,7 @@ protected ValidationResult validateParameter( @Override protected ValidationResult validateForkSupported(final long blockTimestamp) { - if (protocolSchedule.isPresent()) { - if (supportedHardFork.isPresent() && blockTimestamp >= supportedHardFork.get().milestone()) { - return ValidationResult.valid(); - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, - "Cancun configured to start at timestamp: " + supportedHardFork.get().milestone()); - } - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, "Configuration error, no schedule for Cancun fork set"); - } + return ForkSupportHelper.validateForkSupported(CANCUN, cancunMilestone, blockTimestamp); } @Override @@ -101,12 +85,16 @@ protected Optional isPayloadAttributesValid( LOG.error( "Parent beacon block root hash not present in payload attributes after cancun hardfork"); return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadAttributesError())); - } else if (payloadAttributes.getTimestamp().longValue() == 0) { + } + + if (payloadAttributes.getTimestamp() == 0) { return Optional.of(new JsonRpcErrorResponse(requestId, getInvalidPayloadAttributesError())); - } else if (payloadAttributes.getTimestamp() < supportedHardFork.get().milestone()) { + } + + if (cancunMilestone.isEmpty() || payloadAttributes.getTimestamp() < cancunMilestone.get()) { return Optional.of(new JsonRpcErrorResponse(requestId, RpcErrorType.UNSUPPORTED_FORK)); - } else { - return Optional.empty(); } + + return Optional.empty(); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java index ade62077df0..1732585f163 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; + import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.ethereum.ProtocolContext; @@ -24,7 +26,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.util.Optional; @@ -33,7 +34,7 @@ public class EngineGetPayloadV2 extends AbstractEngineGetPayload { - private final Optional cancun; + private final Optional cancunMilestone; public EngineGetPayloadV2( final Vertx vertx, @@ -49,7 +50,7 @@ public EngineGetPayloadV2( mergeMiningCoordinator, blockResultFactory, engineCallListener); - this.cancun = schedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun")); + cancunMilestone = schedule.milestoneFor(CANCUN); } @Override @@ -67,20 +68,10 @@ protected JsonRpcResponse createResponse( @Override protected ValidationResult validateForkSupported(final long blockTimestamp) { - if (protocolSchedule.isPresent()) { - if (cancun.isPresent() && blockTimestamp >= cancun.get().milestone()) { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, - "Cancun configured to start at timestamp: " - + cancun.get().milestone() - + " please call engine_getPayloadV3"); - } else { - return ValidationResult.valid(); - } - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, - "Configuration error, no schedule for Cancun fork set, not sure when to stop honoring use of V2"); + if (cancunMilestone.isPresent() && blockTimestamp >= cancunMilestone.get()) { + return ValidationResult.invalid(RpcErrorType.UNSUPPORTED_FORK); } + + return ValidationResult.valid(); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3.java index 742a240aa02..555afb50b91 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV3.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; + import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.ethereum.ProtocolContext; @@ -24,7 +26,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.util.Optional; @@ -33,7 +34,7 @@ public class EngineGetPayloadV3 extends AbstractEngineGetPayload { - private final Optional cancun; + private final Optional cancunMilestone; public EngineGetPayloadV3( final Vertx vertx, @@ -49,7 +50,7 @@ public EngineGetPayloadV3( mergeMiningCoordinator, blockResultFactory, engineCallListener); - this.cancun = schedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun")); + cancunMilestone = schedule.milestoneFor(CANCUN); } @Override @@ -67,17 +68,6 @@ protected JsonRpcResponse createResponse( @Override protected ValidationResult validateForkSupported(final long blockTimestamp) { - if (protocolSchedule.isPresent()) { - if (cancun.isPresent() && blockTimestamp >= cancun.get().milestone()) { - return ValidationResult.valid(); - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, - "Cancun configured to start at timestamp: " + cancun.get().milestone()); - } - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, "Configuration error, no schedule for Cancun fork set"); - } + return ForkSupportHelper.validateForkSupported(CANCUN, cancunMilestone, blockTimestamp); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4.java index c5a713beea2..f67a2743eac 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV4.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.PRAGUE; + import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.ethereum.ProtocolContext; @@ -24,7 +26,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.BlockResultFactory; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.util.Optional; @@ -33,7 +34,7 @@ public class EngineGetPayloadV4 extends AbstractEngineGetPayload { - private final Optional prague; + private final Optional pragueMilestone; public EngineGetPayloadV4( final Vertx vertx, @@ -49,7 +50,7 @@ public EngineGetPayloadV4( mergeMiningCoordinator, blockResultFactory, engineCallListener); - this.prague = schedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Prague")); + pragueMilestone = schedule.milestoneFor(PRAGUE); } @Override @@ -67,17 +68,6 @@ protected JsonRpcResponse createResponse( @Override protected ValidationResult validateForkSupported(final long blockTimestamp) { - if (protocolSchedule.isPresent()) { - if (prague.isPresent() && blockTimestamp >= prague.get().milestone()) { - return ValidationResult.valid(); - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, - "Prague configured to start at timestamp: " + prague.get().milestone()); - } - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, "Configuration error, no schedule for Prague fork set"); - } + return ForkSupportHelper.validateForkSupported(PRAGUE, pragueMilestone, blockTimestamp); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV2.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV2.java index 640d8dc6036..ba7624a493e 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV2.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV2.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; + import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.datatypes.VersionedHash; import org.hyperledger.besu.ethereum.ProtocolContext; @@ -33,6 +35,7 @@ import io.vertx.core.Vertx; public class EngineNewPayloadV2 extends AbstractEngineNewPayload { + private final Optional cancunMilestone; public EngineNewPayloadV2( final Vertx vertx, @@ -42,6 +45,7 @@ public EngineNewPayloadV2( final EthPeers ethPeers, final EngineCallListener engineCallListener) { super(vertx, protocolSchedule, protocolContext, mergeCoordinator, ethPeers, engineCallListener); + cancunMilestone = protocolSchedule.milestoneFor(CANCUN); } @Override @@ -74,4 +78,13 @@ protected ValidationResult validateBlobs( final ProtocolSpec protocolSpec) { return ValidationResult.valid(); } + + @Override + protected ValidationResult validateForkSupported(final long blockTimestamp) { + if (cancunMilestone.isPresent() && blockTimestamp >= cancunMilestone.get()) { + return ValidationResult.invalid(RpcErrorType.UNSUPPORTED_FORK); + } + + return ValidationResult.valid(); + } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3.java index 1ca232b7737..c6bfb638c87 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; + import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; @@ -21,7 +23,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.util.List; @@ -31,7 +32,7 @@ public class EngineNewPayloadV3 extends AbstractEngineNewPayload { - private final Optional cancun; + private final Optional cancunMilestone; public EngineNewPayloadV3( final Vertx vertx, @@ -42,7 +43,7 @@ public EngineNewPayloadV3( final EngineCallListener engineCallListener) { super( vertx, timestampSchedule, protocolContext, mergeCoordinator, ethPeers, engineCallListener); - this.cancun = timestampSchedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("Cancun")); + this.cancunMilestone = timestampSchedule.milestoneFor(CANCUN); } @Override @@ -73,18 +74,6 @@ protected ValidationResult validateParameters( @Override protected ValidationResult validateForkSupported(final long blockTimestamp) { - if (protocolSchedule.isPresent()) { - if (cancun.isPresent() - && Long.compareUnsigned(blockTimestamp, cancun.get().milestone()) >= 0) { - return ValidationResult.valid(); - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, - "Cancun configured to start at timestamp: " + cancun.get().milestone()); - } - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, "Configuration error, no schedule for Cancun fork set"); - } + return ForkSupportHelper.validateForkSupported(CANCUN, cancunMilestone, blockTimestamp); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4.java index 8b073a95552..09354fa1bac 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV4.java @@ -14,6 +14,8 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.PRAGUE; + import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; import org.hyperledger.besu.ethereum.ProtocolContext; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; @@ -21,7 +23,6 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; import org.hyperledger.besu.ethereum.eth.manager.EthPeers; import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule; -import org.hyperledger.besu.ethereum.mainnet.ScheduledProtocolSpec; import org.hyperledger.besu.ethereum.mainnet.ValidationResult; import java.util.List; @@ -31,7 +32,7 @@ public class EngineNewPayloadV4 extends AbstractEngineNewPayload { - private final Optional prague; + private final Optional pragueMilestone; public EngineNewPayloadV4( final Vertx vertx, @@ -42,7 +43,7 @@ public EngineNewPayloadV4( final EngineCallListener engineCallListener) { super( vertx, timestampSchedule, protocolContext, mergeCoordinator, ethPeers, engineCallListener); - this.prague = timestampSchedule.hardforkFor(s -> s.fork().name().equalsIgnoreCase("prague")); + pragueMilestone = timestampSchedule.milestoneFor(PRAGUE); } @Override @@ -76,17 +77,6 @@ protected ValidationResult validateParameters( @Override protected ValidationResult validateForkSupported(final long blockTimestamp) { - if (protocolSchedule.isPresent()) { - if (prague.isPresent() && blockTimestamp >= prague.get().milestone()) { - return ValidationResult.valid(); - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, - "Prague configured to start at timestamp: " + prague.get().milestone()); - } - } else { - return ValidationResult.invalid( - RpcErrorType.UNSUPPORTED_FORK, "Configuration error, no schedule for Prague fork set"); - } + return ForkSupportHelper.validateForkSupported(PRAGUE, pragueMilestone, blockTimestamp); } } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/ForkSupportHelper.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/ForkSupportHelper.java new file mode 100644 index 00000000000..0059f3d61b1 --- /dev/null +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/ForkSupportHelper.java @@ -0,0 +1,42 @@ +/* + * 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.api.jsonrpc.internal.methods.engine; + +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; +import org.hyperledger.besu.ethereum.mainnet.HardforkId; +import org.hyperledger.besu.ethereum.mainnet.ValidationResult; + +import java.util.Optional; + +public class ForkSupportHelper { + public static ValidationResult validateForkSupported( + final HardforkId hardforkId, + final Optional maybeForkMilestone, + final long blockTimestamp) { + if (maybeForkMilestone.isEmpty()) { + return ValidationResult.invalid( + RpcErrorType.UNSUPPORTED_FORK, + "Configuration error, no schedule for " + hardforkId.name() + " fork set"); + } + + if (blockTimestamp < maybeForkMilestone.get()) { + return ValidationResult.invalid( + RpcErrorType.UNSUPPORTED_FORK, + hardforkId.name() + " configured to start at timestamp: " + maybeForkMilestone.get()); + } + + return ValidationResult.valid(); + } +} diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdatedTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdatedTest.java index 6936f2833d4..d2fb3c37411 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdatedTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractEngineForkchoiceUpdatedTest.java @@ -19,6 +19,7 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.INVALID; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.SYNCING; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.ExecutionEngineJsonRpcMethod.EngineStatus.VALID; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; @@ -92,6 +93,7 @@ public AbstractEngineForkchoiceUpdatedTest(final MethodFactory methodFactory) { private static final Vertx vertx = Vertx.vertx(); private static final Hash mockHash = Hash.hash(Bytes32.fromHexStringLenient("0x1337deadbeef")); + protected static final long CANCUN_MILESTONE = 1_000_000L; private static final EngineForkchoiceUpdatedParameter mockFcuParam = new EngineForkchoiceUpdatedParameter(mockHash, mockHash, mockHash); @@ -100,14 +102,14 @@ public AbstractEngineForkchoiceUpdatedTest(final MethodFactory methodFactory) { new BlockHeaderTestFixture().baseFeePerGas(Wei.ONE); @Mock private ProtocolSpec protocolSpec; - @Mock private ProtocolSchedule protocolSchedule; + @Mock protected ProtocolSchedule protocolSchedule; @Mock private ProtocolContext protocolContext; @Mock private MergeContext mergeContext; @Mock protected MergeMiningCoordinator mergeCoordinator; - @Mock private MutableBlockchain blockchain; + @Mock protected MutableBlockchain blockchain; @Mock private EngineCallListener engineCallListener; @@ -118,6 +120,7 @@ public void before() { when(protocolSpec.getWithdrawalsValidator()) .thenReturn(new WithdrawalsValidator.ProhibitedWithdrawals()); when(protocolSchedule.getForNextBlockHeader(any(), anyLong())).thenReturn(protocolSpec); + when(protocolSchedule.milestoneFor(CANCUN)).thenReturn(Optional.of(CANCUN_MILESTONE)); this.method = methodFactory.create( vertx, protocolSchedule, protocolContext, mergeCoordinator, engineCallListener); @@ -237,7 +240,7 @@ public void shouldReturnValidWithoutFinalizedWithPayload() { var payloadParams = new EnginePayloadAttributesParameter( - String.valueOf(System.currentTimeMillis()), + String.valueOf(defaultPayloadTimestamp()), Bytes32.fromHexStringLenient("0xDEADBEEF").toHexString(), Address.ECREC.toString(), null, @@ -426,7 +429,7 @@ public void shouldIgnoreUpdateToOldHeadAndNotPreparePayload() { var payloadParams = new EnginePayloadAttributesParameter( - String.valueOf(System.currentTimeMillis()), + String.valueOf(defaultPayloadTimestamp()), Bytes32.fromHexStringLenient("0xDEADBEEF").toHexString(), Address.ECREC.toString(), null, @@ -488,7 +491,7 @@ public void shouldReturnInvalidIfWithdrawalsIsNotNull_WhenWithdrawalsProhibited( var payloadParams = new EnginePayloadAttributesParameter( - String.valueOf(System.currentTimeMillis()), + String.valueOf(mockHeader.getTimestamp() + 1), Bytes32.fromHexStringLenient("0xDEADBEEF").toHexString(), Address.ECREC.toString(), emptyList(), @@ -513,7 +516,7 @@ public void shouldReturnValidIfWithdrawalsIsNull_WhenWithdrawalsProhibited() { var payloadParams = new EnginePayloadAttributesParameter( - String.valueOf(System.currentTimeMillis()), + String.valueOf(mockHeader.getTimestamp() + 1), Bytes32.fromHexStringLenient("0xDEADBEEF").toHexString(), Address.ECREC.toString(), null, @@ -557,7 +560,7 @@ public void shouldReturnInvalidIfWithdrawalsIsNull_WhenWithdrawalsAllowed() { var payloadParams = new EnginePayloadAttributesParameter( - String.valueOf(System.currentTimeMillis()), + String.valueOf(mockHeader.getTimestamp() + 1), Bytes32.fromHexStringLenient("0xDEADBEEF").toHexString(), Address.ECREC.toString(), null, @@ -593,7 +596,7 @@ public void shouldReturnValidIfWithdrawalsIsNotNull_WhenWithdrawalsAllowed() { var payloadParams = new EnginePayloadAttributesParameter( - String.valueOf(System.currentTimeMillis()), + String.valueOf(mockHeader.getTimestamp() + 1), Bytes32.fromHexStringLenient("0xDEADBEEF").toHexString(), Address.ECREC.toString(), withdrawalParameters, @@ -642,7 +645,7 @@ public void shouldReturnValidIfProtocolScheduleIsEmpty() { var payloadParams = new EnginePayloadAttributesParameter( - String.valueOf(System.currentTimeMillis()), + String.valueOf(mockHeader.getTimestamp() + 1), Bytes32.fromHexStringLenient("0xDEADBEEF").toHexString(), Address.ECREC.toString(), null, @@ -674,7 +677,7 @@ public void shouldReturnValidIfProtocolScheduleIsEmpty() { verify(engineCallListener, times(1)).executionEngineCalled(); } - private void setupValidForkchoiceUpdate(final BlockHeader mockHeader) { + protected void setupValidForkchoiceUpdate(final BlockHeader mockHeader) { when(blockchain.getBlockHeader(any())).thenReturn(Optional.of(mockHeader)); when(mergeCoordinator.getOrSyncHeadByHash(mockHeader.getHash(), Hash.ZERO)) .thenReturn(Optional.of(mockHeader)); @@ -738,7 +741,7 @@ protected RpcErrorType expectedInvalidPayloadError() { return RpcErrorType.INVALID_PARAMS; } - private JsonRpcResponse resp( + protected JsonRpcResponse resp( final EngineForkchoiceUpdatedParameter forkchoiceParam, final Optional payloadParam) { return method.response( @@ -772,4 +775,8 @@ private void assertInvalidForkchoiceState( assertThat(errorResp.getErrorType()).isEqualTo(jsonRpcError); assertThat(errorResp.getError().getMessage()).isEqualTo(jsonRpcError.getMessage()); } + + protected long defaultPayloadTimestamp() { + return 1; + } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractScheduledApiTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractScheduledApiTest.java index 9da345dbcdc..bfe23e07e77 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractScheduledApiTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/AbstractScheduledApiTest.java @@ -14,7 +14,12 @@ */ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; -import static org.mockito.ArgumentMatchers.argThat; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.EXPERIMENTAL_EIPS; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.LONDON; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.PARIS; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.PRAGUE; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.SHANGHAI; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; @@ -67,22 +72,22 @@ public boolean matches(final Predicate value) { @BeforeEach public void before() { lenient() - .when(protocolSchedule.hardforkFor(argThat(new HardforkMatcher(londonHardfork)))) - .thenReturn(Optional.of(londonHardfork)); + .when(protocolSchedule.milestoneFor((LONDON))) + .thenReturn(Optional.of(londonHardfork.milestone())); lenient() - .when(protocolSchedule.hardforkFor(argThat(new HardforkMatcher(parisHardfork)))) - .thenReturn(Optional.of(parisHardfork)); + .when(protocolSchedule.milestoneFor(PARIS)) + .thenReturn(Optional.of(parisHardfork.milestone())); lenient() - .when(protocolSchedule.hardforkFor(argThat(new HardforkMatcher(cancunHardfork)))) - .thenReturn(Optional.of(cancunHardfork)); + .when(protocolSchedule.milestoneFor(CANCUN)) + .thenReturn(Optional.of(cancunHardfork.milestone())); lenient() - .when(protocolSchedule.hardforkFor(argThat(new HardforkMatcher(pragueHardfork)))) - .thenReturn(Optional.of(pragueHardfork)); + .when(protocolSchedule.milestoneFor(PRAGUE)) + .thenReturn(Optional.of(pragueHardfork.milestone())); lenient() - .when(protocolSchedule.hardforkFor(argThat(new HardforkMatcher(shanghaiHardfork)))) - .thenReturn(Optional.of(shanghaiHardfork)); + .when(protocolSchedule.milestoneFor(SHANGHAI)) + .thenReturn(Optional.of(shanghaiHardfork.milestone())); lenient() - .when(protocolSchedule.hardforkFor(argThat(new HardforkMatcher(experimentalHardfork)))) - .thenReturn(Optional.of(experimentalHardfork)); + .when(protocolSchedule.milestoneFor(EXPERIMENTAL_EIPS)) + .thenReturn(Optional.of(experimentalHardfork.milestone())); } } diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2Test.java index 550e1380d27..7ae80313a1d 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2Test.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineForkchoiceUpdatedV2Test.java @@ -15,10 +15,26 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.UNSUPPORTED_FORK; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; +import org.hyperledger.besu.consensus.merge.blockcreation.MergeMiningCoordinator; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EngineForkchoiceUpdatedParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.EnginePayloadAttributesParameter; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import java.util.Optional; + +import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @@ -39,6 +55,36 @@ public void shouldReturnExpectedMethodName() { assertThat(method.getName()).isEqualTo("engine_forkchoiceUpdatedV2"); } + @Test + public void shouldReturnUnsupportedForkIfBlockTimestampIsAfterCancunMilestone() { + when(mergeCoordinator.updateForkChoice( + any(BlockHeader.class), any(Hash.class), any(Hash.class))) + .thenReturn(mock(MergeMiningCoordinator.ForkchoiceResult.class)); + + BlockHeader mockHeader = blockHeaderBuilder.timestamp(CANCUN_MILESTONE + 1).buildHeader(); + setupValidForkchoiceUpdate(mockHeader); + + final EngineForkchoiceUpdatedParameter param = + new EngineForkchoiceUpdatedParameter(mockHeader.getBlockHash(), Hash.ZERO, Hash.ZERO); + + final EnginePayloadAttributesParameter payloadParams = + new EnginePayloadAttributesParameter( + String.valueOf(mockHeader.getTimestamp()), + Bytes32.fromHexStringLenient("0xDEADBEEF").toHexString(), + Address.ECREC.toString(), + null, + null); + + final JsonRpcResponse resp = resp(param, Optional.of(payloadParams)); + + final JsonRpcError jsonRpcError = + Optional.of(resp) + .map(JsonRpcErrorResponse.class::cast) + .map(JsonRpcErrorResponse::getError) + .get(); + assertThat(jsonRpcError.getCode()).isEqualTo(UNSUPPORTED_FORK.getCode()); + } + @Override protected String getMethodName() { return RpcMethod.ENGINE_FORKCHOICE_UPDATED_V2.getMethodName(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java index 5f42f02c82b..5c455222f17 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineGetPayloadV2Test.java @@ -15,16 +15,26 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.engine; import static org.assertj.core.api.Assertions.assertThat; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.UNSUPPORTED_FORK; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import org.hyperledger.besu.consensus.merge.PayloadWrapper; import org.hyperledger.besu.ethereum.api.jsonrpc.RpcMethod; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.EngineGetPayloadResultV2; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity; +import org.hyperledger.besu.ethereum.core.Block; +import org.hyperledger.besu.ethereum.core.BlockBody; +import org.hyperledger.besu.ethereum.core.BlockHeader; +import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; +import org.hyperledger.besu.ethereum.core.BlockWithReceipts; +import java.util.Collections; import java.util.Optional; import org.apache.tuweni.bytes.Bytes32; @@ -106,6 +116,29 @@ public void shouldReturnExecutionPayloadWithoutWithdrawals_PreShanghaiBlock() { verify(engineCallListener, times(1)).executionEngineCalled(); } + @Test + public void shouldReturnUnsupportedForkIfBlockTimestampIsAfterCancunMilestone() { + // Cancun starts at timestamp 30 + final BlockHeader mockHeader = new BlockHeaderTestFixture().timestamp(31L).buildHeader(); + final Block mockBlock = + new Block(mockHeader, new BlockBody(Collections.emptyList(), Collections.emptyList())); + final BlockWithReceipts mockBlockWithReceipts = + new BlockWithReceipts(mockBlock, Collections.emptyList()); + final PayloadWrapper mockPayload = new PayloadWrapper(mockPid, mockBlockWithReceipts); + + when(mergeContext.retrievePayloadById(mockPid)).thenReturn(Optional.of(mockPayload)); + + final var resp = resp(RpcMethod.ENGINE_GET_PAYLOAD_V2.getMethodName(), mockPid); + + final JsonRpcError jsonRpcError = + Optional.of(resp) + .map(JsonRpcErrorResponse.class::cast) + .map(JsonRpcErrorResponse::getError) + .get(); + assertThat(jsonRpcError.getCode()).isEqualTo(UNSUPPORTED_FORK.getCode()); + verify(engineCallListener, times(1)).executionEngineCalled(); + } + @Override protected String getMethodName() { return RpcMethod.ENGINE_GET_PAYLOAD_V2.getMethodName(); diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV2Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV2Test.java index afee556eda2..4d53d83cf8b 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV2Test.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV2Test.java @@ -19,6 +19,7 @@ import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.parameters.WithdrawalParameterTestFixture.WITHDRAWAL_PARAM_1; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_BLOB_GAS_USED_PARAMS; import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.INVALID_PARAMS; +import static org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.RpcErrorType.UNSUPPORTED_FORK; import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -193,6 +194,24 @@ public void shouldReturnInvalidIfWithdrawalsIsNull_WhenWithdrawalsAllowed() { verify(engineCallListener, times(1)).executionEngineCalled(); } + @Test + public void shouldReturnUnsupportedForkIfBlockTimestampIsAfterCancunMilestone() { + // Cancun starte at timestamp 30 + final long blockTimestamp = 31L; + BlockHeader blockHeader = + createBlockHeaderFixture( + Optional.of(Collections.emptyList()), Optional.empty(), Optional.empty()) + .timestamp(blockTimestamp) + .buildHeader(); + + var resp = + resp(mockEnginePayload(blockHeader, Collections.emptyList(), List.of(), null, null, null)); + + final JsonRpcError jsonRpcError = fromErrorResp(resp); + assertThat(jsonRpcError.getCode()).isEqualTo(UNSUPPORTED_FORK.getCode()); + verify(engineCallListener, times(1)).executionEngineCalled(); + } + @Override protected ExecutionEngineJsonRpcMethod.EngineStatus getExpectedInvalidBlockHashStatus() { return INVALID; diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3Test.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3Test.java index 82025888176..72f0108a692 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3Test.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/engine/EngineNewPayloadV3Test.java @@ -66,6 +66,7 @@ import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes32; import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.junit.jupiter.MockitoExtension; @@ -261,6 +262,12 @@ private Transaction createTransactionWithBlobs() { .createTransaction(senderKeys); } + @Override + @Disabled + public void shouldReturnUnsupportedForkIfBlockTimestampIsAfterCancunMilestone() { + // only relevant for v2 + } + @Override protected JsonRpcResponse resp(final EnginePayloadParameter payload) { Object[] params = diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolSchedule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolSchedule.java index e868abf2aed..d6d3cc0a559 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolSchedule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/DefaultProtocolSchedule.java @@ -25,6 +25,8 @@ import java.math.BigInteger; import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; import java.util.NavigableSet; import java.util.Optional; import java.util.TreeSet; @@ -39,6 +41,8 @@ public class DefaultProtocolSchedule implements ProtocolSchedule { protected NavigableSet protocolSpecs = new TreeSet<>(Comparator.comparing(ScheduledProtocolSpec::fork).reversed()); + private final Map milestones = new HashMap<>(); + private final Optional chainId; public DefaultProtocolSchedule(final Optional chainId) { @@ -97,6 +101,12 @@ public void putTimestampMilestone(final long timestamp, final ProtocolSpec proto putMilestone(TimestampProtocolSpec.create(timestamp, protocolSpec)); } + @Override + public void setMilestones(final Map milestones) { + this.milestones.clear(); + this.milestones.putAll(milestones); + } + private void putMilestone(final ScheduledProtocolSpec scheduledProtocolSpec) { // Ensure this replaces any existing spec at the same block number. protocolSpecs.remove(scheduledProtocolSpec); @@ -117,6 +127,11 @@ public Optional hardforkFor( .map(ScheduledProtocolSpec::fork); } + @Override + public Optional milestoneFor(final HardforkId hardforkId) { + return Optional.ofNullable(milestones.get(hardforkId)); + } + @Override public void setPermissionTransactionFilter( final PermissionTransactionFilter permissionTransactionFilter) { diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/HardforkId.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/HardforkId.java new file mode 100644 index 00000000000..6c764a42094 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/HardforkId.java @@ -0,0 +1,61 @@ +/* + * 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.mainnet; + +public interface HardforkId { + + String name(); + + enum MainnetHardforkId implements HardforkId { + FRONTIER, + HOMESTEAD, + DAO_FORK, + TANGERINE_WHISTLE, + SPURIOUS_DRAGON, + BYZANTIUM, + CONSTANTINOPLE, + PETERSBURG, + ISTANBUL, + MUIR_GLACIER, + BERLIN, + LONDON, + ARROW_GLACIER, + GRAY_GLACIER, + PARIS, + SHANGHAI, + CANCUN, + CANCUN_EOF, + PRAGUE, + PRAGUE_EOF, + FUTURE_EIPS, + EXPERIMENTAL_EIPS + } + + enum ClassicHardforkId implements HardforkId { + FRONTIER, + HOMESTEAD, + CLASSIC_TANGERINE_WHISTLE, + DIE_HARD, + GOTHAM, + DEFUSE_DIFFICULTY_BOMB, + ATLANTIS, + AGHARTA, + PHOENIX, + THANOS, + MAGNETO, + MYSTIQUE, + SPIRAL + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSchedule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSchedule.java index e07b2835e9f..88448a8b549 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSchedule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolSchedule.java @@ -19,6 +19,7 @@ import org.hyperledger.besu.ethereum.core.ProcessableBlockHeader; import java.math.BigInteger; +import java.util.Map; import java.util.Optional; import java.util.function.Predicate; @@ -46,11 +47,19 @@ default ProtocolSpec getForNextBlockHeader( void putTimestampMilestone(final long timestamp, final ProtocolSpec protocolSpec); + default void setMilestones(final Map milestoneList) { + throw new UnsupportedOperationException("Not implemented"); + } + default Optional hardforkFor( final Predicate predicate) { throw new UnsupportedOperationException("Not implemented"); } + default Optional milestoneFor(final HardforkId hardforkId) { + throw new UnsupportedOperationException("Not implemented"); + } + boolean isOnMilestoneBoundary(final BlockHeader blockHeader); boolean anyMatch(Predicate predicate); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java index 1cb3a5cb3c5..fb8abbe1b65 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilder.java @@ -23,6 +23,8 @@ import org.hyperledger.besu.plugin.services.MetricsSystem; import java.math.BigInteger; +import java.util.List; +import java.util.Map; import java.util.NavigableMap; import java.util.Optional; import java.util.OptionalInt; @@ -142,7 +144,11 @@ private void initSchedule( validateForkOrdering(); - final NavigableMap builders = buildMilestoneMap(specFactory); + final List mileStones = createMilestones(specFactory); + final Map completeMileStoneList = buildFullMilestoneMap(mileStones); + protocolSchedule.setMilestones(completeMileStoneList); + + final NavigableMap builders = buildFlattenedMilestoneMap(mileStones); // At this stage, all milestones are flagged with the correct modifier, but ProtocolSpecs must // be @@ -159,7 +165,11 @@ private void initSchedule( builders.put( modifierBlock, new BuilderMapEntry( - parent.milestoneType, modifierBlock, parent.builder(), entry.getValue())); + parent.hardforkId, + parent.milestoneType, + modifierBlock, + parent.builder(), + entry.getValue())); }); } @@ -306,10 +316,9 @@ private long validateForkOrder( return referenceForkBlock; } - private NavigableMap buildMilestoneMap( - final MainnetProtocolSpecFactory specFactory) { - return createMilestones(specFactory) - .flatMap(Optional::stream) + private NavigableMap buildFlattenedMilestoneMap( + final List mileStones) { + return mileStones.stream() .collect( Collectors.toMap( BuilderMapEntry::blockIdentifier, @@ -318,68 +327,170 @@ private NavigableMap buildMilestoneMap( TreeMap::new)); } - private Stream> createMilestones( - final MainnetProtocolSpecFactory specFactory) { + private Map buildFullMilestoneMap(final List mileStones) { + return mileStones.stream() + .collect( + Collectors.toMap( + b -> b.hardforkId, + BuilderMapEntry::blockIdentifier, + (existing, replacement) -> existing)); + } + + private List createMilestones(final MainnetProtocolSpecFactory specFactory) { return Stream.of( - blockNumberMilestone(OptionalLong.of(0), specFactory.frontierDefinition()), - blockNumberMilestone(config.getHomesteadBlockNumber(), specFactory.homesteadDefinition()), - blockNumberMilestone( - config.getTangerineWhistleBlockNumber(), specFactory.tangerineWhistleDefinition()), - blockNumberMilestone( - config.getSpuriousDragonBlockNumber(), specFactory.spuriousDragonDefinition()), - blockNumberMilestone(config.getByzantiumBlockNumber(), specFactory.byzantiumDefinition()), - blockNumberMilestone( - config.getConstantinopleBlockNumber(), specFactory.constantinopleDefinition()), - blockNumberMilestone(config.getPetersburgBlockNumber(), specFactory.petersburgDefinition()), - blockNumberMilestone(config.getIstanbulBlockNumber(), specFactory.istanbulDefinition()), - blockNumberMilestone( - config.getMuirGlacierBlockNumber(), specFactory.muirGlacierDefinition()), - blockNumberMilestone(config.getBerlinBlockNumber(), specFactory.berlinDefinition()), - blockNumberMilestone(config.getLondonBlockNumber(), specFactory.londonDefinition(config)), - blockNumberMilestone( - config.getArrowGlacierBlockNumber(), specFactory.arrowGlacierDefinition(config)), - blockNumberMilestone( - config.getGrayGlacierBlockNumber(), specFactory.grayGlacierDefinition(config)), - blockNumberMilestone( - config.getMergeNetSplitBlockNumber(), specFactory.parisDefinition(config)), - // Timestamp Forks - timestampMilestone(config.getShanghaiTime(), specFactory.shanghaiDefinition(config)), - timestampMilestone(config.getCancunTime(), specFactory.cancunDefinition(config)), - timestampMilestone(config.getCancunEOFTime(), specFactory.cancunEOFDefinition(config)), - timestampMilestone(config.getPragueTime(), specFactory.pragueDefinition(config)), - timestampMilestone(config.getPragueEOFTime(), specFactory.pragueEOFDefinition(config)), - timestampMilestone(config.getFutureEipsTime(), specFactory.futureEipsDefinition(config)), - timestampMilestone( - config.getExperimentalEipsTime(), specFactory.experimentalEipsDefinition(config)), - - // Classic Milestones - blockNumberMilestone( - config.getEcip1015BlockNumber(), specFactory.tangerineWhistleDefinition()), - blockNumberMilestone(config.getDieHardBlockNumber(), specFactory.dieHardDefinition()), - blockNumberMilestone(config.getGothamBlockNumber(), specFactory.gothamDefinition()), - blockNumberMilestone( - config.getDefuseDifficultyBombBlockNumber(), - specFactory.defuseDifficultyBombDefinition()), - blockNumberMilestone(config.getAtlantisBlockNumber(), specFactory.atlantisDefinition()), - blockNumberMilestone(config.getAghartaBlockNumber(), specFactory.aghartaDefinition()), - blockNumberMilestone(config.getPhoenixBlockNumber(), specFactory.phoenixDefinition()), - blockNumberMilestone(config.getThanosBlockNumber(), specFactory.thanosDefinition()), - blockNumberMilestone(config.getMagnetoBlockNumber(), specFactory.magnetoDefinition()), - blockNumberMilestone(config.getMystiqueBlockNumber(), specFactory.mystiqueDefinition()), - blockNumberMilestone(config.getSpiralBlockNumber(), specFactory.spiralDefinition())); + blockNumberMilestone( + HardforkId.MainnetHardforkId.FRONTIER, + OptionalLong.of(0), + specFactory.frontierDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.HOMESTEAD, + config.getHomesteadBlockNumber(), + specFactory.homesteadDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.TANGERINE_WHISTLE, + config.getTangerineWhistleBlockNumber(), + specFactory.tangerineWhistleDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.SPURIOUS_DRAGON, + config.getSpuriousDragonBlockNumber(), + specFactory.spuriousDragonDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.BYZANTIUM, + config.getByzantiumBlockNumber(), + specFactory.byzantiumDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.CONSTANTINOPLE, + config.getConstantinopleBlockNumber(), + specFactory.constantinopleDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.PETERSBURG, + config.getPetersburgBlockNumber(), + specFactory.petersburgDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.ISTANBUL, + config.getIstanbulBlockNumber(), + specFactory.istanbulDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.MUIR_GLACIER, + config.getMuirGlacierBlockNumber(), + specFactory.muirGlacierDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.BERLIN, + config.getBerlinBlockNumber(), + specFactory.berlinDefinition()), + blockNumberMilestone( + HardforkId.MainnetHardforkId.LONDON, + config.getLondonBlockNumber(), + specFactory.londonDefinition(config)), + blockNumberMilestone( + HardforkId.MainnetHardforkId.ARROW_GLACIER, + config.getArrowGlacierBlockNumber(), + specFactory.arrowGlacierDefinition(config)), + blockNumberMilestone( + HardforkId.MainnetHardforkId.GRAY_GLACIER, + config.getGrayGlacierBlockNumber(), + specFactory.grayGlacierDefinition(config)), + blockNumberMilestone( + HardforkId.MainnetHardforkId.PARIS, + config.getMergeNetSplitBlockNumber(), + specFactory.parisDefinition(config)), + // Timestamp Forks + timestampMilestone( + HardforkId.MainnetHardforkId.SHANGHAI, + config.getShanghaiTime(), + specFactory.shanghaiDefinition(config)), + timestampMilestone( + HardforkId.MainnetHardforkId.CANCUN, + config.getCancunTime(), + specFactory.cancunDefinition(config)), + timestampMilestone( + HardforkId.MainnetHardforkId.CANCUN_EOF, + config.getCancunEOFTime(), + specFactory.cancunEOFDefinition(config)), + timestampMilestone( + HardforkId.MainnetHardforkId.PRAGUE, + config.getPragueTime(), + specFactory.pragueDefinition(config)), + timestampMilestone( + HardforkId.MainnetHardforkId.PRAGUE_EOF, + config.getPragueEOFTime(), + specFactory.pragueEOFDefinition(config)), + timestampMilestone( + HardforkId.MainnetHardforkId.FUTURE_EIPS, + config.getFutureEipsTime(), + specFactory.futureEipsDefinition(config)), + timestampMilestone( + HardforkId.MainnetHardforkId.EXPERIMENTAL_EIPS, + config.getExperimentalEipsTime(), + specFactory.experimentalEipsDefinition(config)), + + // Classic Milestones + blockNumberMilestone( + HardforkId.ClassicHardforkId.CLASSIC_TANGERINE_WHISTLE, + config.getEcip1015BlockNumber(), + specFactory.tangerineWhistleDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.DIE_HARD, + config.getDieHardBlockNumber(), + specFactory.dieHardDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.GOTHAM, + config.getGothamBlockNumber(), + specFactory.gothamDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.DEFUSE_DIFFICULTY_BOMB, + config.getDefuseDifficultyBombBlockNumber(), + specFactory.defuseDifficultyBombDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.ATLANTIS, + config.getAtlantisBlockNumber(), + specFactory.atlantisDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.AGHARTA, + config.getAghartaBlockNumber(), + specFactory.aghartaDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.PHOENIX, + config.getPhoenixBlockNumber(), + specFactory.phoenixDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.THANOS, + config.getThanosBlockNumber(), + specFactory.thanosDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.MAGNETO, + config.getMagnetoBlockNumber(), + specFactory.magnetoDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.MYSTIQUE, + config.getMystiqueBlockNumber(), + specFactory.mystiqueDefinition()), + blockNumberMilestone( + HardforkId.ClassicHardforkId.SPIRAL, + config.getSpiralBlockNumber(), + specFactory.spiralDefinition())) + .flatMap(Optional::stream) + .toList(); } private Optional timestampMilestone( - final OptionalLong blockIdentifier, final ProtocolSpecBuilder builder) { - return createMilestone(blockIdentifier, builder, BuilderMapEntry.MilestoneType.TIMESTAMP); + final HardforkId hardforkId, + final OptionalLong blockIdentifier, + final ProtocolSpecBuilder builder) { + return createMilestone( + hardforkId, blockIdentifier, builder, BuilderMapEntry.MilestoneType.TIMESTAMP); } private Optional blockNumberMilestone( - final OptionalLong blockIdentifier, final ProtocolSpecBuilder builder) { - return createMilestone(blockIdentifier, builder, BuilderMapEntry.MilestoneType.BLOCK_NUMBER); + final HardforkId hardforkId, + final OptionalLong blockIdentifier, + final ProtocolSpecBuilder builder) { + return createMilestone( + hardforkId, blockIdentifier, builder, BuilderMapEntry.MilestoneType.BLOCK_NUMBER); } private Optional createMilestone( + final HardforkId hardforkId, final OptionalLong blockIdentifier, final ProtocolSpecBuilder builder, final BuilderMapEntry.MilestoneType milestoneType) { @@ -389,7 +500,11 @@ private Optional createMilestone( final long blockVal = blockIdentifier.getAsLong(); return Optional.of( new BuilderMapEntry( - milestoneType, blockVal, builder, protocolSpecAdapters.getModifierForBlock(blockVal))); + hardforkId, + milestoneType, + blockVal, + builder, + protocolSpecAdapters.getModifierForBlock(blockVal))); } private ProtocolSpec getProtocolSpec( @@ -429,6 +544,7 @@ private void addProtocolSpec( } private record BuilderMapEntry( + HardforkId hardforkId, ProtocolScheduleBuilder.BuilderMapEntry.MilestoneType milestoneType, long blockIdentifier, ProtocolSpecBuilder builder, diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java index 0e1011de761..a1f3d647979 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/ProtocolScheduleBuilderTest.java @@ -16,6 +16,11 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.BERLIN; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.CANCUN; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.LONDON; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.PRAGUE; +import static org.hyperledger.besu.ethereum.mainnet.HardforkId.MainnetHardforkId.SHANGHAI; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -32,6 +37,7 @@ import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem; import java.math.BigInteger; +import java.util.Optional; import java.util.OptionalLong; import java.util.function.Function; import java.util.stream.Collectors; @@ -122,6 +128,44 @@ void createProtocolScheduleInOrder() { .isEqualTo("Prague"); } + @Test + void milestoneForShouldQueryAllAvailableHardforks() { + final long PRAGUE_TIME = 1722333828L; + + when(configOptions.getHomesteadBlockNumber()).thenReturn(OptionalLong.of(0)); + when(configOptions.getByzantiumBlockNumber()).thenReturn(OptionalLong.of(0)); + when(configOptions.getConstantinopleBlockNumber()).thenReturn(OptionalLong.of(0)); + when(configOptions.getPetersburgBlockNumber()).thenReturn(OptionalLong.of(0)); + when(configOptions.getIstanbulBlockNumber()).thenReturn(OptionalLong.of(0)); + when(configOptions.getBerlinBlockNumber()).thenReturn(OptionalLong.of(0)); + when(configOptions.getLondonBlockNumber()).thenReturn(OptionalLong.of(0)); + when(configOptions.getShanghaiTime()).thenReturn(OptionalLong.of(0)); + when(configOptions.getCancunTime()).thenReturn(OptionalLong.of(0)); + when(configOptions.getPragueTime()).thenReturn(OptionalLong.of(PRAGUE_TIME)); + + final ProtocolSchedule protocolSchedule = builder.createProtocolSchedule(); + + final Optional maybeBerlinMileStone = protocolSchedule.milestoneFor(BERLIN); + assertThat(maybeBerlinMileStone).isPresent(); + assertThat(maybeBerlinMileStone.get()).isEqualTo(0); + + final Optional maybeLondonMileStone = protocolSchedule.milestoneFor(LONDON); + assertThat(maybeLondonMileStone).isPresent(); + assertThat(maybeLondonMileStone.get()).isEqualTo(0); + + final Optional maybeShanghaiMileStone = protocolSchedule.milestoneFor(SHANGHAI); + assertThat(maybeShanghaiMileStone).isPresent(); + assertThat(maybeShanghaiMileStone.get()).isEqualTo(0); + + final Optional maybeCancunMileStone = protocolSchedule.milestoneFor(CANCUN); + assertThat(maybeCancunMileStone).isPresent(); + assertThat(maybeCancunMileStone.get()).isEqualTo(0); + + final Optional maybePragueMileStone = protocolSchedule.milestoneFor(PRAGUE); + assertThat(maybePragueMileStone).isPresent(); + assertThat(maybePragueMileStone.get()).isEqualTo(PRAGUE_TIME); + } + @Test void createProtocolScheduleOverlappingUsesLatestFork() { when(configOptions.getHomesteadBlockNumber()).thenReturn(OptionalLong.of(0L));