EIP-7742 - gas calculation using target_block_count

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
pull/7823/head
Simon Dudley 3 weeks ago
parent f82bb7d6b7
commit 7eef3b0e0f
  1. 4
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java
  2. 12
      ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java
  3. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java
  4. 3
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/feemarket/ExcessBlobGasCalculator.java
  5. 9
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRule.java
  6. 4
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java
  7. 74
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/headervalidationrules/BlobGasValidationRuleTest.java
  8. 46
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculator.java
  9. 44
      evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java
  10. 14
      evm/src/test/java/org/hyperledger/besu/evm/gascalculator/CancunGasCalculatorTest.java
  11. 52
      evm/src/test/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculatorTest.java

@ -16,10 +16,7 @@ package org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -124,7 +121,6 @@ public class TransactionTracerTest {
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader); when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator); when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator);
when(protocolContext.getBadBlockManager()).thenReturn(badBlockManager); when(protocolContext.getBadBlockManager()).thenReturn(badBlockManager);
lenient().when(gasCalculator.computeExcessBlobGas(anyLong(), anyInt())).thenReturn(0L);
} }
@Test @Test

@ -17,7 +17,7 @@ package org.hyperledger.besu.ethereum.blockcreation.txselection.selectors;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.fail; import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Answers.RETURNS_DEEP_STUBS; import static org.mockito.Answers.RETURNS_DEEP_STUBS;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -61,7 +61,7 @@ class BlobSizeTransactionSelectorTest {
Suppliers.memoize(SignatureAlgorithmFactory::getInstance); Suppliers.memoize(SignatureAlgorithmFactory::getInstance);
private static final KeyPair KEYS = SIGNATURE_ALGORITHM.get().generateKeyPair(); private static final KeyPair KEYS = SIGNATURE_ALGORITHM.get().generateKeyPair();
private static final long BLOB_GAS_PER_BLOB = CancunGasCalculator.BLOB_GAS_PER_BLOB; private static final long BLOB_GAS_PER_BLOB = new CancunGasCalculator().getBlobGasPerBlob();
private static final int MAX_BLOBS = 6; private static final int MAX_BLOBS = 6;
private static final long MAX_BLOB_GAS = BLOB_GAS_PER_BLOB * MAX_BLOBS; private static final long MAX_BLOB_GAS = BLOB_GAS_PER_BLOB * MAX_BLOBS;
@ -89,8 +89,8 @@ class BlobSizeTransactionSelectorTest {
@Test @Test
void firstBlobTransactionIsSelected() { void firstBlobTransactionIsSelected() {
when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS); when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS);
when(blockSelectionContext.gasCalculator().blobGasCost(anyInt())) when(blockSelectionContext.gasCalculator().blobGasCost(anyLong()))
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Integer.class)); .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class));
final var selector = new BlobSizeTransactionSelector(blockSelectionContext); final var selector = new BlobSizeTransactionSelector(blockSelectionContext);
@ -131,8 +131,8 @@ class BlobSizeTransactionSelectorTest {
@Test @Test
void returnsTooLargeForRemainingBlobGas() { void returnsTooLargeForRemainingBlobGas() {
when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS); when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS);
when(blockSelectionContext.gasCalculator().blobGasCost(anyInt())) when(blockSelectionContext.gasCalculator().blobGasCost(anyLong()))
.thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Integer.class)); .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class));
final var selector = new BlobSizeTransactionSelector(blockSelectionContext); final var selector = new BlobSizeTransactionSelector(blockSelectionContext);

