Skip to content

Commit

Permalink
EIP-7623 (#8093)
Browse files Browse the repository at this point in the history
Implements EIP-7623 (increase calldata cost)

Signed-off-by: Daniel Lehrner <[email protected]>
Signed-off-by: Simon Dudley <[email protected]>
Co-authored-by: Simon Dudley <[email protected]>
  • Loading branch information
daniellehrner and siladu authored Jan 16, 2025
1 parent b5fdcc0 commit 1e7f6f6
Show file tree
Hide file tree
Showing 17 changed files with 433 additions and 107 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
- Improve debug_traceBlock calls performance and reduce output size [#8076](https://github.com/hyperledger/besu/pull/8076)
- Add support for EIP-7702 transaction in the txpool [#8018](https://github.com/hyperledger/besu/pull/8018) [#7984](https://github.com/hyperledger/besu/pull/7984)
- Add support for `movePrecompileToAddress` in `StateOverrides` (`eth_call`)[8115](https://github.com/hyperledger/besu/pull/8115)
- Add EIP-7623 - Increase calldata cost [#8093](https://github.com/hyperledger/besu/pull/8093)

### Bug fixes
- Fix serialization of state overrides when `movePrecompileToAddress` is present [#8204](https://github.com/hyperledger/besu/pull/8024)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ protected long getGasLimit(final PrivateTransaction privateTransaction, final St
// choose the highest of the two options
return Math.max(
privateTransaction.getGasLimit(),
gasCalculator.transactionIntrinsicGasCost(Bytes.fromBase64String(pmtPayload), false));
gasCalculator.transactionIntrinsicGasCost(Bytes.fromBase64String(pmtPayload), false, 0));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_PRIVATE_METADATA_UPDATER;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION;
import static org.hyperledger.besu.ethereum.mainnet.PrivateStateUtils.KEY_TRANSACTION_HASH;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;

import org.hyperledger.besu.collections.trie.BytesTrieSet;
import org.hyperledger.besu.datatypes.AccessListEntry;
Expand Down Expand Up @@ -351,22 +352,22 @@ public TransactionProcessingResult processTransaction(
warmAddressList.add(miningBeneficiary);
}

final long intrinsicGas =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation());
final long accessListGas =
gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);
final long codeDelegationGas =
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
final long gasAvailable =
transaction.getGasLimit() - intrinsicGas - accessListGas - codeDelegationGas;
final long intrinsicGas =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(),
transaction.isContractCreation(),
clampedAdd(accessListGas, codeDelegationGas));

final long gasAvailable = transaction.getGasLimit() - intrinsicGas;
LOG.trace(
"Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - codeDelegation)",
"Gas available for execution {} = {} - {} (limit - intrinsic)",
gasAvailable,
transaction.getGasLimit(),
intrinsicGas,
accessListGas,
codeDelegationGas);
intrinsicGas);

final WorldUpdater worldUpdater = evmWorldUpdater.updater();
final ImmutableMap.Builder<String, Object> contextVariablesBuilder =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.mainnet;

import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import static org.hyperledger.besu.evm.internal.Words.clampedAdd;
import static org.hyperledger.besu.evm.worldstate.DelegateCodeHelper.hasDelegatedCode;

import org.hyperledger.besu.crypto.SECPSignature;
Expand Down Expand Up @@ -250,17 +251,22 @@ private ValidationResult<TransactionInvalidReason> validateCostAndFee(
}
}

final long intrinsicGasCost =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation())
+ (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L))
+ gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
if (Long.compareUnsigned(intrinsicGasCost, transaction.getGasLimit()) > 0) {
final long baselineGas =
clampedAdd(
transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L),
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize()));
final long intrinsicGasCostOrFloor =
Math.max(
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation(), baselineGas),
gasCalculator.transactionFloorCost(transaction.getPayload()));

if (Long.compareUnsigned(intrinsicGasCostOrFloor, transaction.getGasLimit()) > 0) {
return ValidationResult.invalid(
TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT,
String.format(
"intrinsic gas cost %s exceeds gas limit %s",
intrinsicGasCost, transaction.getGasLimit()));
intrinsicGasCostOrFloor, transaction.getGasLimit()));
}

