Skip to content

Commit

Permalink
common/evm/vm: implement EIP7623 calldata cost increase
Browse files Browse the repository at this point in the history
  • Loading branch information
jochem-brouwer committed Dec 16, 2024
1 parent 74a2123 commit 7e8562e
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 2 deletions.
9 changes: 9 additions & 0 deletions packages/common/src/eips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,15 @@ export const eipsDict: EIPsDict = {
*/
requiredEIPs: [3540, 3541, 3670],
},
/**
* Description : Increase calldata cost to reduce maximum block size
* URL : https://github.com/ethereum/EIPs/blob/da2a86bf15044416e8eb0301c9bdb8d561feeb32/EIPS/eip-7623.md
* Status : Review
*/
7623: {
minimumHardfork: Hardfork.Chainstart,
requiredEIPs: [],
},
/**
* Description : General purpose execution layer requests
* URL : https://eips.ethereum.org/EIPS/eip-7685
Expand Down
6 changes: 6 additions & 0 deletions packages/tx/src/params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ export const paramsTx: ParamsDict = {
blobCommitmentVersionKzg: 1, // The number indicated a versioned hash is a KZG commitment
},
/**
* Increase calldata cost to reduce maximum block size
*/
7623: {
totalCostFloorPerToken: 10,
},
/**
. * Set EOA account code for one transaction
. */
7702: {
Expand Down
25 changes: 23 additions & 2 deletions packages/vm/src/runTx.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
KECCAK256_NULL,
MAX_UINT64,
SECP256K1_ORDER_DIV_2,
bigIntMax,
bytesToBigInt,
bytesToHex,
bytesToUnprefixedHex,
Expand Down Expand Up @@ -249,11 +250,24 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise<RunTxResult> {

// Validate gas limit against tx base fee (DataFee + TxFee + Creation Fee)
const intrinsicGas = tx.getIntrinsicGas()
let floorCost = BIGINT_0

if (vm.common.isActivatedEIP(7623)) {
// Tx should at least cover the floor price for tx data
let tokens = 0
for (let i = 0; i < tx.data.length; i++) {
tokens += tx.data[i] === 0 ? 1 : 4
}
floorCost =
tx.common.param('txGas') + tx.common.param('totalCostFloorPerToken') * BigInt(tokens)
}

let gasLimit = tx.gasLimit
if (gasLimit < intrinsicGas) {
const minGasLimit = bigIntMax(intrinsicGas, floorCost)
if (gasLimit < minGasLimit) {
const msg = _errorMsg(
`tx gas limit ${Number(gasLimit)} is lower than the minimum gas limit of ${Number(
intrinsicGas,
minGasLimit,
)}`,
vm,
block,
Expand Down Expand Up @@ -635,6 +649,13 @@ async function _runTx(vm: VM, opts: RunTxOpts): Promise<RunTxResult> {
debug(`No tx gasRefund`)
}
}

if (vm.common.isActivatedEIP(7623)) {
if (results.totalGasSpent < floorCost) {
results.totalGasSpent = floorCost
}
}

results.amountSpent = results.totalGasSpent * gasPrice

// Update sender's balance
Expand Down
125 changes: 125 additions & 0 deletions packages/vm/test/api/EIPs/eip-7623.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
import { createBlock } from '@ethereumjs/block'
import { Common, Hardfork, Mainnet } from '@ethereumjs/common'
import { createLegacyTx } from '@ethereumjs/tx'
import { Account, Address, createZeroAddress, hexToBytes, privateToAddress } from '@ethereumjs/util'
import { assert, describe, it } from 'vitest'

import { createVM, runTx } from '../../../src/index.js'

const common = new Common({ chain: Mainnet, hardfork: Hardfork.Prague })

const pkey = hexToBytes(`0x${'20'.repeat(32)}`)
const GWEI = BigInt(1000000000)
const sender = new Address(privateToAddress(pkey))

const coinbase = new Address(hexToBytes(`0x${'ff'.repeat(20)}`))

const block = createBlock(
{
header: {
baseFeePerGas: 7,
coinbase,
},
},
{ common },
)

const code = hexToBytes('0x60008080806001415AF100')
const contractAddress = new Address(hexToBytes(`0x${'ee'.repeat(20)}`))

async function getVM(common: Common) {
const vm = await createVM({ common })
await vm.stateManager.putAccount(sender, new Account())
const account = await vm.stateManager.getAccount(sender)
const balance = GWEI * BigInt(21000) * BigInt(10000000)
account!.balance = balance
await vm.stateManager.putAccount(sender, account!)

await vm.stateManager.putCode(contractAddress, code)
return vm
}

describe('EIP 7623 calldata cost increase tests', () => {
it('charges floor gas', async () => {
const vm = await getVM(common)

const tx = createLegacyTx(
{
to: createZeroAddress(),
data: new Uint8Array(100).fill(1),
gasLimit: 1000000,
gasPrice: 10,
},
{ common },
).sign(pkey)

const result = await runTx(vm, {
block,
tx,
skipHardForkValidation: true,
})

const baseCost = tx.common.param('txGas')
const floorCost = tx.common.param('totalCostFloorPerToken')

Check failure on line 63 in packages/vm/test/api/EIPs/eip-7623.spec.ts

View workflow job for this annotation

GitHub Actions / vm-pr / vm-api

test/api/EIPs/eip-7623.spec.ts > EIP 7623 calldata cost increase tests > charges floor gas

Error: Missing parameter value for totalCostFloorPerToken ❯ Common.param ../common/src/common.ts:360:13 ❯ test/api/EIPs/eip-7623.spec.ts:63:33

Check failure on line 63 in packages/vm/test/api/EIPs/eip-7623.spec.ts

View workflow job for this annotation

GitHub Actions / browser / test-all-browser

test/api/EIPs/eip-7623.spec.ts > EIP 7623 calldata cost increase tests > charges floor gas

Error: Missing parameter value for totalCostFloorPerToken ❯ test/api/EIPs/eip-7623.spec.ts:63:32

const expected = baseCost + BigInt(tx.data.length) * BigInt(4) * floorCost

assert.equal(result.totalGasSpent, expected)
})
it('rejects transactions having a gas limit below the floor gas limit', async () => {
const vm = await getVM(common)

const tx = createLegacyTx(
{
to: createZeroAddress(),
data: new Uint8Array(100).fill(1),
gasLimit: 21000 + 100 * 4,
gasPrice: 10,
},
{ common },
).sign(pkey)
try {
await runTx(vm, {
block,
tx,
skipHardForkValidation: true,
})
assert.fail('runTx should throw')
} catch (e) {
assert.ok('Succesfully failed')
}
})
it('correctly charges execution gas instead of floor gas when execution gas exceeds the floor gas', async () => {
const vm = await getVM(common)
const to = createZeroAddress()

// Store 1 in slot 1
await vm.stateManager.putCode(to, hexToBytes('0x6001600155'))

const tx = createLegacyTx(
{
to: createZeroAddress(),
data: new Uint8Array(100).fill(1),
gasLimit: 1000000,
gasPrice: 10,
},
{ common },
).sign(pkey)

const result = await runTx(vm, {
block,
tx,
skipHardForkValidation: true,
})

const baseCost = tx.common.param('txGas')

const expected =
baseCost +
BigInt(tx.data.length) * tx.common.param('txDataNonZeroGas') +
BigInt(2 * 3) +
BigInt(22_100)

assert.equal(result.totalGasSpent, expected)
})
})

0 comments on commit 7e8562e

Please sign in to comment.