@ -37,7 +37,6 @@ import org.hyperledger.besu.ethereum.trie.MerkleTrieException;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldState;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator; import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.worldview.BonsaiWorldStateUpdateAccumulator;
import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup; import org.hyperledger.besu.ethereum.vm.CachingBlockHashLookup;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.operation.BlockHashOperation; import org.hyperledger.besu.evm.operation.BlockHashOperation;
import org.hyperledger.besu.evm.tracing.OperationTracer; import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.evm.worldstate.WorldState; import org.hyperledger.besu.evm.worldstate.WorldState;
@ -174,7 +173,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor {
currentGasUsed += transaction.getGasLimit() - transactionProcessingResult.getGasRemaining(); currentGasUsed += transaction.getGasLimit() - transactionProcessingResult.getGasRemaining();
if (transaction.getVersionedHashes().isPresent()) { if (transaction.getVersionedHashes().isPresent()) {
currentBlobGasUsed += currentBlobGasUsed +=
(transaction.getVersionedHashes().get().size() * CancunGasCalculator.BLOB_GAS_PER_BLOB); (transaction.getVersionedHashes().get().size()
* protocolSpec.getGasCalculator().getBlobGasPerBlob());
} }
final TransactionReceipt transactionReceipt = final TransactionReceipt transactionReceipt =

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

@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.mainnet.headervalidationrules;
import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.mainnet.DetachedBlockHeaderValidationRule; import org.hyperledger.besu.ethereum.mainnet.DetachedBlockHeaderValidationRule;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.slf4j.Logger; import org.slf4j.Logger;
@ -45,7 +44,8 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule
long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L); long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L);
long calculatedExcessBlobGas = long calculatedExcessBlobGas =
gasCalculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed); gasCalculator.computeExcessBlobGas(
parentExcessBlobGas, parentBlobGasUsed, parent.getTargetBlobCount());
if (headerExcessBlobGas != calculatedExcessBlobGas) { if (headerExcessBlobGas != calculatedExcessBlobGas) {
LOG.info( LOG.info(
@ -55,10 +55,9 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule
return false; return false;
} }
long headerBlobGasUsed = header.getBlobGasUsed().orElse(0L); long headerBlobGasUsed = header.getBlobGasUsed().orElse(0L);
if (headerBlobGasUsed % CancunGasCalculator.BLOB_GAS_PER_BLOB != 0) { if (headerBlobGasUsed % gasCalculator.getBlobGasPerBlob() != 0) {
LOG.info( LOG.info(
"blob gas used must be multiple of GAS_PER_BLOB ({})", "blob gas used must be multiple of GAS_PER_BLOB ({})", gasCalculator.getBlobGasPerBlob());
CancunGasCalculator.BLOB_GAS_PER_BLOB);
return false; return false;
} }
return true; return true;

@ -24,7 +24,7 @@ import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason
import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE; import static org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason.UPFRONT_COST_EXCEEDS_BALANCE;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -589,7 +589,7 @@ public class MainnetTransactionValidatorTest {
@Test @Test
public void shouldAcceptTransactionWithAtLeastOneBlob() { public void shouldAcceptTransactionWithAtLeastOneBlob() {
when(gasCalculator.blobGasCost(anyInt())).thenReturn(2L); when(gasCalculator.blobGasCost(anyLong())).thenReturn(2L);
final TransactionValidator validator = final TransactionValidator validator =
createTransactionValidator( createTransactionValidator(
gasCalculator, gasCalculator,

@ -20,26 +20,37 @@ import org.hyperledger.besu.datatypes.BlobGas;
import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture; import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; 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.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
/** Tests for the {@link BlobGasValidationRule} class. */ /** Tests for the {@link BlobGasValidationRule} class. */
public class BlobGasValidationRuleTest { public class BlobGasValidationRuleTest {
private CancunGasCalculator gasCalculator; private CancunGasCalculator cancunGasCalculator;
private BlobGasValidationRule blobGasValidationRule; private BlobGasValidationRule cancunBlobGasValidationRule;
private PragueGasCalculator pragueGasCalculator;
private BlobGasValidationRule pragueBlobGasValidationRule;
@BeforeEach @BeforeEach
public void setUp() { public void setUp() {
gasCalculator = new CancunGasCalculator(); cancunGasCalculator = new CancunGasCalculator();
blobGasValidationRule = new BlobGasValidationRule(gasCalculator); cancunBlobGasValidationRule = new BlobGasValidationRule(cancunGasCalculator);
pragueGasCalculator = new PragueGasCalculator();
pragueBlobGasValidationRule = new BlobGasValidationRule(pragueGasCalculator);
} }
/** Tests that the header blob gas matches the calculated blob gas and passes validation. */ /**
* Cancun EIP-4844 - Tests that the header blob gas matches the calculated blob gas and passes
* validation.
*/
@Test @Test
public void validateHeader_BlobGasMatchesCalculated_SuccessValidation() { public void validateHeader_BlobGasMatchesCalculated_SuccessValidation() {
long target = gasCalculator.getTargetBlobGasPerBlock(); long target = cancunGasCalculator.getTargetBlobGasPerBlock();
// Create parent header // Create parent header
final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture();
@ -52,15 +63,16 @@ public class BlobGasValidationRuleTest {
headerBuilder.excessBlobGas(BlobGas.of(1L)); headerBuilder.excessBlobGas(BlobGas.of(1L));
final BlockHeader header = headerBuilder.buildHeader(); final BlockHeader header = headerBuilder.buildHeader();
assertThat(blobGasValidationRule.validate(header, parentHeader)).isTrue(); assertThat(cancunBlobGasValidationRule.validate(header, parentHeader)).isTrue();
} }
/** /**
* Tests that the header blob gas is different from the calculated blob gas and fails validation. * Cancun EIP-4844 - Tests that the header blob gas is different from the calculated blob gas and
* fails validation.
*/ */
@Test @Test
public void validateHeader_BlobGasDifferentFromCalculated_FailsValidation() { public void validateHeader_BlobGasDifferentFromCalculated_FailsValidation() {
long target = gasCalculator.getTargetBlobGasPerBlock(); long target = cancunGasCalculator.getTargetBlobGasPerBlock();
// Create parent header // Create parent header
final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture();
@ -72,6 +84,48 @@ public class BlobGasValidationRuleTest {
final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture();
final BlockHeader header = headerBuilder.buildHeader(); final BlockHeader header = headerBuilder.buildHeader();
assertThat(blobGasValidationRule.validate(header, parentHeader)).isFalse(); assertThat(cancunBlobGasValidationRule.validate(header, parentHeader)).isFalse();
}
/**
* Prague EIP-7742 - Tests that the header blob gas matches the calculated blob gas and passes
* validation.
*/
@Test
public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Target3() {
// Create parent header
final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture();
parentBuilder.excessBlobGas(BlobGas.of(1L));
parentBuilder.blobGasUsed(pragueGasCalculator.blobGasCost(3));
parentBuilder.targetBlobCount(UInt64.valueOf(3));
final BlockHeader parentHeader = parentBuilder.buildHeader();
// Create block header with matching excessBlobGas
final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture();
headerBuilder.excessBlobGas(BlobGas.of(1L));
final BlockHeader header = headerBuilder.buildHeader();
assertThat(pragueBlobGasValidationRule.validate(header, parentHeader)).isTrue();
}
/**
* Prague EIP-7742 - Tests that the header blob gas matches the calculated blob gas and passes
* validation.
*/
@Test
public void validateHeader_BlobGasMatchesCalculated_SuccessValidation_Prague_Target4() {
// Create parent header
final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture();
parentBuilder.excessBlobGas(BlobGas.of(1L));
parentBuilder.blobGasUsed(pragueGasCalculator.blobGasCost(4));
parentBuilder.targetBlobCount(UInt64.valueOf(4));
final BlockHeader parentHeader = parentBuilder.buildHeader();
// Create block header with matching excessBlobGas
final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture();
headerBuilder.excessBlobGas(BlobGas.of(1L));
final BlockHeader header = headerBuilder.buildHeader();
assertThat(pragueBlobGasValidationRule.validate(header, parentHeader)).isTrue();
} }
} }

@ -44,13 +44,13 @@ public class CancunGasCalculator extends ShanghaiGasCalculator {
private static final long TSTORE_GAS = WARM_STORAGE_READ_COST; private static final long TSTORE_GAS = WARM_STORAGE_READ_COST;
/** /**
* The blob gas cost per blob. This is the gas cost for each blob of blob that is added to the * The blob gas cost per blob. This is the gas cost for each blob of data that is added to the
* block. * block.
*/ */
public static final long BLOB_GAS_PER_BLOB = 1 << 17; private static final long BLOB_GAS_PER_BLOB = 1 << 17;
/** The target blob gas per block. */ /** The target blob gas per block. */
public static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000; static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000;
// EIP-1153 // EIP-1153
@Override @Override
@ -64,8 +64,13 @@ public class CancunGasCalculator extends ShanghaiGasCalculator {
} }
@Override @Override
public long blobGasCost(final int blobCount) { public long blobGasCost(final long blobCount) {
return BLOB_GAS_PER_BLOB * blobCount; return getBlobGasPerBlob() * blobCount;
}
@Override
public long getBlobGasPerBlob() {
return BLOB_GAS_PER_BLOB;
} }
/** /**
@ -76,35 +81,4 @@ public class CancunGasCalculator extends ShanghaiGasCalculator {
public long getTargetBlobGasPerBlock() { public long getTargetBlobGasPerBlock() {
return TARGET_BLOB_GAS_PER_BLOCK; return TARGET_BLOB_GAS_PER_BLOCK;
} }
/**
* Computes the excess blob gas for a given block based on the parent's excess blob gas and blob
* gas used. If the sum of parent's excess blob gas and parent's blob gas used is less than the
* target blob gas per block, the excess blob gas is calculated as 0. Otherwise, it is computed as
* the difference between the sum and the target blob gas per block.
*
* @param parentExcessBlobGas The excess blob gas of the parent block.
* @param newBlobs blob gas incurred by current block
* @return The excess blob gas for the current block.
*/
@Override
public long computeExcessBlobGas(final long parentExcessBlobGas, final int newBlobs) {
final long consumedBlobGas = blobGasCost(newBlobs);
final long currentExcessBlobGas = parentExcessBlobGas + consumedBlobGas;
if (currentExcessBlobGas < TARGET_BLOB_GAS_PER_BLOCK) {
return 0L;
}
return currentExcessBlobGas - TARGET_BLOB_GAS_PER_BLOCK;
}
@Override
public long computeExcessBlobGas(final long parentExcessBlobGas, final long parentBlobGasUsed) {
final long currentExcessBlobGas = parentExcessBlobGas + parentBlobGasUsed;
if (currentExcessBlobGas < TARGET_BLOB_GAS_PER_BLOCK) {
return 0L;
}
return currentExcessBlobGas - TARGET_BLOB_GAS_PER_BLOCK;
}
} }

@ -40,10 +40,12 @@ import org.hyperledger.besu.evm.precompile.SHA256PrecompiledContract;
import org.hyperledger.besu.evm.processor.AbstractMessageProcessor; import org.hyperledger.besu.evm.processor.AbstractMessageProcessor;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.Supplier; import java.util.function.Supplier;
import org.apache.tuweni.bytes.Bytes; import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256; 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. * Provides various gas cost lookups and calculations used during block processing.
@ -614,36 +616,50 @@ public interface GasCalculator {
} }
/** /**
* Return the gas cost given the number of blobs * Returns the blob gas cost per blob. This is the gas cost for each blob of data that is added to
* the block.
* *
* @param blobCount the number of blobs * @return the blob gas cost per blob
* @return the total gas cost
*/ */
default long blobGasCost(final int blobCount) { default long getBlobGasPerBlob() {
return 0L; return 0L;
} }
/** /**
* Compute the new value for the excess blob gas, given the parent value and the count of new * Return the gas cost given the number of blobs
* blobs
* *
* @param parentExcessBlobGas excess blob gas from the parent * @param blobCount the number of blobs
* @param newBlobs count of new blobs * @return the total gas cost
* @return the new excess blob gas value
*/ */
default long computeExcessBlobGas(final long parentExcessBlobGas, final int newBlobs) { default long blobGasCost(final long blobCount) {
return 0L; return 0L;
} }
/** /**
* Compute the new value for the excess blob gas, given the parent value and the blob gas used * Compute the new value for the excess blob gas, given the parent value, the parent blob gas used
* and the parent target blob count, if present. Used from Cancun onwards. Presence of
* parentTargetBlobCount implies EIP-7442/Prague enabled. Default to Cancun constant target gas
* value if targetBlobCount is not present.
* *
* @param parentExcessBlobGas excess blob gas from the parent * @param parentExcessBlobGas excess blob gas from the parent
* @param blobGasUsed blob gas used * @param parentBlobGasUsed blob gas used from the parent
* @param parentTargetBlobCount the optional target blob count from the parent
* @return the new excess blob gas value * @return the new excess blob gas value
*/ */
default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) { default long computeExcessBlobGas(
return 0L; final long parentExcessBlobGas,
final long parentBlobGasUsed,
final Optional<UInt64> parentTargetBlobCount) {
final long parentTargetBlobGas =
parentTargetBlobCount
.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;
} }
/** /**

@ -17,31 +17,37 @@ package org.hyperledger.besu.evm.gascalculator;
import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThat;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestInstance;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class CancunGasCalculatorTest { public class CancunGasCalculatorTest {
private final CancunGasCalculator gasCalculator = new CancunGasCalculator(); private final CancunGasCalculator cancunGasCalculator = new CancunGasCalculator();
@ParameterizedTest(name = "{index} - parent gas {0}, used gas {1}, new excess {2}") @ParameterizedTest(name = "{index} - parent gas {0}, used gas {1}, new excess {2}")
@MethodSource("blobGasses") @MethodSource("blobGasses")
public void shouldCalculateExcessBlobGasCorrectly( public void shouldCalculateExcessBlobGasCorrectly(
final long parentExcess, final long used, final long expected) { final long parentExcess, final long used, final long expected) {
assertThat(gasCalculator.computeExcessBlobGas(parentExcess, (int) used)).isEqualTo(expected); final long usedBlobGas = cancunGasCalculator.blobGasCost(used);
assertThat(
cancunGasCalculator.computeExcessBlobGas(parentExcess, usedBlobGas, Optional.empty()))
.isEqualTo(expected);
} }
static Iterable<Arguments> blobGasses() { Iterable<Arguments> blobGasses() {
long targetGasPerBlock = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK; long targetGasPerBlock = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK;
return List.of( return List.of(
Arguments.of(0L, 0L, 0L), Arguments.of(0L, 0L, 0L),
Arguments.of(targetGasPerBlock, 0L, 0L), Arguments.of(targetGasPerBlock, 0L, 0L),
Arguments.of(0L, 3, 0L), Arguments.of(0L, 3, 0L),
Arguments.of(1, 3, 1), Arguments.of(1, 3, 1),
Arguments.of(targetGasPerBlock, 1, CancunGasCalculator.BLOB_GAS_PER_BLOB), Arguments.of(targetGasPerBlock, 1, cancunGasCalculator.getBlobGasPerBlob()),
Arguments.of(targetGasPerBlock, 3, targetGasPerBlock)); Arguments.of(targetGasPerBlock, 3, targetGasPerBlock));
} }

@ -18,13 +18,65 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.datatypes.Address; 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.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 PragueGasCalculatorTest { class PragueGasCalculatorTest {
private final PragueGasCalculator pragueGasCalculator = new PragueGasCalculator();
@Test @Test
void testPrecompileSize() { void testPrecompileSize() {
PragueGasCalculator subject = new PragueGasCalculator(); PragueGasCalculator subject = new PragueGasCalculator();
assertThat(subject.isPrecompile(Address.precompiled(0x14))).isFalse(); assertThat(subject.isPrecompile(Address.precompiled(0x14))).isFalse();
assertThat(subject.isPrecompile(Address.BLS12_MAP_FP2_TO_G2)).isTrue(); assertThat(subject.isPrecompile(Address.BLS12_MAP_FP2_TO_G2)).isTrue();
} }
@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 target, final long expected) {
final long usedBlobGas = pragueGasCalculator.blobGasCost(used);
assertThat(
pragueGasCalculator.computeExcessBlobGas(
parentExcess, usedBlobGas, Optional.of(UInt64.valueOf(target))))
.isEqualTo(expected);
}
Iterable<Arguments> blobGasses() {
long threeBlobTargetGas = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK;
long cancunTargetCount = 3;
long newTargetCount = 4;
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(
pragueGasCalculator.blobGasCost(newTargetCount),
1L,
newTargetCount,
pragueGasCalculator.getBlobGasPerBlob()),
Arguments.of(threeBlobTargetGas, newTargetCount, newTargetCount, threeBlobTargetGas));
}
} }

Loading…
Cancel
Save