EIP-7840 Implementation (#8042)

Drive Cancun, Prague and Osaka target and max blobs per block from genesis config.
If blobSchedule is missing, use the mainnet default values.
Target is wired into appropriate *GasCalculator implementations.
Max is wired into appropriate *TargetingGasLimitCalculator implementations.

---------

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
Co-authored-by: Sally MacFarlane <macfarla.github@gmail.com>
This commit is contained in:
Simon Dudley
2024-12-20 10:14:35 +10:00
committed by GitHub
parent 86fe1badbd
commit e830db7248
29 changed files with 702 additions and 107 deletions

View File

@@ -17,6 +17,7 @@
### 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

View File

@@ -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

View File

@@ -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<BlobSchedule> getCancun() {
return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, CANCUN_KEY).map(BlobSchedule::new);
}
/**
* Gets prague blob schedule.
*
* @return the prague blob schedule
*/
public Optional<BlobSchedule> getPrague() {
return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, PRAGUE_KEY).map(BlobSchedule::new);
}
/**
* Gets osaka blob schedule.
*
* @return the osaka blob schedule
*/
public Optional<BlobSchedule> getOsaka() {
return JsonUtil.getObjectNode(blobScheduleOptionsConfigRoot, OSAKA_KEY).map(BlobSchedule::new);
}
/**
* As map.
*
* @return the map
*/
public Map<String, Object> asMap() {
final ImmutableMap.Builder<String, Object> 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<String, Object> asMap() {
return Map.of("target", target, "max", max);
}
}
}

View File

@@ -546,4 +546,11 @@ public interface GenesisConfigOptions {
* @return the consolidation request contract address
*/
Optional<Address> getConsolidationRequestContractAddress();
/**
* The blob schedule is a list of hardfork names and their associated target and max blob values.
*
* @return the blob schedule
*/
Optional<BlobScheduleOptions> getBlobScheduleOptions();
}

View File

@@ -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 class JsonGenesisConfigOptions implements GenesisConfigOptions {
.orElse(EthashConfigOptions.DEFAULT);
}
@Override
public Optional<BlobScheduleOptions> getBlobScheduleOptions() {
return JsonUtil.getObjectNode(configRoot, BLOB_SCHEDULE_CONFIG_KEY)
.map(BlobScheduleOptions::new);
}
@Override
public TransitionsConfigOptions getTransitions() {
return transitions;
@@ -537,6 +544,10 @@ public class JsonGenesisConfigOptions implements GenesisConfigOptions {
builder.put("fixedBaseFee", true);
}
if (getBlobScheduleOptions().isPresent()) {
builder.put("blobSchedule", getBlobScheduleOptions().get().asMap());
}
return builder.build();
}

View File