if (transaction.calculateUpfrontGasCost(transaction.getMaxGasPrice(), Wei.ZERO, 0).bitLength()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,21 @@

import static org.assertj.core.api.Assertions.assertThat;

import org.hyperledger.besu.datatypes.AccessListEntry;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PragueGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ShanghaiGasCalculator;

import java.util.List;
import java.util.stream.Stream;

import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
Expand All @@ -36,6 +42,8 @@ public class IntrinsicGasTest {
public static Stream<Arguments> data() {
final GasCalculator frontier = new FrontierGasCalculator();
final GasCalculator istanbul = new IstanbulGasCalculator();
final GasCalculator shanghai = new ShanghaiGasCalculator();
final GasCalculator prague = new PragueGasCalculator();
return Stream.of(
// EnoughGAS
Arguments.of(
Expand Down Expand Up @@ -81,16 +89,36 @@ public static Stream<Arguments> data() {
Arguments.of(
istanbul,
21116L,
"0xf87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804"));
"0xf87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804"),
// CallData Gas Increase
Arguments.of(
prague,
21116L,
"0xf87c80018261a894095e7baea6a6c7c4c2dfeb977efac326af552d870a9d00000000000000000000000000000000000000000000000000000000001ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804"),
// AccessList
Arguments.of(
shanghai,
25300L,
"0x01f89a018001826a4094095e7baea6a6c7c4c2dfeb977efac326af552d878080f838f794a95e7baea6a6c7c4c2dfeb977efac326af552d87e1a0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80a05cbd172231fc0735e0fb994dd5b1a4939170a260b36f0427a8a80866b063b948a07c230f7f578dd61785c93361b9871c0706ebfa6d06e3f4491dc9558c5202ed36"));
}

@ParameterizedTest
@MethodSource("data")
public void validateGasCost(
final GasCalculator gasCalculator, final long expectedGas, final String txRlp) {
Transaction t = Transaction.readFrom(RLP.input(Bytes.fromHexString(txRlp)));
Bytes rlp = Bytes.fromHexString(txRlp);

// non-frontier transactions need to be opaque for parsing to work
if (rlp.get(0) > 0) {
final BytesValueRLPOutput output = new BytesValueRLPOutput();
output.writeBytes(rlp);
rlp = output.encoded();
}

Transaction t = Transaction.readFrom(RLP.input(rlp));
Assertions.assertThat(
gasCalculator.transactionIntrinsicGasCost(t.getPayload(), t.isContractCreation()))
gasCalculator.transactionIntrinsicGasCost(
t.getPayload(), t.isContractCreation(), baselineGas(gasCalculator, t)))
.isEqualTo(expectedGas);
}

Expand All @@ -100,4 +128,21 @@ void dryRunDetector() {
.withFailMessage("This test is here so gradle --dry-run executes this class")
.isTrue();
}

long baselineGas(final GasCalculator gasCalculator, final Transaction transaction) {
final List<AccessListEntry> accessListEntries = transaction.getAccessList().orElse(List.of());

int accessListStorageCount = 0;
for (final var entry : accessListEntries) {
final List<Bytes32> storageKeys = entry.storageKeys();
accessListStorageCount += storageKeys.size();
}
final long accessListGas =
gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount);

final long codeDelegationGas =
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());

return accessListGas + codeDelegationGas;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,27 @@ public void shouldRejectTransactionIfIntrinsicGasExceedsGasLimit() {
.gasLimit(10)
.chainId(Optional.empty())
.createTransaction(senderKeys);
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(50L);
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(50L);

assertThat(
validator.validate(
transaction, Optional.empty(), Optional.empty(), transactionValidationParams))
.isEqualTo(
ValidationResult.invalid(TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT));
}

