Skip to content

Commit

Permalink
EIP-7742 - gas calculation using target_block_count
Browse files Browse the repository at this point in the history
Signed-off-by: Simon Dudley <simon.dudley@consensys.net>
  • Loading branch information
siladu committed Oct 30, 2024
1 parent f82bb7d commit 7eef3b0
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,7 @@

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;

Expand Down Expand Up @@ -124,7 +121,6 @@ public void setUp() throws Exception {
when(blockchain.getChainHeadHeader()).thenReturn(blockHeader);
when(protocolSpec.getGasCalculator()).thenReturn(gasCalculator);
when(protocolContext.getBadBlockManager()).thenReturn(badBlockManager);
lenient().when(gasCalculator.computeExcessBlobGas(anyLong(), anyInt())).thenReturn(0L);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
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;
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -89,8 +89,8 @@ void notBlobTransactionsAreSelectedWithoutAnyCheck() {
@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);

Expand Down Expand Up @@ -131,8 +131,8 @@ void returnsBlobsFullWhenMaxNumberOfBlobsAlreadyPresent() {
@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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
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;
Expand Down Expand Up @@ -174,7 +173,8 @@ public BlockProcessingResult processBlock(
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 =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public static BlobGas calculateExcessBlobGasForParent(
.getGasCalculator()
.computeExcessBlobGas(
parentHeader.getExcessBlobGas().map(BlobGas::toLong).orElse(0L),
parentHeader.getBlobGasUsed().orElse(0L));
parentHeader.getBlobGasUsed().orElse(0L),
parentHeader.getTargetBlobCount());
return BlobGas.of(headerExcess);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
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;
Expand Down Expand Up @@ -45,7 +44,8 @@ public boolean validate(final BlockHeader header, final BlockHeader parent) {
long parentBlobGasUsed = parent.getBlobGasUsed().orElse(0L);

long calculatedExcessBlobGas =
gasCalculator.computeExcessBlobGas(parentExcessBlobGas, parentBlobGasUsed);
gasCalculator.computeExcessBlobGas(
parentExcessBlobGas, parentBlobGasUsed, parent.getTargetBlobCount());

if (headerExcessBlobGas != calculatedExcessBlobGas) {
LOG.info(
Expand All @@ -55,10 +55,9 @@ public boolean validate(final BlockHeader header, final BlockHeader parent) {
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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
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;

Expand Down Expand Up @@ -589,7 +589,7 @@ public void shouldRejectContractCreateWithBlob() {

@Test
public void shouldAcceptTransactionWithAtLeastOneBlob() {
when(gasCalculator.blobGasCost(anyInt())).thenReturn(2L);
when(gasCalculator.blobGasCost(anyLong())).thenReturn(2L);
final TransactionValidator validator =
createTransactionValidator(
gasCalculator,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,37 @@
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();
Expand All @@ -52,15 +63,16 @@ public void validateHeader_BlobGasMatchesCalculated_SuccessValidation() {
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();
Expand All @@ -72,6 +84,48 @@ public void validateHeader_BlobGasDifferentFromCalculated_FailsValidation() {
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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,13 @@ protected CancunGasCalculator(final int maxPrecompile) {
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
Expand All @@ -64,8 +64,13 @@ public long getTransientStoreOperationGasCost() {
}

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

/**
Expand All @@ -76,35 +81,4 @@ public long blobGasCost(final int blobCount) {
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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,12 @@
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.
Expand Down Expand Up @@ -614,36 +616,50 @@ default long getTransientStoreOperationGasCost() {
}

/**
* 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;
}

/**
Expand Down
Loading

0 comments on commit 7eef3b0

Please sign in to comment.