@@ -472,6 +472,11 @@ public class StubGenesisConfigOptions implements GenesisConfigOptions, Cloneable
return Optional.empty();
}
@Override
public Optional<BlobScheduleOptions> getBlobScheduleOptions() {
return Optional.empty();
}
/**
* Homestead block stub genesis config options.
*

View File

@@ -16,6 +16,16 @@
"terminalTotalDifficulty": 58750000000000000000000,
"shanghaiTime": 1681338455,
"cancunTime": 1710338135,
"blobSchedule": {
"cancun": {
"target": 3,
"max": 6
},
"prague": {
"target": 6,
"max": 9
}
},
"ethash": {
},
"discovery": {

View File

@@ -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);
}
}

View File

@@ -408,6 +408,46 @@ class GenesisConfigOptionsTest {
.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<String, Object> map = config.asMap();
assertThat(map).containsOnlyKeys("blobSchedule");
final Map<String, Object> blobSchedule = (Map<String, Object>) map.get("blobSchedule");
assertThat(blobSchedule).containsOnlyKeys("cancun", "prague", "osaka");
assertThat((Map<String, Object>) blobSchedule.get("cancun"))
.containsOnlyKeys("target", "max")
.containsValues(1, 2);
assertThat((Map<String, Object>) blobSchedule.get("prague"))
.containsOnlyKeys("target", "max")
.containsValues(3, 4);
assertThat((Map<String, Object>) blobSchedule.get("osaka"))
.containsOnlyKeys("target", "max")
.containsValues(4, 5);
}
private GenesisConfigOptions fromConfigOptions(final Map<String, Object> configOptions) {
final ObjectNode rootNode = JsonUtil.createEmptyObjectNode();
final ObjectNode options = JsonUtil.objectNodeFromMap(configOptions);

View File

@@ -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
}
}

View File

@@ -61,7 +61,8 @@ public class BlobSizeTransactionSelector extends AbstractTransactionSelector {
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;

View File

@@ -17,8 +17,11 @@ package org.hyperledger.besu.ethereum;
/** 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

View File

@@ -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;
}
}

View File

@@ -33,9 +33,10 @@ import org.hyperledger.besu.ethereum.mainnet.headervalidationrules.NoNonceRule;
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 final class MainnetBlockHeaderValidator {
.addRule(new IncrementalTimestampRule());
}
public static BlockHeaderValidator.Builder cancunBlockHeaderValidator(final FeeMarket feeMarket) {
public static BlockHeaderValidator.Builder blobAwareBlockHeaderValidator(
final FeeMarket feeMarket, final Supplier<GasCalculator> gasCalculator) {
return mergeBlockHeaderValidator(feeMarket)
.addRule(new BlobGasValidationRule(new CancunGasCalculator()));
.addRule(new BlobGasValidationRule(gasCalculator.get()));
}
}

View File

@@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.mainnet;
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.ByzantiumGasCalculator;
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 @@ public abstract class MainnetProtocolSpecs {
FeeMarket.cancun(londonForkBlockNumber, genesisConfigOptions.getBaseFeePerGas());
}
final var cancunBlobSchedule =
genesisConfigOptions
.getBlobScheduleOptions()
.flatMap(BlobScheduleOptions::getCancun)
.orElse(BlobScheduleOptions.BlobSchedule.CANCUN_DEFAULT);
final java.util.function.Supplier<GasCalculator> cancunGasCalcSupplier =
() -> new CancunGasCalculator(cancunBlobSchedule.getTarget());
return shanghaiDefinition(
chainId,
enableRevertReason,
@@ -693,12 +704,12 @@ public abstract class MainnetProtocolSpecs {
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 @@ public abstract class MainnetProtocolSpecs {
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 @@ public abstract class MainnetProtocolSpecs {
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 @@ public abstract class MainnetProtocolSpecs {
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 @@ public abstract class MainnetProtocolSpecs {
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<GasCalculator> pragueGasCalcSupplier =
() -> new PragueGasCalculator(pragueBlobSchedule.getTarget());
return cancunDefinition(
chainId,
enableRevertReason,
@@ -785,8 +823,12 @@ public abstract class MainnetProtocolSpecs {
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 @@ public abstract class MainnetProtocolSpecs {
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 @@ public abstract class MainnetProtocolSpecs {
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 @@ public abstract class MainnetProtocolSpecs {
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<BigInteger> 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<GasCalculator> 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 @@ public abstract class MainnetProtocolSpecs {
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(

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -36,8 +36,7 @@ public class ExcessBlobGasCalculator {
.getGasCalculator()
.computeExcessBlobGas(
parentHeader.getExcessBlobGas().map(BlobGas::toLong).orElse(0L),
parentHeader.getBlobGasUsed().orElse(0L),
parentHeader.getTargetBlobsPerBlock());
parentHeader.getBlobGasUsed().orElse(0L));
return BlobGas.of(headerExcess);
}
}

View File

@@ -44,8 +44,7 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule
long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L);
long calculatedExcessBlobGas =
gasCalculator.computeExcessBlobGas(
parentExcessBlobGas, parentBlobGasUsed, parent.getTargetBlobsPerBlock());
gasCalculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed);
if (headerExcessBlobGas != calculatedExcessBlobGas) {
LOG.info(

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -22,7 +22,6 @@ import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
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 class BlobGasValidationRuleTest {
}
/**
* 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 class BlobGasValidationRuleTest {
}
/**
* 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();
}
}

View File

@@ -26,18 +26,35 @@ import static org.hyperledger.besu.datatypes.Address.KZG_POINT_EVAL;
*/
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 @@ public class CancunGasCalculator extends ShanghaiGasCalculator {
*/
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 class CancunGasCalculator extends ShanghaiGasCalculator {
* @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;
}
}

View File

@@ -40,12 +40,10 @@ import org.hyperledger.besu.evm.precompile.SHA256PrecompiledContract;
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 @@ public interface GasCalculator {
}
/**
* 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<UInt64> 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;
}
/**

View File

@@ -17,10 +17,9 @@ package org.hyperledger.besu.evm.gascalculator;
import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2;
/**
* Gas Calculator for Prague
* Gas Calculator for Osaka
*
* <p>Placeholder for new gas schedule items. If Prague finalzies without changes this can be
* removed
* <p>Placeholder for new gas schedule items. If Osaka finalzies without changes this can be removed
*
* <UL>
* <LI>TBD
@@ -28,21 +27,34 @@ import static org.hyperledger.besu.datatypes.Address.BLS12_MAP_FP2_TO_G2;
*/
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 Prague Gas Calculator
* 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 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

View File

@@ -28,18 +28,34 @@ import org.hyperledger.besu.datatypes.CodeDelegation;
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;
}

View File

@@ -17,7 +17,6 @@ package org.hyperledger.besu.evm.gascalculator;
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 @@ import org.junit.jupiter.params.provider.MethodSource;
@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<Arguments> 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),

View File

@@ -18,10 +18,20 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address;
import org.junit.jupiter.api.Test;
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 @@ class OsakaGasCalculatorTest {
@Test
void testNewConstants() {
CancunGasCalculator cancunGas = new CancunGasCalculator();
OsakaGasCalculator praugeGasCalculator = new OsakaGasCalculator();
assertThat(osakaGasCalculator.getMinCalleeGas()).isGreaterThan(cancunGas.getMinCalleeGas());
assertThat(osakaGasCalculator.getMinRetainedGas()).isGreaterThan(cancunGas.getMinRetainedGas());
}
assertThat(praugeGasCalculator.getMinCalleeGas()).isGreaterThan(cancunGas.getMinCalleeGas());
assertThat(praugeGasCalculator.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<Arguments> blobGasses() {
long nineBlobTargetGas = TARGET_BLOB_GAS_PER_BLOCK_OSAKA;
long newTargetCount = 9;
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));
}
}

View File

@@ -19,9 +19,7 @@ import static org.assertj.core.api.Assertions.assertThat;
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 @@ import org.junit.jupiter.params.provider.MethodSource;
@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 @@ class PragueGasCalculatorTest {
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<Arguments> 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));
}
}