@Test
public void shouldRejectTransactionIfFloorExceedsGasLimit_EIP_7623() {
final TransactionValidator validator =
createTransactionValidator(
gasCalculator, GasLimitCalculator.constant(), false, Optional.empty());
final Transaction transaction =
new TransactionTestFixture()
.gasLimit(10)
.chainId(Optional.empty())
.createTransaction(senderKeys);
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(5L);
when(gasCalculator.transactionFloorCost(any())).thenReturn(51L);

assertThat(
validator.validate(
Expand Down Expand Up @@ -398,7 +418,7 @@ public void shouldAcceptOnlyTransactionsInAcceptedTransactionTypes() {
transaction, Optional.empty(), Optional.empty(), transactionValidationParams))
.isEqualTo(ValidationResult.invalid(INVALID_TRANSACTION_FORMAT));

when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(0L);
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(0L);

assertThat(
eip1559Validator.validate(
Expand Down Expand Up @@ -475,7 +495,7 @@ public void shouldAcceptValidEIP1559() {
.chainId(Optional.of(BigInteger.ONE))
.createTransaction(senderKeys);
final Optional<Wei> basefee = Optional.of(Wei.of(150000L));
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(50L);
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(50L);

assertThat(
validator.validate(transaction, basefee, Optional.empty(), transactionValidationParams))
Expand All @@ -500,7 +520,7 @@ public void shouldValidate1559TransactionWithPriceLowerThanBaseFeeForTransaction
.type(TransactionType.EIP1559)
.chainId(Optional.of(BigInteger.ONE))
.createTransaction(senderKeys);
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean())).thenReturn(50L);
when(gasCalculator.transactionIntrinsicGasCost(any(), anyBoolean(), anyLong())).thenReturn(50L);

assertThat(
validator.validate(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -402,16 +402,20 @@ public void run() {

long txGas = gas;
if (chargeIntrinsicGas) {
final long intrinsicGasCost =
protocolSpec
.getGasCalculator()
.transactionIntrinsicGasCost(tx.getPayload(), tx.isContractCreation());
txGas -= intrinsicGasCost;
final long accessListCost =
tx.getAccessList()
.map(list -> protocolSpec.getGasCalculator().accessListGasCost(list))
.orElse(0L);
txGas -= accessListCost;

final long delegateCodeCost =
protocolSpec.getGasCalculator().delegateCodeGasCost(tx.codeDelegationListSize());

final long intrinsicGasCost =
protocolSpec
.getGasCalculator()
.transactionIntrinsicGasCost(
tx.getPayload(), tx.isContractCreation(), accessListCost + delegateCodeCost);
txGas -= intrinsicGasCost;
}

final EVM evm = protocolSpec.getEvm();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,7 +437,7 @@ static T8nResult runTest(
gasUsed += transactionGasUsed;
long intrinsicGas =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.getTo().isEmpty());
transaction.getPayload(), transaction.getTo().isEmpty(), 0);
TransactionReceipt receipt =
protocolSpec
.getTransactionReceiptFactory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@
},
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"nonce": "0x00",
"balance": "0xad78ebc5ac62000000",
"balance": "0xaa00be18c288efd690",
"code": "0x",
"storage": {}
}
Expand Down Expand Up @@ -194,7 +194,7 @@
"nonce": "0x1"
},
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
"balance": "0xaa00be18c288efd690",
"balance": "0xa688906bd8afdfad20",
"nonce": "0x2"
}
},
Expand All @@ -204,7 +204,7 @@
"requests": [
"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000200405973070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000030100000000000000"
],
"stateRoot": "0x6471f6d90b87f759176a0ad62a7096f69d0d24fd873bdb6b6ced57d04a71e274",
"stateRoot": "0xc769f83dbad9b87a209216d18c4b19cb12b61838594a2e8270898438f4e147af",
"txRoot": "0x2b790bf82ef7259a0e4513d1b89a77d81e99672ba68758ef2ba3fde32851d023",
"receiptsRoot": "0x9c8d7a917ecb3ff2566f264abbf39131e51b08b07eb2b69cb46989d79d985593",
"logsHash": "0x43e31613bfefc1f55d8b3ca2b61f933f3838d523dc11cb5d7ffdd2ecf0ab5d49",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,13 @@ public void milestone(

assertThat(transaction.getSender()).isEqualTo(expected.getSender());
assertThat(transaction.getHash()).isEqualTo(expected.getHash());
final long intrinsicGasCost =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation())
+ (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L));
final long baselineGas =
transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L) +
gasCalculator.delegateCodeGasCost(transaction.codeDelegationListSize());
final long intrinsicGasCost = gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(),
transaction.isContractCreation(),
baselineGas);
assertThat(intrinsicGasCost).isEqualTo(expected.getIntrinsicGas());
} catch (final Exception e) {
if (expected.isSucceeds()) {
Expand Down
Loading

0 comments on commit 1e7f6f6

Please sign in to comment.