diff --git a/config/src/main/java/org/hyperledger/besu/config/BlobScheduleOptions.java b/config/src/main/java/org/hyperledger/besu/config/BlobScheduleOptions.java index 1eb1880c7af..fd66c89028f 100644 --- a/config/src/main/java/org/hyperledger/besu/config/BlobScheduleOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/BlobScheduleOptions.java @@ -14,7 +14,11 @@ */ package org.hyperledger.besu.config; +import java.util.Map; +import java.util.Optional; + import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableMap; /** The Checkpoint config options. */ public class BlobScheduleOptions { @@ -39,10 +43,8 @@ public BlobScheduleOptions(final ObjectNode blobScheduleConfigRoot) { * * @return the cancun blob schedule */ - public BlobSchedule getCancun() { - return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, CANCUN_KEY) - .map(BlobSchedule::new) - .orElse(BlobSchedule.DEFAULT); + public Optional getCancun() { + return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, CANCUN_KEY).map(BlobSchedule::new); } /** @@ -50,10 +52,8 @@ public BlobSchedule getCancun() { * * @return the prague blob schedule */ - public BlobSchedule getPrague() { - return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, PRAGUE_KEY) - .map(BlobSchedule::new) - .orElse(BlobSchedule.DEFAULT); + public Optional getPrague() { + return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, PRAGUE_KEY).map(BlobSchedule::new); } /** @@ -61,10 +61,21 @@ public BlobSchedule getPrague() { * * @return the osaka blob schedule */ - public BlobSchedule getOsaka() { - return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, OSAKA_KEY) - .map(BlobSchedule::new) - .orElse(BlobSchedule.DEFAULT); + public Optional getOsaka() { + return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, OSAKA_KEY).map(BlobSchedule::new); + } + + /** + * As map. + * + * @return the map + */ + public Map asMap() { + final ImmutableMap.Builder builder = ImmutableMap.builder(); + getCancun().ifPresent(bs -> builder.put(CANCUN_KEY, bs.asMap())); + getPrague().ifPresent(bs -> builder.put(PRAGUE_KEY, bs.asMap())); + getOsaka().ifPresent(bs -> builder.put(OSAKA_KEY, bs.asMap())); + return builder.build(); } /** The Blob schedule for a particular fork. */ @@ -72,9 +83,6 @@ public static class BlobSchedule { private final int target; private final int max; - /** The constant DEFAULT. */ - public static final BlobSchedule DEFAULT = new BlobSchedule(JsonUtil.createEmptyObjectNode()); - /** The constant CANCUN_DEFAULT. */ public static final BlobSchedule CANCUN_DEFAULT = new BlobSchedule(3, 6); @@ -90,9 +98,8 @@ public static class BlobSchedule { * @param blobScheduleConfigRoot the blob schedule config root */ public BlobSchedule(final ObjectNode blobScheduleConfigRoot) { - // TODO SLD EIP-7840 - reasonable defaults? - this.target = JsonUtil.getInt(blobScheduleConfigRoot, "target").orElse(3); - this.max = JsonUtil.getInt(blobScheduleConfigRoot, "max").orElse(6); + this.target = JsonUtil.getInt(blobScheduleConfigRoot, "target").orElseThrow(); + this.max = JsonUtil.getInt(blobScheduleConfigRoot, "max").orElseThrow(); } private BlobSchedule(final int target, final int max) { @@ -117,5 +124,14 @@ public int getTarget() { public int getMax() { return max; } + + /** + * As map. + * + * @return the map + */ + Map asMap() { + return Map.of("target", target, "max", max); + } } } diff --git a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java index 6005db04b90..9239d4977e8 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -469,7 +469,6 @@ public Optional
getConsolidationRequestContractAddress() { return inputAddress.map(Address::fromHexString); } - // TODO SLD - EIP-7840 @Override public Map asMap() { final ImmutableMap.Builder builder = ImmutableMap.builder(); @@ -545,6 +544,10 @@ public Map asMap() { builder.put("fixedBaseFee", true); } + if (getBlobScheduleOptions().isPresent()) { + builder.put("blobSchedule", getBlobScheduleOptions().get().asMap()); + } + return builder.build(); } diff --git a/config/src/main/resources/mainnet.json b/config/src/main/resources/mainnet.json index ab35a5aba35..5a0fd8d725f 100644 --- a/config/src/main/resources/mainnet.json +++ b/config/src/main/resources/mainnet.json @@ -16,6 +16,16 @@ "terminalTotalDifficulty": 58750000000000000000000, "shanghaiTime": 1681338455, "cancunTime": 1710338135, + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6 + }, + "prague": { + "target": 6, + "max": 9 + } + }, "ethash": { }, "discovery": { diff --git a/config/src/test/java/org/hyperledger/besu/config/BlobScheduleOptionsTest.java b/config/src/test/java/org/hyperledger/besu/config/BlobScheduleOptionsTest.java index 1e56c3d533c..85db660d46d 100644 --- a/config/src/test/java/org/hyperledger/besu/config/BlobScheduleOptionsTest.java +++ b/config/src/test/java/org/hyperledger/besu/config/BlobScheduleOptionsTest.java @@ -28,11 +28,14 @@ public void blobScheduleIsParsed() { assertThat(configOptions.getBlobScheduleOptions()).isNotEmpty(); final BlobScheduleOptions blobScheduleOptions = configOptions.getBlobScheduleOptions().get(); - assertThat(blobScheduleOptions.getCancun().getTarget()).isEqualTo(4); - assertThat(blobScheduleOptions.getCancun().getMax()).isEqualTo(7); - assertThat(blobScheduleOptions.getPrague().getTarget()).isEqualTo(7); - assertThat(blobScheduleOptions.getPrague().getMax()).isEqualTo(10); - assertThat(blobScheduleOptions.getOsaka().getTarget()).isEqualTo(10); - assertThat(blobScheduleOptions.getOsaka().getMax()).isEqualTo(13); + assertThat(blobScheduleOptions.getCancun()).isNotEmpty(); + assertThat(blobScheduleOptions.getCancun().get().getTarget()).isEqualTo(4); + assertThat(blobScheduleOptions.getCancun().get().getMax()).isEqualTo(7); + assertThat(blobScheduleOptions.getPrague()).isNotEmpty(); + assertThat(blobScheduleOptions.getPrague().get().getTarget()).isEqualTo(7); + assertThat(blobScheduleOptions.getPrague().get().getMax()).isEqualTo(10); + assertThat(blobScheduleOptions.getOsaka()).isNotEmpty(); + assertThat(blobScheduleOptions.getOsaka().get().getTarget()).isEqualTo(10); + assertThat(blobScheduleOptions.getOsaka().get().getMax()).isEqualTo(13); } } diff --git a/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java b/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java index fe8149cc654..81467a24a10 100644 --- a/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java +++ b/config/src/test/java/org/hyperledger/besu/config/GenesisConfigOptionsTest.java @@ -408,6 +408,46 @@ void asMapIncludesConsolidationRequestContractAddress() { .containsValue(Address.ZERO); } + @SuppressWarnings("unchecked") + @Test + void asMapIncludesBlobFeeSchedule() { + final GenesisConfigOptions config = + GenesisConfigFile.fromConfig( + "{\n" + + " \"config\": {\n" + + " \"blobSchedule\": {\n" + + " \"cancun\": {\n" + + " \"target\": 1,\n" + + " \"max\": 2\n" + + " },\n" + + " \"prague\": {\n" + + " \"target\": 3,\n" + + " \"max\": 4\n" + + " },\n" + + " \"osaka\": {\n" + + " \"target\": 4,\n" + + " \"max\": 5\n" + + " }\n" + + " }\n" + + " }\n" + + "}") + .getConfigOptions(); + + final Map map = config.asMap(); + assertThat(map).containsOnlyKeys("blobSchedule"); + final Map blobSchedule = (Map) map.get("blobSchedule"); + assertThat(blobSchedule).containsOnlyKeys("cancun", "prague", "osaka"); + assertThat((Map) blobSchedule.get("cancun")) + .containsOnlyKeys("target", "max") + .containsValues(1, 2); + assertThat((Map) blobSchedule.get("prague")) + .containsOnlyKeys("target", "max") + .containsValues(3, 4); + assertThat((Map) blobSchedule.get("osaka")) + .containsOnlyKeys("target", "max") + .containsValues(4, 5); + } + private GenesisConfigOptions fromConfigOptions(final Map configOptions) { final ObjectNode rootNode = JsonUtil.createEmptyObjectNode(); final ObjectNode options = JsonUtil.objectNodeFromMap(configOptions); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java index 4215f9a584c..024b67a0b34 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetProtocolSpecs.java @@ -688,7 +688,7 @@ static ProtocolSpecBuilder cancunDefinition( final var cancunBlobSchedule = genesisConfigOptions .getBlobScheduleOptions() - .map(BlobScheduleOptions::getCancun) + .flatMap(BlobScheduleOptions::getCancun) .orElse(BlobScheduleOptions.BlobSchedule.CANCUN_DEFAULT); final java.util.function.Supplier cancunGasCalcSupplier = @@ -767,10 +767,10 @@ static ProtocolSpecBuilder cancunEOFDefinition( final boolean isParallelTxProcessingEnabled, final MetricsSystem metricsSystem) { - final var blobSchedule = + final var cancunBlobSchedule = genesisConfigOptions .getBlobScheduleOptions() - .map(BlobScheduleOptions::getCancun) + .flatMap(BlobScheduleOptions::getCancun) .orElse(BlobScheduleOptions.BlobSchedule.CANCUN_DEFAULT); ProtocolSpecBuilder protocolSpecBuilder = @@ -782,7 +782,13 @@ static ProtocolSpecBuilder cancunEOFDefinition( miningConfiguration, isParallelTxProcessingEnabled, metricsSystem); - return addEOF(chainId, evmConfiguration, protocolSpecBuilder, blobSchedule.getTarget()) + return addEOF( + genesisConfigOptions, + chainId, + evmConfiguration, + protocolSpecBuilder, + cancunBlobSchedule.getTarget(), + cancunBlobSchedule.getMax()) .name("CancunEOF"); } @@ -802,7 +808,7 @@ static ProtocolSpecBuilder pragueDefinition( final var pragueBlobSchedule = genesisConfigOptions .getBlobScheduleOptions() - .map(BlobScheduleOptions::getPrague) + .flatMap(BlobScheduleOptions::getPrague) .orElse(BlobScheduleOptions.BlobSchedule.PRAGUE_DEFAULT); // EIP-3074 AUTH and AUTHCALL gas | EIP-7840 Blob schedule | EIP-7691 6/9 blob increase @@ -879,7 +885,7 @@ static ProtocolSpecBuilder osakaDefinition( final var osakaBlobSchedule = genesisConfigOptions .getBlobScheduleOptions() - .map(BlobScheduleOptions::getOsaka) + .flatMap(BlobScheduleOptions::getOsaka) .orElse(BlobScheduleOptions.BlobSchedule.OSAKA_DEFAULT); ProtocolSpecBuilder protocolSpecBuilder = @@ -891,20 +897,34 @@ static ProtocolSpecBuilder osakaDefinition( miningConfiguration, isParallelTxProcessingEnabled, metricsSystem); - return addEOF(chainId, evmConfiguration, protocolSpecBuilder, osakaBlobSchedule.getTarget()) + return addEOF( + genesisConfigOptions, + chainId, + evmConfiguration, + protocolSpecBuilder, + osakaBlobSchedule.getTarget(), + osakaBlobSchedule.getMax()) .name("Osaka"); } private static ProtocolSpecBuilder addEOF( + final GenesisConfigOptions genesisConfigOptions, final Optional chainId, final EvmConfiguration evmConfiguration, final ProtocolSpecBuilder protocolSpecBuilder, - final int targetBlobsPerBlock) { + final int targetBlobsPerBlock, + final int maxBlobsPerBlock) { - // TODO SLD EIP-7840 Add OsakaTargetingGasLimitCalculator with maxBlobsPerBlock + final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(0L); + final java.util.function.Supplier osakaGasCalcSupplier = + () -> new OsakaGasCalculator(targetBlobsPerBlock); return protocolSpecBuilder // EIP-7692 EOF v1 Gas calculator - .gasCalculator(() -> new OsakaGasCalculator(targetBlobsPerBlock)) + .gasCalculator(osakaGasCalcSupplier) + .gasLimitCalculatorBuilder( + feeMarket -> + new OsakaTargetingGasLimitCalculator( + londonForkBlockNumber, (BaseFeeMarket) feeMarket, maxBlobsPerBlock)) // EIP-7692 EOF v1 EVM and opcodes .evmBuilder( (gasCalculator, jdCacheConfig) -> @@ -917,7 +937,11 @@ private static ProtocolSpecBuilder addEOF( true, List.of(MaxCodeSizeRule.from(evm), EOFValidationCodeRule.from(evm)), 1, - SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)); + SPURIOUS_DRAGON_FORCE_DELETE_WHEN_EMPTY_ADDRESSES)) + .blockHeaderValidatorBuilder( + fm -> + MainnetBlockHeaderValidator.blobAwareBlockHeaderValidator( + fm, osakaGasCalcSupplier)); } static ProtocolSpecBuilder futureEipsDefinition( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/OsakaTargetingGasLimitCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/OsakaTargetingGasLimitCalculator.java new file mode 100644 index 00000000000..69b4bd549a2 --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/OsakaTargetingGasLimitCalculator.java @@ -0,0 +1,37 @@ +/* + * 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; + +import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; + +public class OsakaTargetingGasLimitCalculator extends PragueTargetingGasLimitCalculator { + + /** The mainnet default maximum number of blobs per block for Osaka */ + private static final int DEFAULT_MAX_BLOBS_PER_BLOCK_OSAKA = 12; + + public OsakaTargetingGasLimitCalculator( + final long londonForkBlock, final BaseFeeMarket feeMarket) { + super(londonForkBlock, feeMarket, DEFAULT_MAX_BLOBS_PER_BLOCK_OSAKA); + } + + /** + * Using Osaka mainnet default of 12 blobs for maxBlobsPerBlock: + * CancunGasCalculator.BLOB_GAS_PER_BLOB * 9 blobs = 131072 * 12 = 1572864 = 0x180000 + */ + public OsakaTargetingGasLimitCalculator( + final long londonForkBlock, final BaseFeeMarket feeMarket, final int maxBlobsPerBlock) { + super(londonForkBlock, feeMarket, maxBlobsPerBlock); + } +}