diff --git a/CHANGELOG.md b/CHANGELOG.md index 5202ffb9c8a..bad5945a611 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,13 @@ ### Additions and Improvements - Add RPC HTTP options to specify custom truststore and its password [#7978](https://github.com/hyperledger/besu/pull/7978) - Retrieve all transaction receipts for a block in one request [#6646](https://github.com/hyperledger/besu/pull/6646) +- Implement EIP-7840: Add blob schedule to config files [#8042](https://github.com/hyperledger/besu/pull/8042) ### Bug fixes - Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024) - Revise the approach for setting level_compaction_dynamic_level_bytes RocksDB configuration option [#8037](https://github.com/hyperledger/besu/pull/8037) +- Fix possible incomplete txpool restore from dump file [#7991](https://github.com/hyperledger/besu/pull/7991) ## 24.12.2 Hotfix diff --git a/acceptance-tests/tests/src/test/resources/dev/dev_prague.json b/acceptance-tests/tests/src/test/resources/dev/dev_prague.json index 942edc4e70a..7a7b56d9508 100644 --- a/acceptance-tests/tests/src/test/resources/dev/dev_prague.json +++ b/acceptance-tests/tests/src/test/resources/dev/dev_prague.json @@ -15,6 +15,20 @@ "terminalTotalDifficulty":0, "cancunTime":0, "pragueTime":0, + "blobSchedule": { + "cancun": { + "target": 3, + "max": 6 + }, + "prague": { + "target": 6, + "max": 9 + }, + "osaka": { + "target": 9, + "max": 12 + } + }, "clique": { "period": 5, "epoch": 30000 diff --git a/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java b/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java index f512bf35402..6a0804b6f46 100644 --- a/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java +++ b/besu/src/main/java/org/hyperledger/besu/services/TraceServiceImpl.java @@ -20,6 +20,7 @@ import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.datatypes.Hash; import org.hyperledger.besu.datatypes.Wei; +import org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods.TraceBlock.ChainUpdater; import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.Tracer; import org.hyperledger.besu.ethereum.api.query.BlockchainQueries; import org.hyperledger.besu.ethereum.chain.Blockchain; @@ -155,14 +156,14 @@ public void trace( blocks.get(0).getHash(), traceableState -> { final WorldUpdater worldStateUpdater = traceableState.updater(); + final ChainUpdater chainUpdater = new ChainUpdater(traceableState, worldStateUpdater); beforeTracing.accept(worldStateUpdater); final List results = new ArrayList<>(); blocks.forEach( block -> { - results.addAll(trace(blockchain, block, worldStateUpdater, tracer)); - worldStateUpdater.commit(); + results.addAll(trace(blockchain, block, chainUpdater, tracer)); }); - afterTracing.accept(worldStateUpdater); + afterTracing.accept(chainUpdater.getNextUpdater()); return Optional.of(results); }); } @@ -177,7 +178,7 @@ private Optional> trace( blockchainQueries, block.getHash(), traceableState -> - Optional.of(trace(blockchain, block, traceableState.updater(), tracer))); + Optional.of(trace(blockchain, block, new ChainUpdater(traceableState), tracer))); return results; } @@ -185,7 +186,7 @@ private Optional> trace( private List trace( final Blockchain blockchain, final Block block, - final WorldUpdater worldUpdater, + final ChainUpdater chainUpdater, final BlockAwareOperationTracer tracer) { final List results = new ArrayList<>(); final ProtocolSpec protocolSpec = protocolSchedule.getByBlockHeader(block.getHeader()); @@ -208,6 +209,7 @@ private List trace( .map(parent -> calculateExcessBlobGasForParent(protocolSpec, parent)) .orElse(BlobGas.ZERO)); + final WorldUpdater worldUpdater = chainUpdater.getNextUpdater(); final TransactionProcessingResult result = transactionProcessor.processTransaction( worldUpdater, diff --git a/config/src/main/java/org/hyperledger/besu/config/BlobScheduleOptions.java b/config/src/main/java/org/hyperledger/besu/config/BlobScheduleOptions.java new file mode 100644 index 00000000000..1ad458fa398 --- /dev/null +++ b/config/src/main/java/org/hyperledger/besu/config/BlobScheduleOptions.java @@ -0,0 +1,137 @@ +/* + * 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.config; + +import java.util.Map; +import java.util.Optional; + +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.google.common.collect.ImmutableMap; + +/** The Blob Schedule config options. */ +public class BlobScheduleOptions { + + private final ObjectNode blobScheduleOptionsConfigRoot; + + private static final String CANCUN_KEY = "cancun"; + private static final String PRAGUE_KEY = "prague"; + private static final String OSAKA_KEY = "osaka"; + + /** + * Instantiates a new Blob Schedule config options. + * + * @param blobScheduleConfigRoot the blob schedule config root + */ + public BlobScheduleOptions(final ObjectNode blobScheduleConfigRoot) { + this.blobScheduleOptionsConfigRoot = blobScheduleConfigRoot; + } + + /** + * Gets cancun blob schedule. + * + * @return the cancun blob schedule + */ + public Optional getCancun() { + return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, CANCUN_KEY).map(BlobSchedule::new); + } + + /** + * Gets prague blob schedule. + * + * @return the prague blob schedule + */ + public Optional getPrague() { + return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, PRAGUE_KEY).map(BlobSchedule::new); + } + + /** + * Gets osaka blob schedule. + * + * @return the osaka blob schedule + */ + 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. */ + public static class BlobSchedule { + private final int target; + private final int max; + + /** The constant CANCUN_DEFAULT. */ + public static final BlobSchedule CANCUN_DEFAULT = new BlobSchedule(3, 6); + + /** The constant PRAGUE_DEFAULT. */ + public static final BlobSchedule PRAGUE_DEFAULT = new BlobSchedule(6, 9); + + /** The constant OSAKA_DEFAULT. */ + public static final BlobSchedule OSAKA_DEFAULT = new BlobSchedule(9, 12); + + /** + * Instantiates a new Blob schedule. + * + * @param blobScheduleConfigRoot the blob schedule config root + */ + public BlobSchedule(final ObjectNode blobScheduleConfigRoot) { + this.target = JsonUtil.getInt(blobScheduleConfigRoot, "target").orElseThrow(); + this.max = JsonUtil.getInt(blobScheduleConfigRoot, "max").orElseThrow(); + } + + private BlobSchedule(final int target, final int max) { + this.target = target; + this.max = max; + } + + /** + * Gets target. + * + * @return the target + */ + public int getTarget() { + return target; + } + + /** + * Gets max. + * + * @return the max + */ + 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/GenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java index e2458c93247..877f17de416 100644 --- a/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/GenesisConfigOptions.java @@ -546,4 +546,11 @@ default boolean isConsensusMigration() { * @return the consolidation request contract address */ Optional
getConsolidationRequestContractAddress(); + + /** + * The blob schedule is a list of hardfork names and their associated target and max blob values. + * + * @return the blob schedule + */ + Optional getBlobScheduleOptions(); } 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 51f85f0ec82..9239d4977e8 100644 --- a/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/JsonGenesisConfigOptions.java @@ -47,6 +47,7 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions { private static final String TRANSITIONS_CONFIG_KEY = "transitions"; private static final String DISCOVERY_CONFIG_KEY = "discovery"; private static final String CHECKPOINT_CONFIG_KEY = "checkpoint"; + private static final String BLOB_SCHEDULE_CONFIG_KEY = "blobschedule"; private static final String ZERO_BASE_FEE_KEY = "zerobasefee"; private static final String FIXED_BASE_FEE_KEY = "fixedbasefee"; private static final String WITHDRAWAL_REQUEST_CONTRACT_ADDRESS_KEY = @@ -199,6 +200,12 @@ public EthashConfigOptions getEthashConfigOptions() { .orElse(EthashConfigOptions.DEFAULT); } + @Override + public Optional getBlobScheduleOptions() { + return JsonUtil.getObjectNode(configRoot, BLOB_SCHEDULE_CONFIG_KEY) + .map(BlobScheduleOptions::new); + } + @Override public TransitionsConfigOptions getTransitions() { return transitions; @@ -537,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/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java index c9c8dad7954..2f6e6897a94 100644 --- a/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java +++ b/config/src/main/java/org/hyperledger/besu/config/StubGenesisConfigOptions.java @@ -472,6 +472,11 @@ public Optional
getConsolidationRequestContractAddress() { return Optional.empty(); } + @Override + public Optional getBlobScheduleOptions() { + return Optional.empty(); + } + /** * Homestead block stub genesis config options. * 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 new file mode 100644 index 00000000000..fc746362929 --- /dev/null +++ b/config/src/test/java/org/hyperledger/besu/config/BlobScheduleOptionsTest.java @@ -0,0 +1,41 @@ +/* + * 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.config; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.junit.jupiter.api.Test; + +public class BlobScheduleOptionsTest { + + @Test + public void blobScheduleIsParsed() { + final GenesisConfig genesisConfigFile = + GenesisConfig.fromResource("/mainnet_with_blob_schedule.json"); + final GenesisConfigOptions configOptions = genesisConfigFile.getConfigOptions(); + + assertThat(configOptions.getBlobScheduleOptions()).isNotEmpty(); + final BlobScheduleOptions blobScheduleOptions = configOptions.getBlobScheduleOptions().get(); + 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 9b09362851f..16622f35c21 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 = + GenesisConfig.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/config/src/test/resources/mainnet_with_blob_schedule.json b/config/src/test/resources/mainnet_with_blob_schedule.json new file mode 100644 index 00000000000..b313a7dbabe --- /dev/null +++ b/config/src/test/resources/mainnet_with_blob_schedule.json @@ -0,0 +1,37 @@ +{ + "config": { + "chainId": 3151908, + "homesteadBlock": 0, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "preMergeForkBlock": 0, + "terminalTotalDifficulty": 0, + "ethash": {}, + "shanghaiTime": 0, + "cancunTime": 0, + "blobSchedule": { + "cancun": { + "target": 4, + "max": 7 + }, + "prague": { + "target": 7, + "max": 10 + }, + "osaka": { + "target": 10, + "max": 13 + } + }, + "depositContractAddress": "0x4242424242424242424242424242424242424242", + "pragueTime": 1734106711, + "osakaTime": 1734107095 + } +} \ No newline at end of file diff --git a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelector.java b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelector.java index 9a4c83e9625..d5fd4212ec4 100644 --- a/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelector.java +++ b/ethereum/blockcreation/src/main/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelector.java @@ -61,7 +61,8 @@ public TransactionSelectionResult evaluateTransactionPreProcessing( if (remainingBlobGas == 0) { LOG.atTrace() - .setMessage("The block already contains the max number of allowed blobs") + .setMessage( + "The block already contains the max number of allowed blobs, pending tx: {}") .addArgument(evaluationContext.getPendingTransaction()::toTraceLog) .log(); return TransactionSelectionResult.BLOBS_FULL; diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java index 7c15d6229c0..0d1a09511a5 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/GasLimitCalculator.java @@ -17,8 +17,11 @@ /** The GasLimitCalculator interface defines methods for calculating the gas limit. */ public interface GasLimitCalculator { - /** The constant BLOB_GAS_LIMIT represents the gas limit for blob data. */ - long BLOB_GAS_LIMIT = 786432; + /** + * The constant BLOB_GAS_LIMIT represents the gas limit for blob data. Defaults to the Cancun + * value where it was first introduced as part of EIP-4844 + */ + long BLOB_GAS_LIMIT = 0xC0000; /** * Calculates the next gas limit based on the current gas limit, target gas limit, and new block diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CancunTargetingGasLimitCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CancunTargetingGasLimitCalculator.java index 7dd1be481f6..707abda32a6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CancunTargetingGasLimitCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/CancunTargetingGasLimitCalculator.java @@ -15,17 +15,33 @@ package org.hyperledger.besu.ethereum.mainnet; import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket; +import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; public class CancunTargetingGasLimitCalculator extends LondonTargetingGasLimitCalculator { - private static final long MAX_BLOB_GAS_PER_BLOCK = 786432L; + + /** The mainnet default maximum number of blobs per block for Cancun */ + private static final int DEFAULT_MAX_BLOBS_PER_BLOCK_CANCUN = 6; + + private final long maxBlobGasPerBlock; public CancunTargetingGasLimitCalculator( final long londonForkBlock, final BaseFeeMarket feeMarket) { + this(londonForkBlock, feeMarket, DEFAULT_MAX_BLOBS_PER_BLOCK_CANCUN); + } + + /** + * Using Cancun mainnet default of 6 blobs for maxBlobsPerBlock: getBlobGasPerBlob() * 6 blobs = + * 131072 * 6 = 786432 = 0xC0000 + */ + public CancunTargetingGasLimitCalculator( + final long londonForkBlock, final BaseFeeMarket feeMarket, final int maxBlobsPerBlock) { super(londonForkBlock, feeMarket); + final CancunGasCalculator cancunGasCalculator = new CancunGasCalculator(); + this.maxBlobGasPerBlock = cancunGasCalculator.getBlobGasPerBlob() * maxBlobsPerBlock; } @Override public long currentBlobGasLimit() { - return MAX_BLOB_GAS_PER_BLOCK; + return maxBlobGasPerBlock; } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockHeaderValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockHeaderValidator.java index 0598c817bd0..79927d28cf9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockHeaderValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetBlockHeaderValidator.java @@ -33,9 +33,10 @@ import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.ProofOfWorkValidationRule; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampBoundedByFutureParameter; import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.TimestampMoreRecentThanParent; -import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; import java.util.Optional; +import java.util.function.Supplier; import com.google.common.annotations.VisibleForTesting; import org.apache.tuweni.bytes.Bytes; @@ -197,8 +198,9 @@ public static BlockHeaderValidator.Builder mergeBlockHeaderValidator(final FeeMa .addRule(new IncrementalTimestampRule()); } - public static BlockHeaderValidator.Builder cancunBlockHeaderValidator(final FeeMarket feeMarket) { + public static BlockHeaderValidator.Builder blobAwareBlockHeaderValidator( + final FeeMarket feeMarket, final Supplier gasCalculator) { return mergeBlockHeaderValidator(feeMarket) - .addRule(new BlobGasValidationRule(new CancunGasCalculator())); + .addRule(new BlobGasValidationRule(gasCalculator.get())); } } 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 26c9e69c8f5..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 @@ -16,6 +16,7 @@ import static org.hyperledger.besu.ethereum.mainnet.requests.MainnetRequestsProcessor.pragueRequestsProcessors; +import org.hyperledger.besu.config.BlobScheduleOptions; import org.hyperledger.besu.config.GenesisConfigOptions; import org.hyperledger.besu.config.PowAlgorithm; import org.hyperledger.besu.crypto.SignatureAlgorithm; @@ -56,6 +57,7 @@ import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator; import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator; +import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.HomesteadGasCalculator; import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator; import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator; @@ -683,6 +685,15 @@ static ProtocolSpecBuilder cancunDefinition( FeeMarket.cancun(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas()); } + final var cancunBlobSchedule = + genesisConfigOptions + .getBlobScheduleOptions() + .flatMap(BlobScheduleOptions::getCancun) + .orElse(BlobScheduleOptions.BlobSchedule.CANCUN_DEFAULT); + + final java.util.function.Supplier cancunGasCalcSupplier = + () -> new CancunGasCalculator(cancunBlobSchedule.getTarget()); + return shanghaiDefinition( chainId, enableRevertReason, @@ -693,12 +704,12 @@ static ProtocolSpecBuilder cancunDefinition( metricsSystem) .feeMarket(cancunFeeMarket) // gas calculator for EIP-4844 blob gas - .gasCalculator(CancunGasCalculator::new) + .gasCalculator(cancunGasCalcSupplier) // gas limit with EIP-4844 max blob gas per block .gasLimitCalculatorBuilder( feeMarket -> new CancunTargetingGasLimitCalculator( - londonForkBlockNumber, (BaseFeeMarket) feeMarket)) + londonForkBlockNumber, (BaseFeeMarket) feeMarket, cancunBlobSchedule.getMax())) // EVM changes to support EIP-1153: TSTORE and EIP-5656: MCOPY .evmBuilder( (gasCalculator, jdCacheConfig) -> @@ -739,7 +750,10 @@ static ProtocolSpecBuilder cancunDefinition( TransactionType.BLOB), evm.getMaxInitcodeSize())) .precompileContractRegistryBuilder(MainnetPrecompiledContractRegistries::cancun) - .blockHeaderValidatorBuilder(MainnetBlockHeaderValidator::cancunBlockHeaderValidator) + .blockHeaderValidatorBuilder( + fm -> + MainnetBlockHeaderValidator.blobAwareBlockHeaderValidator( + fm, cancunGasCalcSupplier)) .blockHashProcessor(new CancunBlockHashProcessor()) .name("Cancun"); } @@ -753,6 +767,12 @@ static ProtocolSpecBuilder cancunEOFDefinition( final boolean isParallelTxProcessingEnabled, final MetricsSystem metricsSystem) { + final var cancunBlobSchedule = + genesisConfigOptions + .getBlobScheduleOptions() + .flatMap(BlobScheduleOptions::getCancun) + .orElse(BlobScheduleOptions.BlobSchedule.CANCUN_DEFAULT); + ProtocolSpecBuilder protocolSpecBuilder = cancunDefinition( chainId, @@ -762,7 +782,14 @@ static ProtocolSpecBuilder cancunEOFDefinition( miningConfiguration, isParallelTxProcessingEnabled, metricsSystem); - return addEOF(chainId, evmConfiguration, protocolSpecBuilder).name("CancunEOF"); + return addEOF( + genesisConfigOptions, + chainId, + evmConfiguration, + protocolSpecBuilder, + cancunBlobSchedule.getTarget(), + cancunBlobSchedule.getMax()) + .name("CancunEOF"); } static ProtocolSpecBuilder pragueDefinition( @@ -777,6 +804,17 @@ static ProtocolSpecBuilder pragueDefinition( RequestContractAddresses requestContractAddresses = RequestContractAddresses.fromGenesis(genesisConfigOptions); + final long londonForkBlockNumber = genesisConfigOptions.getLondonBlockNumber().orElse(0L); + final var pragueBlobSchedule = + genesisConfigOptions + .getBlobScheduleOptions() + .flatMap(BlobScheduleOptions::getPrague) + .orElse(BlobScheduleOptions.BlobSchedule.PRAGUE_DEFAULT); + + // EIP-3074 AUTH and AUTHCALL gas | EIP-7840 Blob schedule | EIP-7691 6/9 blob increase + final java.util.function.Supplier pragueGasCalcSupplier = + () -> new PragueGasCalculator(pragueBlobSchedule.getTarget()); + return cancunDefinition( chainId, enableRevertReason, @@ -785,8 +823,12 @@ static ProtocolSpecBuilder pragueDefinition( miningConfiguration, isParallelTxProcessingEnabled, metricsSystem) - // EIP-3074 AUTH and AUTCALL gas - .gasCalculator(PragueGasCalculator::new) + .gasCalculator(pragueGasCalcSupplier) + // EIP-7840 Blob schedule | EIP-7691 6/9 blob increase + .gasLimitCalculatorBuilder( + feeMarket -> + new PragueTargetingGasLimitCalculator( + londonForkBlockNumber, (BaseFeeMarket) feeMarket, pragueBlobSchedule.getMax())) // EIP-3074 AUTH and AUTHCALL .evmBuilder( (gasCalculator, jdCacheConfig) -> @@ -818,6 +860,14 @@ static ProtocolSpecBuilder pragueDefinition( TransactionType.DELEGATE_CODE), evm.getMaxInitcodeSize())) + // TODO SLD EIP-7840 Can we dynamically wire in the appropriate GasCalculator instead of + // overriding + // blockHeaderValidatorBuilder every time the GasCalculator changes? + // EIP-7840 blob schedule | EIP-7691 6/9 blob increase + .blockHeaderValidatorBuilder( + fm -> + MainnetBlockHeaderValidator.blobAwareBlockHeaderValidator( + fm, pragueGasCalcSupplier)) // EIP-2935 Blockhash processor .blockHashProcessor(new PragueBlockHashProcessor()) .name("Prague"); @@ -832,6 +882,12 @@ static ProtocolSpecBuilder osakaDefinition( final boolean isParallelTxProcessingEnabled, final MetricsSystem metricsSystem) { + final var osakaBlobSchedule = + genesisConfigOptions + .getBlobScheduleOptions() + .flatMap(BlobScheduleOptions::getOsaka) + .orElse(BlobScheduleOptions.BlobSchedule.OSAKA_DEFAULT); + ProtocolSpecBuilder protocolSpecBuilder = pragueDefinition( chainId, @@ -841,16 +897,34 @@ static ProtocolSpecBuilder osakaDefinition( miningConfiguration, isParallelTxProcessingEnabled, metricsSystem); - return addEOF(chainId, evmConfiguration, protocolSpecBuilder).name("Osaka"); + 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 ProtocolSpecBuilder protocolSpecBuilder, + final int targetBlobsPerBlock, + final int 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(OsakaGasCalculator::new) + .gasCalculator(osakaGasCalcSupplier) + .gasLimitCalculatorBuilder( + feeMarket -> + new OsakaTargetingGasLimitCalculator( + londonForkBlockNumber, (BaseFeeMarket) feeMarket, maxBlobsPerBlock)) // EIP-7692 EOF v1 EVM and opcodes .evmBuilder( (gasCalculator, jdCacheConfig) -> @@ -863,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..903cce27dff --- /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 * 12 blobs = 131072 * 12 = 1572864 = 0x180000 + */ + public OsakaTargetingGasLimitCalculator( + final long londonForkBlock, final BaseFeeMarket feeMarket, final int maxBlobsPerBlock) { + super(londonForkBlock, feeMarket, maxBlobsPerBlock); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueTargetingGasLimitCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueTargetingGasLimitCalculator.java new file mode 100644 index 00000000000..b7574c3f3db --- /dev/null +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/PragueTargetingGasLimitCalculator.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 PragueTargetingGasLimitCalculator extends CancunTargetingGasLimitCalculator { + + /** The mainnet default maximum number of blobs per block for Prague */ + private static final int DEFAULT_MAX_BLOBS_PER_BLOCK_PRAGUE = 9; + + public PragueTargetingGasLimitCalculator( + final long londonForkBlock, final BaseFeeMarket feeMarket) { + super(londonForkBlock, feeMarket, DEFAULT_MAX_BLOBS_PER_BLOCK_PRAGUE); + } + + /** + * Using Prague mainnet default of 9 blobs for maxBlobsPerBlock: + * CancunGasCalculator.BLOB_GAS_PER_BLOB * 9 blobs = 131072 * 9 = 1179648 = 0x120000 + */ + public PragueTargetingGasLimitCalculator( + final long londonForkBlock, final BaseFeeMarket feeMarket, final int maxBlobsPerBlock) { + super(londonForkBlock, feeMarket, maxBlobsPerBlock); + } +} diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java index b483e4292ca..f6372097b7f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java @@ -36,8 +36,7 @@ public static BlobGas calculateExcessBlobGasForParent( .getGasCalculator() .computeExcessBlobGas( parentHeader.getExcessBlobGas().map(BlobGas::toLong).orElse(0L), - parentHeader.getBlobGasUsed().orElse(0L), - parentHeader.getTargetBlobsPerBlock()); + parentHeader.getBlobGasUsed().orElse(0L)); return BlobGas.of(headerExcess); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java index 2f24d618326..c986ec08588 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java @@ -44,8 +44,7 @@ public boolean validate(final BlockHeader header, final BlockHeader parent) { long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L); long calculatedExcessBlobGas = - gasCalculator.computeExcessBlobGas( - parentExcessBlobGas, parentBlobGasUsed, parent.getTargetBlobsPerBlock()); + gasCalculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed); if (headerExcessBlobGas != calculatedExcessBlobGas) { LOG.info( diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/CancunTargetingGasLimitCalculatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/CancunTargetingGasLimitCalculatorTest.java new file mode 100644 index 00000000000..c732aeafb75 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/CancunTargetingGasLimitCalculatorTest.java @@ -0,0 +1,33 @@ +/* + * Copyright contributors to 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 static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +class CancunTargetingGasLimitCalculatorTest { + + @Test + void currentBlobGasLimitIs6Blobs() { + var cancunTargetingGasLimitCalculator = + new CancunTargetingGasLimitCalculator(0L, FeeMarket.cancun(0L, Optional.empty())); + assertThat(cancunTargetingGasLimitCalculator.currentBlobGasLimit()).isEqualTo(0xC0000); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueTargetingGasLimitCalculatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueTargetingGasLimitCalculatorTest.java new file mode 100644 index 00000000000..e85b89bdfec --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/PragueTargetingGasLimitCalculatorTest.java @@ -0,0 +1,33 @@ +/* + * Copyright contributors to 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 static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket; + +import java.util.Optional; + +import org.junit.jupiter.api.Test; + +class PragueTargetingGasLimitCalculatorTest { + + @Test + void currentBlobGasLimitIs9BlobsByDefault() { + var pragueTargetingGasLimitCalculator = + new PragueTargetingGasLimitCalculator(0L, FeeMarket.cancun(0L, Optional.empty())); + assertThat(pragueTargetingGasLimitCalculator.currentBlobGasLimit()).isEqualTo(0x120000); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java index d14089e0b0f..c188f38d937 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java @@ -22,7 +22,6 @@ import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator; -import org.apache.tuweni.units.bigints.UInt64; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -88,16 +87,17 @@ public void validateHeader_BlobGasDifferentFromCalculated_FailsValidation() { } /** - * Prague EIP-7742 - Tests that the header blob gas matches the calculated blob gas and passes + * Prague EIP-7840 - Tests that the header blob gas matches the calculated blob gas and passes * validation. */ @Test - public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Target3() { + public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague() { + long target = pragueGasCalculator.getTargetBlobGasPerBlock(); + // Create parent header final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); parentBuilder.excessBlobGas(BlobGas.of(1L)); - parentBuilder.blobGasUsed(pragueGasCalculator.blobGasCost(3)); - parentBuilder.targetBlobsPerBlock(UInt64.valueOf(3)); + parentBuilder.blobGasUsed(target); final BlockHeader parentHeader = parentBuilder.buildHeader(); // Create block header with matching excessBlobGas @@ -109,23 +109,23 @@ public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Tar } /** - * Prague EIP-7742 - Tests that the header blob gas matches the calculated blob gas and passes - * validation. + * Prague EIP-7840 - Tests that the header blob gas is different from the calculated blob gas and + * fails validation. */ @Test - public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Target4() { + public void validateHeader_BlobGasDifferentFromCalculated_FailsValidation_Prague() { + long target = pragueGasCalculator.getTargetBlobGasPerBlock(); + // Create parent header final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); parentBuilder.excessBlobGas(BlobGas.of(1L)); - parentBuilder.blobGasUsed(pragueGasCalculator.blobGasCost(4)); - parentBuilder.targetBlobsPerBlock(UInt64.valueOf(4)); + parentBuilder.blobGasUsed(target); final BlockHeader parentHeader = parentBuilder.buildHeader(); - // Create block header with matching excessBlobGas + // Create block header with different excessBlobGas final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); - headerBuilder.excessBlobGas(BlobGas.of(1L)); final BlockHeader header = headerBuilder.buildHeader(); - assertThat(pragueBlobGasValidationRule.validate(header, parentHeader)).isTrue(); + assertThat(pragueBlobGasValidationRule.validate(header, parentHeader)).isFalse(); } } diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeers.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeers.java index 9816dc66d3f..e917c56a6c1 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeers.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeers.java @@ -23,6 +23,7 @@ import org.hyperledger.besu.ethereum.eth.sync.TrailingPeerRequirements; import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; +import java.util.List; import java.util.Optional; import java.util.concurrent.CompletableFuture; @@ -86,41 +87,24 @@ protected Optional fromBestPeer(final EthPeer peer) { } protected Optional selectBestPeer() { - return ethContext - .getEthPeers() - .bestPeerMatchingCriteria(this::canPeerDeterminePivotBlock) - // Only select a pivot block number when we have a minimum number of height estimates - .filter(unused -> enoughFastSyncPeersArePresent()); - } - - private boolean enoughFastSyncPeersArePresent() { - final long peerCount = countPeersThatCanDeterminePivotBlock(); + List peers = + ethContext + .getEthPeers() + .streamAvailablePeers() + .filter((peer) -> peer.chainState().hasEstimatedHeight() && peer.isFullyValidated()) + .toList(); + + // Only select a pivot block number when we have a minimum number of height estimates final int minPeerCount = syncConfig.getSyncMinimumPeerCount(); - if (peerCount < minPeerCount) { + if (peers.size() < minPeerCount) { LOG.info( "Waiting for valid peers with chain height information. {} / {} required peers currently available.", - peerCount, + peers.size(), minPeerCount); - return false; + return Optional.empty(); + } else { + return peers.stream().max(ethContext.getEthPeers().getBestPeerComparator()); } - return true; - } - - private long countPeersThatCanDeterminePivotBlock() { - return ethContext - .getEthPeers() - .streamAvailablePeers() - .filter(this::canPeerDeterminePivotBlock) - .count(); - } - - private boolean canPeerDeterminePivotBlock(final EthPeer peer) { - LOG.debug( - "peer {} hasEstimatedHeight {} isFullyValidated? {}", - peer.getLoggableId(), - peer.chainState().hasEstimatedHeight(), - peer.isFullyValidated()); - return peer.chainState().hasEstimatedHeight() && peer.isFullyValidated(); } private long conservativelyEstimatedPivotBlock() { diff --git a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java index 3acf49894a3..1adc5977bb6 100644 --- a/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java +++ b/ethereum/eth/src/main/java/org/hyperledger/besu/ethereum/eth/transactions/TransactionPool.java @@ -54,11 +54,11 @@ import java.io.IOException; import java.math.BigInteger; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; import java.util.ArrayList; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; -import java.util.IntSummaryStatistics; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; @@ -73,6 +73,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -645,15 +646,16 @@ public CompletableFuture setDisabled() { isPoolEnabled.set(false); subscribeConnectId.ifPresent(ethContext.getEthPeers()::unsubscribeConnect); pendingTransactionsListenersProxy.unsubscribe(); - final PendingTransactions pendingTransactionsToSave = pendingTransactions; + final CompletableFuture saveOperation = + saveRestoreManager + .saveToDisk(pendingTransactions) + .exceptionally( + t -> { + LOG.error("Error while saving transaction pool to disk", t); + return null; + }); pendingTransactions = new DisabledPendingTransactions(); - return saveRestoreManager - .saveToDisk(pendingTransactionsToSave) - .exceptionally( - t -> { - LOG.error("Error while saving transaction pool to disk", t); - return null; - }); + return saveOperation; } return CompletableFuture.completedFuture(null); } @@ -750,6 +752,7 @@ class SaveRestoreManager { private final AtomicBoolean isCancelled = new AtomicBoolean(false); CompletableFuture saveToDisk(final PendingTransactions pendingTransactionsToSave) { + cancelInProgressReadOperation(); return serializeAndDedupOperation( () -> executeSaveToDisk(pendingTransactionsToSave), writeInProgress); } @@ -758,20 +761,31 @@ CompletableFuture loadFromDisk() { return serializeAndDedupOperation(this::executeLoadFromDisk, readInProgress); } + private void cancelInProgressReadOperation() { + if (!readInProgress.get().isDone()) { + LOG.debug("Cancelling in progress read operation"); + isCancelled.set(true); + try { + waitUntilReadOperationIsCancelled(); + LOG.debug("In progress read operation cancelled"); + } catch (InterruptedException | ExecutionException e) { + LOG.warn("Error while cancelling in progress read operation", e); + throw new RuntimeException(e); + } + } + } + + private void waitUntilReadOperationIsCancelled() + throws InterruptedException, ExecutionException { + readInProgress.get().get(); + } + private CompletableFuture serializeAndDedupOperation( final Runnable operation, final AtomicReference> operationInProgress) { if (configuration.getEnableSaveRestore()) { try { if (diskAccessLock.tryAcquire(1, TimeUnit.MINUTES)) { - if (!operationInProgress.get().isDone()) { - isCancelled.set(true); - try { - operationInProgress.get().get(); - } catch (ExecutionException ee) { - // nothing to do - } - } isCancelled.set(false); operationInProgress.set( @@ -791,12 +805,17 @@ private CompletableFuture serializeAndDedupOperation( private void executeSaveToDisk(final PendingTransactions pendingTransactionsToSave) { final File saveFile = configuration.getSaveFile(); + final boolean appending = saveFile.exists(); try (final BufferedWriter bw = - new BufferedWriter(new FileWriter(saveFile, StandardCharsets.US_ASCII))) { + new BufferedWriter(new FileWriter(saveFile, StandardCharsets.US_ASCII, appending))) { final var allTxs = pendingTransactionsToSave.getPendingTransactions(); - LOG.info("Saving {} transactions to file {}", allTxs.size(), saveFile); + LOG.info( + "{} {} transactions to file {}", + appending ? "Appending" : "Saving", + allTxs.size(), + saveFile); - final long savedTxs = + final long processedTxCount = allTxs.parallelStream() .takeWhile(unused -> !isCancelled.get()) .map( @@ -819,13 +838,19 @@ private void executeSaveToDisk(final PendingTransactions pendingTransactionsToSa return 1; }) .sum(); + if (isCancelled.get()) { LOG.info( - "Saved {} transactions to file {}, before operation was cancelled", - savedTxs, + "{} {} transactions to file {}, before operation was cancelled", + appending ? "Appended" : "Saved", + processedTxCount, saveFile); } else { - LOG.info("Saved {} transactions to file {}", savedTxs, saveFile); + LOG.info( + "{} {} transactions to file {}", + appending ? "Appended" : "Saved", + processedTxCount, + saveFile); } } catch (IOException e) { LOG.error("Error while saving txpool content to disk", e); @@ -839,10 +864,10 @@ private void executeLoadFromDisk() { LOG.info("Loading transaction pool content from file {}", saveFile); try (final BufferedReader br = new BufferedReader(new FileReader(saveFile, StandardCharsets.US_ASCII))) { - final IntSummaryStatistics stats = + final Map stats = br.lines() .takeWhile(unused -> !isCancelled.get()) - .mapToInt( + .map( line -> { final boolean isLocal = line.charAt(0) == 'l'; final Transaction tx = @@ -850,30 +875,66 @@ private void executeLoadFromDisk() { final ValidationResult result = addTransaction(tx, isLocal); - - return result.isValid() ? 1 : 0; + return result.isValid() ? "OK" : result.getInvalidReason().name(); }) - .summaryStatistics(); + .collect(Collectors.groupingBy(Function.identity(), Collectors.counting())); + + br.close(); + + final var added = stats.getOrDefault("OK", 0L); + final var processedLines = stats.values().stream().mapToLong(Long::longValue).sum(); + + LOG.debug("Restored transactions stats {}", stats); if (isCancelled.get()) { LOG.info( "Added {} transactions of {} loaded from file {}, before operation was cancelled", - stats.getSum(), - stats.getCount(), + added, + processedLines, saveFile); + removeProcessedLines(saveFile, processedLines); } else { LOG.info( - "Added {} transactions of {} loaded from file {}", - stats.getSum(), - stats.getCount(), + "Added {} transactions of {} loaded from file {}, deleting file", + added, + processedLines, saveFile); + saveFile.delete(); } } catch (IOException e) { LOG.error("Error while saving txpool content to disk", e); } } - saveFile.delete(); } } + + private void removeProcessedLines(final File saveFile, final long processedLines) + throws IOException { + + LOG.debug("Removing processed lines from save file"); + + final var tmp = File.createTempFile(saveFile.getName(), ".tmp"); + + try (final BufferedReader reader = + Files.newBufferedReader(saveFile.toPath(), StandardCharsets.US_ASCII); + final BufferedWriter writer = + Files.newBufferedWriter(tmp.toPath(), StandardCharsets.US_ASCII)) { + reader + .lines() + .skip(processedLines) + .forEach( + line -> { + try { + writer.write(line); + writer.newLine(); + } catch (IOException e) { + throw new RuntimeException(e); + } + }); + } + + saveFile.delete(); + Files.move(tmp.toPath(), saveFile.toPath()); + } } } diff --git a/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeersTest.java b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeersTest.java new file mode 100644 index 00000000000..0d48d6ea26a --- /dev/null +++ b/ethereum/eth/src/test/java/org/hyperledger/besu/ethereum/eth/sync/fastsync/PivotSelectorFromPeersTest.java @@ -0,0 +1,91 @@ +/* + * Copyright contributors to 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.eth.sync.fastsync; + +import org.hyperledger.besu.ethereum.eth.manager.ChainState; +import org.hyperledger.besu.ethereum.eth.manager.EthContext; +import org.hyperledger.besu.ethereum.eth.manager.EthPeer; +import org.hyperledger.besu.ethereum.eth.manager.EthPeers; +import org.hyperledger.besu.ethereum.eth.sync.SynchronizerConfiguration; +import org.hyperledger.besu.ethereum.eth.sync.state.SyncState; + +import java.util.Optional; +import java.util.stream.Stream; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; +import org.mockito.quality.Strictness; + +@ExtendWith(MockitoExtension.class) +@MockitoSettings(strictness = Strictness.LENIENT) +public class PivotSelectorFromPeersTest { + private @Mock EthContext ethContext; + private @Mock EthPeers ethPeers; + private @Mock SyncState syncState; + + private PivotSelectorFromPeers selector; + + @BeforeEach + public void beforeTest() { + SynchronizerConfiguration syncConfig = + SynchronizerConfiguration.builder().syncMinimumPeerCount(2).syncPivotDistance(1).build(); + + selector = new PivotSelectorFromPeers(ethContext, syncConfig, syncState); + } + + @Test + public void testSelectNewPivotBlock() { + EthPeer peer1 = mockPeer(true, 10, true); + EthPeer peer2 = mockPeer(true, 8, true); + + Mockito.when(ethContext.getEthPeers()).thenReturn(ethPeers); + Mockito.when(ethPeers.streamAvailablePeers()).thenReturn(Stream.of(peer1, peer2)); + Mockito.when(ethPeers.getBestPeerComparator()) + .thenReturn((p1, ignored) -> p1 == peer1 ? 1 : -1); + + Optional result = selector.selectNewPivotBlock(); + + Assertions.assertTrue(result.isPresent()); + Assertions.assertEquals(9, result.get().getPivotBlockNumber().getAsLong()); + } + + @Test + public void testSelectNewPivotBlockWithInsufficientPeers() { + Mockito.when(ethContext.getEthPeers()).thenReturn(ethPeers); + Mockito.when(ethPeers.streamAvailablePeers()).thenReturn(Stream.empty()); + + Optional result = selector.selectNewPivotBlock(); + + Assertions.assertTrue(result.isEmpty()); + } + + private EthPeer mockPeer( + final boolean hasEstimatedHeight, final long chainHeight, final boolean isFullyValidated) { + EthPeer ethPeer = Mockito.mock(EthPeer.class); + ChainState chainState = Mockito.mock(ChainState.class); + Mockito.when(ethPeer.chainState()).thenReturn(chainState); + Mockito.when(chainState.hasEstimatedHeight()).thenReturn(hasEstimatedHeight); + Mockito.when(chainState.getEstimatedHeight()).thenReturn(chainHeight); + Mockito.when(ethPeer.isFullyValidated()).thenReturn(isFullyValidated); + + return ethPeer; + } +} diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java index 2d6168322ec..6a44bbc1d0e 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java @@ -26,18 +26,35 @@ */ public class CancunGasCalculator extends ShanghaiGasCalculator { + /** The default mainnet target blobs per block for Cancun */ + private static final int DEFAULT_TARGET_BLOBS_PER_BLOCK_CANCUN = 3; + + /** this.getBlobGasPerBlob() * 3 blobs = 131072 * 6 = 393216 = 0x60000 */ + private final long targetBlobGasPerBlock; + /** Instantiates a new Cancun Gas Calculator. */ public CancunGasCalculator() { - this(KZG_POINT_EVAL.toArrayUnsafe()[19]); + this(KZG_POINT_EVAL.toArrayUnsafe()[19], DEFAULT_TARGET_BLOBS_PER_BLOCK_CANCUN); + } + + /** + * Instantiates a new Cancun Gas Calculator + * + * @param targetBlobsPerBlock the target blobs per block + */ + public CancunGasCalculator(final int targetBlobsPerBlock) { + this(KZG_POINT_EVAL.toArrayUnsafe()[19], targetBlobsPerBlock); } /** * Instantiates a new Cancun Gas Calculator * * @param maxPrecompile the max precompile + * @param targetBlobsPerBlock the target blobs per block */ - protected CancunGasCalculator(final int maxPrecompile) { + protected CancunGasCalculator(final int maxPrecompile, final int targetBlobsPerBlock) { super(maxPrecompile); + this.targetBlobGasPerBlock = getBlobGasPerBlob() * targetBlobsPerBlock; } private static final long TLOAD_GAS = WARM_STORAGE_READ_COST; @@ -49,9 +66,6 @@ protected CancunGasCalculator(final int maxPrecompile) { */ private static final long BLOB_GAS_PER_BLOB = 1 << 17; - /** The target blob gas per block. */ - static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000; - // EIP-1153 @Override public long getTransientLoadOperationGasCost() { @@ -79,6 +93,15 @@ public long getBlobGasPerBlob() { * @return The target blob gas per block. */ public long getTargetBlobGasPerBlock() { - return TARGET_BLOB_GAS_PER_BLOCK; + return targetBlobGasPerBlock; + } + + @Override + public long computeExcessBlobGas(final long parentExcessBlobGas, final long parentBlobGasUsed) { + final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed; + if (currentExcessBlobGas < targetBlobGasPerBlock) { + return 0L; + } + return currentExcessBlobGas - targetBlobGasPerBlock; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index 9859d1dd7d7..8192bd1af88 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -40,12 +40,10 @@ import org.hyperledger.besu.evm.processor.AbstractMessageProcessor; import java.util.List; -import java.util.Optional; import java.util.function.Supplier; import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.units.bigints.UInt256; -import org.apache.tuweni.units.bigints.UInt64; /** * Provides various gas cost lookups and calculations used during block processing. @@ -636,30 +634,14 @@ default long blobGasCost(final long blobCount) { } /** - * Compute the new value for the excess blob gas, given the parent value, the parent blob gas used - * and the parent target blobs per block, if present. Used from Cancun onwards. Presence of - * parentTargetBlobsPerBlock implies EIP-7442/Prague enabled. Default to Cancun constant target - * gas value if parentTargetBlobsPerBlock is not present. + * Compute the new value for the excess blob gas, given the parent value and the blob gas used * * @param parentExcessBlobGas excess blob gas from the parent - * @param parentBlobGasUsed blob gas used from the parent - * @param parentTargetBlobsPerBlock the optional target blobs per block from the parent + * @param blobGasUsed blob gas used * @return the new excess blob gas value */ - default long computeExcessBlobGas( - final long parentExcessBlobGas, - final long parentBlobGasUsed, - final Optional parentTargetBlobsPerBlock) { - final long parentTargetBlobGas = - parentTargetBlobsPerBlock - .map(blobCount -> blobGasCost(blobCount.toLong())) - .orElse(CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK); - final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed; - - if (currentExcessBlobGas < parentTargetBlobGas) { - return 0L; - } - return currentExcessBlobGas - parentTargetBlobGas; + default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) { + return 0L; } /** diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/OsakaGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/OsakaGasCalculator.java index b4155dac52a..de56430f00f 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/OsakaGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/OsakaGasCalculator.java @@ -17,10 +17,9 @@ import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2; /** - * Gas Calculator for Prague + * Gas Calculator for Osaka * - *

Placeholder for new gas schedule items. If Prague finalzies without changes this can be - * removed + *

Placeholder for new gas schedule items. If Osaka finalzies without changes this can be removed * *

    *
  • TBD @@ -28,21 +27,34 @@ */ public class OsakaGasCalculator extends PragueGasCalculator { + /** The default mainnet target blobs per block for Osaka */ + private static final int DEFAULT_TARGET_BLOBS_PER_BLOCK_OSAKA = 9; + static final long MIN_RETAINED_GAS = 5_000; static final long MIN_CALLEE_GAS = 2300; - /** Instantiates a new Prague Gas Calculator. */ + /** Instantiates a new Osaka Gas Calculator. */ public OsakaGasCalculator() { - this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19]); + this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19], DEFAULT_TARGET_BLOBS_PER_BLOCK_OSAKA); + } + + /** + * Instantiates a new Osaka Gas Calculator + * + * @param targetBlobsPerBlock the target blobs per block + */ + public OsakaGasCalculator(final int targetBlobsPerBlock) { + this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19], targetBlobsPerBlock); } /** - * Instantiates a new Prague Gas Calculator + * Instantiates a new Osaka Gas Calculator * * @param maxPrecompile the max precompile + * @param targetBlobsPerBlock the target blobs per block */ - protected OsakaGasCalculator(final int maxPrecompile) { - super(maxPrecompile); + protected OsakaGasCalculator(final int maxPrecompile, final int targetBlobsPerBlock) { + super(maxPrecompile, targetBlobsPerBlock); } @Override diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java index 666292e9e10..268a58061c7 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java @@ -28,18 +28,34 @@ public class PragueGasCalculator extends CancunGasCalculator { final long existingAccountGasRefund; + /** + * The default mainnet target blobs per block for Prague getBlobGasPerBlob() * 6 blobs = 131072 * + * 6 = 786432 = 0xC0000 + */ + private static final int DEFAULT_TARGET_BLOBS_PER_BLOCK_PRAGUE = 6; + /** Instantiates a new Prague Gas Calculator. */ public PragueGasCalculator() { - this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19]); + this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19], DEFAULT_TARGET_BLOBS_PER_BLOCK_PRAGUE); + } + + /** + * Instantiates a new Prague Gas Calculator + * + * @param targetBlobsPerBlock the target blobs per block + */ + public PragueGasCalculator(final int targetBlobsPerBlock) { + this(BLS12_MAP_FP2_TO_G2.toArrayUnsafe()[19], targetBlobsPerBlock); } /** * Instantiates a new Prague Gas Calculator * * @param maxPrecompile the max precompile + * @param targetBlobsPerBlock the target blobs per block */ - protected PragueGasCalculator(final int maxPrecompile) { - super(maxPrecompile); + protected PragueGasCalculator(final int maxPrecompile, final int targetBlobsPerBlock) { + super(maxPrecompile, targetBlobsPerBlock); this.existingAccountGasRefund = newAccountGasCost() - CodeDelegation.PER_AUTH_BASE_COST; } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java index b7a11395a9e..02cb9fd1754 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java @@ -17,7 +17,6 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.List; -import java.util.Optional; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; @@ -28,6 +27,7 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) public class CancunGasCalculatorTest { + private static final long TARGET_BLOB_GAS_PER_BLOCK_CANCUN = 0x60000; private final CancunGasCalculator cancunGasCalculator = new CancunGasCalculator(); @ParameterizedTest(name = "{index} - parent gas {0}, used gas {1}, new excess {2}") @@ -35,13 +35,12 @@ public class CancunGasCalculatorTest { public void shouldCalculateExcessBlobGasCorrectly( final long parentExcess, final long used, final long expected) { final long usedBlobGas = cancunGasCalculator.blobGasCost(used); - assertThat( - cancunGasCalculator.computeExcessBlobGas(parentExcess, usedBlobGas, Optional.empty())) + assertThat(cancunGasCalculator.computeExcessBlobGas(parentExcess, usedBlobGas)) .isEqualTo(expected); } Iterable blobGasses() { - long targetGasPerBlock = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK; + long targetGasPerBlock = TARGET_BLOB_GAS_PER_BLOCK_CANCUN; return List.of( Arguments.of(0L, 0L, 0L), Arguments.of(targetGasPerBlock, 0L, 0L), diff --git a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/OsakaGasCalculatorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/OsakaGasCalculatorTest.java index 3236c13e833..28ead82f8aa 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/OsakaGasCalculatorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/OsakaGasCalculatorTest.java @@ -18,10 +18,20 @@ import org.hyperledger.besu.datatypes.Address; +import java.util.List; + import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class OsakaGasCalculatorTest { + private static final long TARGET_BLOB_GAS_PER_BLOCK_OSAKA = 0x120000; + private final OsakaGasCalculator osakaGasCalculator = new OsakaGasCalculator(); + @Test void testPrecompileSize() { OsakaGasCalculator subject = new OsakaGasCalculator(); @@ -32,10 +42,35 @@ void testPrecompileSize() { @Test void testNewConstants() { CancunGasCalculator cancunGas = new CancunGasCalculator(); - OsakaGasCalculator praugeGasCalculator = new OsakaGasCalculator(); + assertThat(osakaGasCalculator.getMinCalleeGas()).isGreaterThan(cancunGas.getMinCalleeGas()); + assertThat(osakaGasCalculator.getMinRetainedGas()).isGreaterThan(cancunGas.getMinRetainedGas()); + } + + @ParameterizedTest( + name = "{index} - parent gas {0}, used gas {1}, blob target {2} new excess {3}") + @MethodSource("blobGasses") + public void shouldCalculateExcessBlobGasCorrectly( + final long parentExcess, final long used, final long expected) { + final long usedBlobGas = osakaGasCalculator.blobGasCost(used); + assertThat(osakaGasCalculator.computeExcessBlobGas(parentExcess, usedBlobGas)) + .isEqualTo(expected); + } + + Iterable blobGasses() { + long nineBlobTargetGas = TARGET_BLOB_GAS_PER_BLOCK_OSAKA; + long newTargetCount = 9; - assertThat(praugeGasCalculator.getMinCalleeGas()).isGreaterThan(cancunGas.getMinCalleeGas()); - assertThat(praugeGasCalculator.getMinRetainedGas()) - .isGreaterThan(cancunGas.getMinRetainedGas()); + return List.of( + // New target count + Arguments.of(0L, 0L, 0L), + Arguments.of(nineBlobTargetGas, 0L, 0L), + Arguments.of(newTargetCount, 0L, 0L), + Arguments.of(0L, newTargetCount, 0L), + Arguments.of(1L, newTargetCount, 1L), + Arguments.of( + osakaGasCalculator.blobGasCost(newTargetCount), + 1L, + osakaGasCalculator.getBlobGasPerBlob()), + Arguments.of(nineBlobTargetGas, newTargetCount, nineBlobTargetGas)); } } diff --git a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java index 0b9d0f2372a..fdacad0e84e 100644 --- a/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java +++ b/evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java @@ -19,9 +19,7 @@ import org.hyperledger.besu.datatypes.Address; import java.util.List; -import java.util.Optional; -import org.apache.tuweni.units.bigints.UInt64; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestInstance; import org.junit.jupiter.params.ParameterizedTest; @@ -31,6 +29,7 @@ @TestInstance(TestInstance.Lifecycle.PER_CLASS) class PragueGasCalculatorTest { + private static final long TARGET_BLOB_GAS_PER_BLOCK_PRAGUE = 0xC0000; private final PragueGasCalculator pragueGasCalculator = new PragueGasCalculator(); @Test @@ -44,39 +43,27 @@ void testPrecompileSize() { name = "{index} - parent gas {0}, used gas {1}, blob target {2} new excess {3}") @MethodSource("blobGasses") public void shouldCalculateExcessBlobGasCorrectly( - final long parentExcess, final long used, final long target, final long expected) { + final long parentExcess, final long used, final long expected) { final long usedBlobGas = pragueGasCalculator.blobGasCost(used); - assertThat( - pragueGasCalculator.computeExcessBlobGas( - parentExcess, usedBlobGas, Optional.of(UInt64.valueOf(target)))) + assertThat(pragueGasCalculator.computeExcessBlobGas(parentExcess, usedBlobGas)) .isEqualTo(expected); } Iterable blobGasses() { - long threeBlobTargetGas = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK; - long cancunTargetCount = 3; - long newTargetCount = 4; + long sixBlobTargetGas = TARGET_BLOB_GAS_PER_BLOCK_PRAGUE; + long newTargetCount = 6; return List.of( - // If blob target count remains at 3 - Arguments.of(0L, 0L, cancunTargetCount, 0L), - Arguments.of(threeBlobTargetGas, 0L, cancunTargetCount, 0L), - Arguments.of(0L, cancunTargetCount, cancunTargetCount, 0L), - Arguments.of(1L, cancunTargetCount, cancunTargetCount, 1L), - Arguments.of( - threeBlobTargetGas, 1L, cancunTargetCount, pragueGasCalculator.getBlobGasPerBlob()), - Arguments.of(threeBlobTargetGas, 3L, cancunTargetCount, threeBlobTargetGas), // New target count - Arguments.of(0L, 0L, newTargetCount, 0L), - Arguments.of(threeBlobTargetGas, 0L, newTargetCount, 0L), - Arguments.of(newTargetCount, 0L, newTargetCount, 0L), - Arguments.of(0L, newTargetCount, newTargetCount, 0L), - Arguments.of(1L, newTargetCount, newTargetCount, 1L), + Arguments.of(0L, 0L, 0L), + Arguments.of(sixBlobTargetGas, 0L, 0L), + Arguments.of(newTargetCount, 0L, 0L), + Arguments.of(0L, newTargetCount, 0L), + Arguments.of(1L, newTargetCount, 1L), Arguments.of( pragueGasCalculator.blobGasCost(newTargetCount), 1L, - newTargetCount, pragueGasCalculator.getBlobGasPerBlob()), - Arguments.of(threeBlobTargetGas, newTargetCount, newTargetCount, threeBlobTargetGas)); + Arguments.of(sixBlobTargetGas, newTargetCount, sixBlobTargetGas)); } }