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 @@ -518,9 +518,7 @@ public TransactionProcessingResult processTransaction(
// For pre-Prague forks, floor cost is 0, so this returns just execution gas
// For Prague+ forks with EIP-7778, this ensures block gas accounts for data floor
// EIP-8037: Gas accounting with multidimensional gas support
final long floorCost =
gasCalculator.transactionFloorCost(
transaction.getPayload(), transaction.getPayloadZeroBytes());
final long floorCost = gasCalculator.transactionFloorCost(transaction);
final TransactionGasAccounting.GasResult gasResult =
TransactionGasAccounting.builder()
.txGasLimit(transaction.getGasLimit())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,7 @@ private ValidationResult<TransactionInvalidReason> validateCostAndFee(
final long intrinsicGasCostOrFloor =
Math.max(
gasCalculator.transactionIntrinsicGasCost(transaction, baselineGas),
gasCalculator.transactionFloorCost(
transaction.getPayload(), transaction.getPayloadZeroBytes()));
gasCalculator.transactionFloorCost(transaction));

if (Long.compareUnsigned(intrinsicGasCostOrFloor, transaction.getGasLimit()) > 0) {
return ValidationResult.invalid(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -818,7 +818,7 @@ void processAccountReadThenUpdateTx(final BlockProcessor blockProcessor) {

Block blockWithTransactions =
createBlockWithTransactions(
"0x1f5501e9dccf8e5255e1fd4f4d0d215df77a979c60254813ac097bc3c4f0e673",
"0xa58e5254b57d4d748214c84a6ed2786b30325d951c4b322b33b0059506e5a97b",
Wei.of(5),
transactionTransfer,
getcontractBalanceTransaction,
Expand Down Expand Up @@ -886,7 +886,7 @@ void processAccountUpdateThenReadTx(final BlockProcessor blockProcessor) {

Block blockWithTransactions =
createBlockWithTransactions(
"0x2eeac8f0934e08deba88bea320eb0400101654cba1f10d5ff21d5f8186cb3789",
"0xfaaa10fddc61e91833311e3d14001e2f39b317022c41329074cc7e12e4a8fd68",
Wei.of(5),
transactionTransfer,
sendEthFromContractTransaction,
Expand Down Expand Up @@ -953,7 +953,7 @@ void processAccountReadThenUpdateTxWithTwoAccounts(final BlockProcessor blockPro

Block blockWithTransactions =
createBlockWithTransactions(
"0x7cf1e5035b25f95eb728b150992179afa5445bcdcba371ed16198ba3f909877e",
"0x9ac394b9d9927ee863719a706ddbfebce4c6462152836c3c70a84f3b538b2048",
Wei.of(5),
transactionTransfer,
getcontractBalanceTransaction,
Expand Down Expand Up @@ -1022,7 +1022,7 @@ void processAccountUpdateThenReadTxWithTwoAccounts(final BlockProcessor blockPro

Block blockWithTransactions =
createBlockWithTransactions(
"0x7cf1e5035b25f95eb728b150992179afa5445bcdcba371ed16198ba3f909877e",
"0x9ac394b9d9927ee863719a706ddbfebce4c6462152836c3c70a84f3b538b2048",
Wei.of(5),
transactionTransfer,
sendEthFromContractTransaction,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ public void shouldRejectTransactionIfFloorExceedsGasLimit_EIP_7623() {
.chainId(Optional.empty())
.createTransaction(senderKeys);
when(gasCalculator.transactionIntrinsicGasCost(any(), anyLong())).thenReturn(5L);
when(gasCalculator.transactionFloorCost(any(), anyLong())).thenReturn(51L);
when(gasCalculator.transactionFloorCost(any(org.hyperledger.besu.datatypes.Transaction.class)))
.thenReturn(51L);

assertThat(
validator.validate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@

import static org.hyperledger.besu.evm.internal.Words.clampedAdd;

import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.frame.MessageFrame;

import java.util.List;
import java.util.function.Supplier;

import org.apache.tuweni.bytes.Bytes;
Expand All @@ -38,13 +40,19 @@
* <UL>
* <LI>EIP-7928: gas cost per item for block access list size limit
* <LI>EIP-7976: calldata floor cost raised to 64 gas per byte
* <LI>EIP-7981: access list data priced at the 64 gas/byte floor
* </UL>
*/
public class AmsterdamGasCalculator extends OsakaGasCalculator {

// EIP-7976: floor cost of 64 gas per calldata byte (zero and non-zero alike)
// EIP-7976 / EIP-7981: floor cost of 64 gas per data byte (calldata or access list).
private static final long TOTAL_COST_FLOOR_PER_BYTE = 64L;

// EIP-7981: data floor contribution of an access list entry.
// 20 address bytes * 64 gas/byte = 1280; each 32-byte storage key * 64 gas/byte = 2048.
private static final long ACCESS_LIST_ADDRESS_FLOOR_COST = 20L * TOTAL_COST_FLOOR_PER_BYTE;
private static final long ACCESS_LIST_STORAGE_KEY_FLOOR_COST = 32L * TOTAL_COST_FLOOR_PER_BYTE;

// EIP-8037: New regular gas constants for Amsterdam
private static final long TX_CREATE_COST = 9_000L;

Expand Down Expand Up @@ -110,6 +118,34 @@ public long transactionFloorCost(final Bytes transactionPayload, final long payl
getMinimumTransactionCost(), transactionPayload.size() * TOTAL_COST_FLOOR_PER_BYTE);
}

@Override
public long transactionFloorCost(final Transaction transaction) {
// EIP-7981: include access list bytes in the data floor so they can't be used to bypass it.
final long calldataBytes = transaction.getPayload().size();
final long accessListBytes =
transaction.getAccessList().map(AmsterdamGasCalculator::accessListBytes).orElse(0L);
return clampedAdd(
getMinimumTransactionCost(), (calldataBytes + accessListBytes) * TOTAL_COST_FLOOR_PER_BYTE);
}

@Override
public long accessListGasCost(final int addresses, final int storageSlots) {
// EIP-2930 baseline (2400 per address, 1900 per key) plus EIP-7981 data floor
// (1280 per address, 2048 per storage key), so access list data is always charged
// at floor rate regardless of which branch of the gasUsed max() wins.
return super.accessListGasCost(addresses, storageSlots)
+ addresses * ACCESS_LIST_ADDRESS_FLOOR_COST
+ storageSlots * ACCESS_LIST_STORAGE_KEY_FLOOR_COST;
}

private static long accessListBytes(final List<AccessListEntry> accessList) {
long bytes = 0L;
for (final AccessListEntry entry : accessList) {
bytes += 20L + 32L * entry.storageKeys().size();
}
return bytes;
}

// --- EIP-8037 Gas Cost Overrides ---

@Override
Expand Down Expand Up @@ -203,8 +239,7 @@ public long calculateGasRefund(
final long refundAllowance = Math.min(executionRefund, maxRefundAllowance);

final long gasUsed = totalConsumed - refundAllowance;
final long floorCost =
transactionFloorCost(transaction.getPayload(), transaction.getPayloadZeroBytes());
final long floorCost = transactionFloorCost(transaction);
return gasLimit - Math.max(gasUsed, floorCost);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,18 @@ default long modExpGasCost(final Bytes input) {
*/
long transactionFloorCost(final Bytes transactionPayload, final long payloadZeroBytes);

/**
* Returns the floor gas cost of a transaction. Unlike the payload-only overload, this variant can
* incorporate additional transaction-level data (e.g. access list bytes under EIP-7981) when
* computing the floor.
*
* @param transaction The transaction
* @return the transaction's floor gas cost
*/
default long transactionFloorCost(final Transaction transaction) {
return transactionFloorCost(transaction.getPayload(), transaction.getPayloadZeroBytes());
}

/**
* Returns the gas cost of the explicitly declared access list.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,29 @@
package org.hyperledger.besu.evm.gascalculator;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.when;

import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Transaction;

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

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

@ExtendWith(MockitoExtension.class)
class AmsterdamGasCalculatorTest {

private final AmsterdamGasCalculator amsterdamGasCalculator = new AmsterdamGasCalculator();

@Mock private Transaction transaction;

@Test
void transactionFloorCostShouldBeAtLeastTransactionBaseCost() {
// floor cost = 21000 (base cost) + 0
Expand All @@ -39,4 +54,38 @@ void transactionFloorCostShouldBeAtLeastTransactionBaseCost() {
Bytes.fromHexString("0x0001000100010001000101"), 5))
.isEqualTo(21704L);
}

@Test
void accessListGasCostIncludesDataFloor() {
// EIP-2930: 2400/address + 1900/key; EIP-7981: +1280/address + 2048/key
// One address + zero keys = 2400 + 1280 = 3680
assertThat(amsterdamGasCalculator.accessListGasCost(1, 0)).isEqualTo(3680L);
// One address + one key = 3680 + 1900 + 2048 = 7628
assertThat(amsterdamGasCalculator.accessListGasCost(1, 1)).isEqualTo(7628L);
// Three addresses + five keys = 3*3680 + 5*(1900+2048) = 11040 + 19740 = 30780
assertThat(amsterdamGasCalculator.accessListGasCost(3, 5)).isEqualTo(30780L);
}

@Test
void transactionFloorCostWithoutAccessListMatchesCalldataOnlyFloor() {
when(transaction.getPayload()).thenReturn(Bytes.repeat((byte) 0x1, 256));
when(transaction.getAccessList()).thenReturn(Optional.empty());

// 21000 + 256 * 64 = 37384
assertThat(amsterdamGasCalculator.transactionFloorCost(transaction)).isEqualTo(37384L);
}

@Test
void transactionFloorCostIncludesAccessListBytes() {
// 10 calldata bytes + 1 address (20 bytes) + 2 keys (2*32 = 64 bytes) = 94 bytes
// 21000 + 94 * 64 = 21000 + 6016 = 27016
final AccessListEntry entry =
new AccessListEntry(
Address.fromHexString("0x00000000000000000000000000000000000000aa"),
List.of(Bytes32.ZERO, Bytes32.ZERO));
when(transaction.getPayload()).thenReturn(Bytes.repeat((byte) 0x1, 10));
when(transaction.getAccessList()).thenReturn(Optional.of(List.of(entry)));

assertThat(amsterdamGasCalculator.transactionFloorCost(transaction)).isEqualTo(27016L);
}
}
Loading