diff --git a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java index 6626a66bb0..3f8be18151 100644 --- a/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.java +++ b/ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/processor/TransactionTracerTest.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.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.Mockito.lenient; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -124,7 +121,6 @@ public class TransactionTracerTest { when(blockchain.getChainHeadHeader()).thenReturn(blockHeader); when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator); when(protocolContext.getBadBlockManager()).thenReturn(badBlockManager); - lenient().when(gasCalculator.computeExcessBlobGas(anyLong(), anyInt())).thenReturn(0L); } @Test diff --git a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java index 123faf2d18..7d9292935e 100644 --- a/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java +++ b/ethereum/blockcreation/src/test/java/org/hyperledger/besu/ethereum/blockcreation/txselection/selectors/BlobSizeTransactionSelectorTest.java @@ -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.fail; 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.verifyNoInteractions; import static org.mockito.Mockito.when; @@ -61,7 +61,7 @@ class BlobSizeTransactionSelectorTest { Suppliers.memoize(SignatureAlgorithmFactory::getInstance); 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 long MAX_BLOB_GAS = BLOB_GAS_PER_BLOB * MAX_BLOBS; @@ -89,8 +89,8 @@ class BlobSizeTransactionSelectorTest { @Test void firstBlobTransactionIsSelected() { when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS); - when(blockSelectionContext.gasCalculator().blobGasCost(anyInt())) - .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Integer.class)); + when(blockSelectionContext.gasCalculator().blobGasCost(anyLong())) + .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class)); final var selector = new BlobSizeTransactionSelector(blockSelectionContext); @@ -131,8 +131,8 @@ class BlobSizeTransactionSelectorTest { @Test void returnsTooLargeForRemainingBlobGas() { when(blockSelectionContext.gasLimitCalculator().currentBlobGasLimit()).thenReturn(MAX_BLOB_GAS); - when(blockSelectionContext.gasCalculator().blobGasCost(anyInt())) - .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Integer.class)); + when(blockSelectionContext.gasCalculator().blobGasCost(anyLong())) + .thenAnswer(iom -> BLOB_GAS_PER_BLOB * iom.getArgument(0, Long.class)); final var selector = new BlobSizeTransactionSelector(blockSelectionContext); diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java index b8863fe142..496652cdf6 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessor.java @@ -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.BonsaiWorldStateUpdateAccumulator; 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.tracing.OperationTracer; import org.hyperledger.besu.evm.worldstate.WorldState; @@ -174,7 +173,8 @@ public abstract class AbstractBlockProcessor implements BlockProcessor { currentGasUsed += transaction.getGasLimit() - transactionProcessingResult.getGasRemaining(); if (transaction.getVersionedHashes().isPresent()) { currentBlobGasUsed += - (transaction.getVersionedHashes().get().size() * CancunGasCalculator.BLOB_GAS_PER_BLOB); + (transaction.getVersionedHashes().get().size() + * protocolSpec.getGasCalculator().getBlobGasPerBlob()); } final TransactionReceipt transactionReceipt = 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 f6372097b7..8d8215b1ca 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,7 +36,8 @@ public class ExcessBlobGasCalculator { .getGasCalculator() .computeExcessBlobGas( parentHeader.getExcessBlobGas().map(BlobGas::toLong).orElse(0L), - parentHeader.getBlobGasUsed().orElse(0L)); + parentHeader.getBlobGasUsed().orElse(0L), + parentHeader.getTargetBlobCount()); 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 3a34d7be30..02a7aa859b 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 @@ -17,7 +17,6 @@ package org.hyperledger.besu.ethereum.mainnet.headervalidationrules; import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.ethereum.core.BlockHeader; import org.hyperledger.besu.ethereum.mainnet.DetachedBlockHeaderValidationRule; -import org.hyperledger.besu.evm.gascalculator.CancunGasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.slf4j.Logger; @@ -45,7 +44,8 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L); long calculatedExcessBlobGas = - gasCalculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed); + gasCalculator.computeExcessBlobGas( + parentExcessBlobGas, parentBlobGasUsed, parent.getTargetBlobCount()); if (headerExcessBlobGas != calculatedExcessBlobGas) { LOG.info( @@ -55,10 +55,9 @@ public class BlobGasValidationRule implements DetachedBlockHeaderValidationRule return false; } long headerBlobGasUsed = header.getBlobGasUsed().orElse(0L); - if (headerBlobGasUsed % CancunGasCalculator.BLOB_GAS_PER_BLOB != 0) { + if (headerBlobGasUsed % gasCalculator.getBlobGasPerBlob() != 0) { LOG.info( - "blob gas used must be multiple of GAS_PER_BLOB ({})", - CancunGasCalculator.BLOB_GAS_PER_BLOB); + "blob gas used must be multiple of GAS_PER_BLOB ({})", gasCalculator.getBlobGasPerBlob()); return false; } return true; diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java index 182f1b609e..c673bf08f6 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java @@ -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.mockito.ArgumentMatchers.any; 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.when; @@ -589,7 +589,7 @@ public class MainnetTransactionValidatorTest { @Test public void shouldAcceptTransactionWithAtLeastOneBlob() { - when(gasCalculator.blobGasCost(anyInt())).thenReturn(2L); + when(gasCalculator.blobGasCost(anyLong())).thenReturn(2L); final TransactionValidator validator = createTransactionValidator( gasCalculator, 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 858a8a9a26..4eadc25718 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 @@ -20,26 +20,37 @@ import org.hyperledger.besu.datatypes.BlobGas; import org.hyperledger.besu.ethereum.core.BlockHeader; 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; /** Tests for the {@link BlobGasValidationRule} class. */ public class BlobGasValidationRuleTest { - private CancunGasCalculator gasCalculator; - private BlobGasValidationRule blobGasValidationRule; + private CancunGasCalculator cancunGasCalculator; + private BlobGasValidationRule cancunBlobGasValidationRule; + + private PragueGasCalculator pragueGasCalculator; + private BlobGasValidationRule pragueBlobGasValidationRule; @BeforeEach public void setUp() { - gasCalculator = new CancunGasCalculator(); - blobGasValidationRule = new BlobGasValidationRule(gasCalculator); + cancunGasCalculator = new CancunGasCalculator(); + 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 public void validateHeader_BlobGasMatchesCalculated_SuccessValidation() { - long target = gasCalculator.getTargetBlobGasPerBlock(); + long target = cancunGasCalculator.getTargetBlobGasPerBlock(); // Create parent header final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); @@ -52,15 +63,16 @@ public class BlobGasValidationRuleTest { headerBuilder.excessBlobGas(BlobGas.of(1L)); 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 public void validateHeader_BlobGasDifferentFromCalculated_FailsValidation() { - long target = gasCalculator.getTargetBlobGasPerBlock(); + long target = cancunGasCalculator.getTargetBlobGasPerBlock(); // Create parent header final BlockHeaderTestFixture parentBuilder = new BlockHeaderTestFixture(); @@ -72,6 +84,48 @@ public class BlobGasValidationRuleTest { final BlockHeaderTestFixture headerBuilder = new BlockHeaderTestFixture(); 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(); } } 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 79165e5c21..2d6168322e 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 @@ -44,13 +44,13 @@ public class CancunGasCalculator extends ShanghaiGasCalculator { 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. */ - 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. */ - public static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000; + static final long TARGET_BLOB_GAS_PER_BLOCK = 0x60000; // EIP-1153 @Override @@ -64,8 +64,13 @@ public class CancunGasCalculator extends ShanghaiGasCalculator { } @Override - public long blobGasCost(final int blobCount) { - return BLOB_GAS_PER_BLOB * blobCount; + public long blobGasCost(final long 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() { 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; - } } 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 fd453deac5..f72c53cf52 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,10 +40,12 @@ 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. @@ -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 total gas cost + * @return the blob gas cost per blob */ - default long blobGasCost(final int blobCount) { + default long getBlobGasPerBlob() { return 0L; } /** - * Compute the new value for the excess blob gas, given the parent value and the count of new - * blobs + * Return the gas cost given the number of blobs * - * @param parentExcessBlobGas excess blob gas from the parent - * @param newBlobs count of new blobs - * @return the new excess blob gas value + * @param blobCount the number of blobs + * @return the total gas cost */ - default long computeExcessBlobGas(final long parentExcessBlobGas, final int newBlobs) { + default long blobGasCost(final long blobCount) { 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 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 */ - default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) { - return 0L; + default long computeExcessBlobGas( + final long parentExcessBlobGas, + final long parentBlobGasUsed, + final Optional 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; } /** 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 ded199bf3b..b7a11395a9 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,31 +17,37 @@ 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; 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) 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}") @MethodSource("blobGasses") public void shouldCalculateExcessBlobGasCorrectly( 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 blobGasses() { + Iterable blobGasses() { long targetGasPerBlock = CancunGasCalculator.TARGET_BLOB_GAS_PER_BLOCK; return List.of( Arguments.of(0L, 0L, 0L), Arguments.of(targetGasPerBlock, 0L, 0L), Arguments.of(0L, 3, 0L), 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)); } 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 c528dab64f..0b9d0f2372 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 @@ -18,13 +18,65 @@ 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; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +@TestInstance(TestInstance.Lifecycle.PER_CLASS) class PragueGasCalculatorTest { + + private final PragueGasCalculator pragueGasCalculator = new PragueGasCalculator(); + @Test void testPrecompileSize() { PragueGasCalculator subject = new PragueGasCalculator(); assertThat(subject.isPrecompile(Address.precompiled(0x14))).isFalse(); 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 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)); + } }