Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,11 @@ default long currentBlobGasLimit() {
*
* @param parentExcessBlobGas excess blob gas from the parent
* @param blobGasUsed blob gas used
* @param parentBaseFeePerGas base fee per gas from the parent
* @return the new excess blob gas value
*/
default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) {
default long computeExcessBlobGas(
final long parentExcessBlobGas, final long blobGasUsed, final long parentBaseFeePerGas) {
return 0L;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ public class CancunTargetingGasLimitCalculator extends LondonTargetingGasLimitCa
private final long targetBlobGasPerBlock;

private final long blobGasPerBlob;
protected final int maxBlobsPerBlock;
protected final int targetBlobsPerBlock;

public CancunTargetingGasLimitCalculator(
final long londonForkBlock,
Expand Down Expand Up @@ -57,15 +59,20 @@ public CancunTargetingGasLimitCalculator(
this.blobGasPerBlob = gasCalculator.getBlobGasPerBlob();
this.targetBlobGasPerBlock = blobGasPerBlob * targetBlobsPerBlock;
this.maxBlobGasPerBlock = blobGasPerBlob * maxBlobsPerBlock;
this.maxBlobsPerBlock = maxBlobsPerBlock;
this.targetBlobsPerBlock = targetBlobsPerBlock;
}

@Override
public long currentBlobGasLimit() {
return maxBlobGasPerBlock;
return getMaxBlobGasPerBlock();
}

@Override
public long computeExcessBlobGas(final long parentExcessBlobGas, final long parentBlobGasUsed) {
public long computeExcessBlobGas(
final long parentExcessBlobGas,
final long parentBlobGasUsed,
final long parentBaseFeePerGas) {
final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed;
if (currentExcessBlobGas < targetBlobGasPerBlock) {
return 0L;
Expand All @@ -85,4 +92,8 @@ public long getBlobGasPerBlob() {
public long getTargetBlobGasPerBlock() {
return targetBlobGasPerBlock;
}

public long getMaxBlobGasPerBlock() {
return maxBlobGasPerBlock;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ public class LondonTargetingGasLimitCalculator extends FrontierTargetingGasLimit
private static final Logger LOG =
LoggerFactory.getLogger(LondonTargetingGasLimitCalculator.class);
private final long londonForkBlock;
private final BaseFeeMarket feeMarket;
protected final BaseFeeMarket feeMarket;

public LondonTargetingGasLimitCalculator(
final long londonForkBlock, final BaseFeeMarket feeMarket) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,15 @@
*/
package org.hyperledger.besu.ethereum.mainnet;

import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.mainnet.feemarket.BaseFeeMarket;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;

public class OsakaTargetingGasLimitCalculator extends CancunTargetingGasLimitCalculator {
/** The blob base cost constant for Osaka */
private static final long BLOB_BASE_COST = 1 << 14; // 2^14

/** The mainnet transaction gas limit cap for Osaka */
private static final long DEFAULT_TRANSACTION_GAS_LIMIT_CAP_OSAKA = 30_000_000L;

Expand Down Expand Up @@ -53,4 +58,28 @@ public OsakaTargetingGasLimitCalculator(
public long transactionGasLimitCap() {
return transactionGasLimitCap;
}

@Override
public long computeExcessBlobGas(
final long parentExcessBlobGas,
final long parentBlobGasUsed,
final long parentBaseFeePerGas) {
final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed;

// First check if we're below the target
if (currentExcessBlobGas < getTargetBlobGasPerBlock()) {
return 0L;
}

// EIP-7918 https://eips.ethereum.org/EIPS/eip-7918
Wei baseFeeBlobGas = feeMarket.blobGasPricePerGas(BlobGas.of(parentExcessBlobGas));
long baseFeeBlobGasLong = baseFeeBlobGas.toLong();
if (BLOB_BASE_COST * parentBaseFeePerGas > getBlobGasPerBlob() * baseFeeBlobGasLong) {
return parentExcessBlobGas
+ parentBlobGasUsed * (maxBlobsPerBlock - targetBlobsPerBlock) / maxBlobsPerBlock;
} else {
// same as Cancun
return currentExcessBlobGas - getTargetBlobGasPerBlock();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet.feemarket;

import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;

Expand All @@ -35,7 +36,8 @@ public static BlobGas calculateExcessBlobGasForParent(
.getGasLimitCalculator()
.computeExcessBlobGas(
parentHeader.getExcessBlobGas().map(BlobGas::toLong).orElse(0L),
parentHeader.getBlobGasUsed().orElse(0L));
parentHeader.getBlobGasUsed().orElse(0L),
parentHeader.getBaseFee().orElse(Wei.ZERO).toLong());
return BlobGas.of(headerExcess);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet.headervalidationrules;

import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.GasLimitCalculator;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.DetachedBlockHeaderValidationRule;
Expand Down Expand Up @@ -48,7 +49,8 @@ public boolean validate(final BlockHeader header, final BlockHeader parent) {
long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L);

long calculatedExcessBlobGas =
gasLimitCalculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed);
gasLimitCalculator.computeExcessBlobGas(
parentExcessBlobGas, parentBlobGasUsed, parent.getBaseFee().orElse(Wei.ZERO).toLong());

if (headerExcessBlobGas != calculatedExcessBlobGas) {
LOG.info(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ class CancunTargetingGasLimitCalculatorTest {
public void shouldCalculateCancunExcessBlobGasCorrectly(
final long parentExcess, final long used, final long expected) {
final long usedBlobGas = new CancunGasCalculator().blobGasCost(used);
assertThat(cancunTargetingGasLimitCalculator.computeExcessBlobGas(parentExcess, usedBlobGas))
assertThat(
cancunTargetingGasLimitCalculator.computeExcessBlobGas(parentExcess, usedBlobGas, 0L))
.isEqualTo(expected);
}

Expand Down Expand Up @@ -126,7 +127,7 @@ void shouldUseFutureForkCalculatorBlobGasPerBlob() {
public void shouldCalculatePragueExcessBlobGasCorrectly(
final long parentExcess, final long used, final long expected) {
final long usedBlobGas = pragueGasCalculator.blobGasCost(used);
assertThat(pragueGasLimitCalculator.computeExcessBlobGas(parentExcess, usedBlobGas))
assertThat(pragueGasLimitCalculator.computeExcessBlobGas(parentExcess, usedBlobGas, 0L))
.isEqualTo(expected);
}

Expand Down Expand Up @@ -172,8 +173,8 @@ void shouldCalculateCorrectlyPragueBlobGasPerBlob() {
int newTargetCount = 9;
public static final OsakaGasCalculator osakaGasCalculator = new OsakaGasCalculator();
// CancunTargetingGasLimitCalculator with Osaka numbers
private final CancunTargetingGasLimitCalculator osakaGasLimitCalculator =
new CancunTargetingGasLimitCalculator(
private final OsakaTargetingGasLimitCalculator osakaGasLimitCalculator =
new OsakaTargetingGasLimitCalculator(
0L,
FeeMarket.cancunDefault(0L, Optional.empty()),
osakaGasCalculator,
Expand All @@ -187,7 +188,7 @@ void shouldCalculateCorrectlyPragueBlobGasPerBlob() {
public void shouldCalculateOsakaExcessBlobGasCorrectly(
final long parentExcess, final long used, final long expected) {
final long usedBlobGas = osakaGasCalculator.blobGasCost(used);
assertThat(osakaGasLimitCalculator.computeExcessBlobGas(parentExcess, usedBlobGas))
assertThat(osakaGasLimitCalculator.computeExcessBlobGas(parentExcess, usedBlobGas, 0L))
.isEqualTo(expected);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* 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.BaseFeeMarket;
import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
import org.hyperledger.besu.evm.gascalculator.OsakaGasCalculator;

import java.util.List;
import java.util.Optional;

import org.assertj.core.api.Assertions;
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 OsakaTargetingGasLimitCalculatorTest {
private static final long TARGET_BLOB_GAS_PER_BLOCK_OSAKA = 0x120000;

private final OsakaGasCalculator osakaGasCalculator = new OsakaGasCalculator();
private final BaseFeeMarket feeMarket = FeeMarket.cancunDefault(0L, Optional.empty());

@ParameterizedTest(
name = "{index} - parent gas {0}, used gas {1}, parent base fee per gas {2}, new excess {3}")
@MethodSource("osakaExcessBlobGasTestCases")
public void shouldCalculateOsakaExcessBlobGasCorrectly(
final long parentExcess,
final long used,
final long parentBaseFeePerGas,
final long expected) {
int maxBlobs = 10;
int targetBlobs = 9;
final OsakaTargetingGasLimitCalculator osakaTargetingGasLimitCalculator =
new OsakaTargetingGasLimitCalculator(
0L, feeMarket, osakaGasCalculator, maxBlobs, targetBlobs);

final long usedBlobGas = osakaGasCalculator.blobGasCost(used);
assertThat(
osakaTargetingGasLimitCalculator.computeExcessBlobGas(
parentExcess, usedBlobGas, parentBaseFeePerGas))
.isEqualTo(expected);
}

Iterable<Arguments> osakaExcessBlobGasTestCases() {
long targetGasPerBlock = TARGET_BLOB_GAS_PER_BLOCK_OSAKA;
return List.of(
// Case 1: Below target, should return 0
Arguments.of(0L, 0L, 0L, 0L),
Arguments.of(targetGasPerBlock - 1, 0L, 0L, 0L),
Arguments.of(0L, 8, 0L, 0L), // 8 blobs is below target (9)

// Case 2: Above target, BLOB_BASE_COST * baseFee <= GAS_PER_BLOB * blobFee
// This should use the formula: parentExcess + parentBlobGasUsed - target
Arguments.of(targetGasPerBlock * 2, 10, 17L, 2490368L),

// Case 3: Above target, BLOB_BASE_COST * baseFee > GAS_PER_BLOB * blobFee
// This should use the formula: parentExcess + parentBlobGasUsed * (max - target) / max
Arguments.of(targetGasPerBlock, 1, 0L, osakaGasCalculator.getBlobGasPerBlob()),
Arguments.of(targetGasPerBlock, 10, 1L, 1310720L),
Arguments.of(targetGasPerBlock, 10, 16L, 1310720L));
}

@Test
void shouldUseCorrectBlobGasPerBlob() {
// should use OsakaGasCalculator's blob gas per blob to calculate the gas limit
final long blobGasPerBlob = osakaGasCalculator.getBlobGasPerBlob();
assertThat(blobGasPerBlob).isEqualTo(131072); // same as Cancun

int maxBlobs = 10;
int targetBlobs = 9;
var osakaTargetingGasLimitCalculator =
new OsakaTargetingGasLimitCalculator(
0L, feeMarket, osakaGasCalculator, maxBlobs, targetBlobs);

// if maxBlobs = 10, then the gas limit would be 131072 * 10 = 1310720
assertThat(osakaTargetingGasLimitCalculator.currentBlobGasLimit())
.isEqualTo(blobGasPerBlob * maxBlobs);
assertThat(osakaTargetingGasLimitCalculator.currentBlobGasLimit()).isEqualTo(1310720);
assertThat(osakaTargetingGasLimitCalculator.getTargetBlobGasPerBlock())
.isEqualTo(blobGasPerBlob * targetBlobs);
}

@Test
void testComputeExcessBlobGasWithDifferentConditions() {
// Create a test instance with specific parameters
int maxBlobs = 10;
int targetBlobs = 9;
var calculator =
new OsakaTargetingGasLimitCalculator(
0L, feeMarket, osakaGasCalculator, maxBlobs, targetBlobs);
assertThat(calculator.maxBlobsPerBlock).isEqualTo(maxBlobs);
assertThat(calculator.targetBlobsPerBlock).isEqualTo(targetBlobs);

long parentExcessBlobGas = calculator.getTargetBlobGasPerBlock();
long parentBlobGasUsed = osakaGasCalculator.getBlobGasPerBlob() * 2;

// Test the condition where BLOB_BASE_COST * baseFee > GAS_PER_BLOB * blobFee
// This should use the first formula
long result1 = calculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed, 0L);
// Expected value based on the test case
assertThat(result1).isEqualTo(262144L);

// Test with very high excess blob gas to trigger the second formula
// When excess is high, blob fee will be high, potentially making BLOB_BASE_COST * baseFee <=
// GAS_PER_BLOB * blobFee
long highExcess = calculator.getTargetBlobGasPerBlock() * 10;
long result2 = calculator.computeExcessBlobGas(highExcess, parentBlobGasUsed, 0L);
// Expected value based on the test case
assertThat(result2).isEqualTo(10878976L);
}

@Test
void dryRunDetector() {
Assertions.assertThat(true)
.withFailMessage("This test is here so gradle --dry-run executes this class")
.isTrue();
}
}
Loading