EIP-7742 - gas calculation using target_block_count

Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
pull/7805/head
Simon Dudley 19 hours ago
parent 0a9ca671b7
commit cde63f23fd
  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

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

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

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

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

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

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

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

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

@ -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<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 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<Arguments> blobGasses() {
Iterable<Arguments> 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));
}

Loading…
Cancel
Save