ethereum.forks.amsterdam.fork

Ethereum Specification.

.. contents:: Table of Contents :backlinks: none :local:

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

114
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

115
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

116
EMPTY_OMMER_HASH = keccak256(rlp.encode([]))

SYSTEM_ADDRESS

117
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

118
BEACON_ROOTS_ADDRESS = hex_to_address(
119
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
120
)

SYSTEM_TRANSACTION_GAS

121
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

122
MAX_BLOB_GAS_PER_BLOCK = GasCosts.BLOB_SCHEDULE_MAX * GasCosts.PER_BLOB

VERSIONED_HASH_VERSION_KZG

123
VERSIONED_HASH_VERSION_KZG = b"\x01"

GWEI_TO_WEI

124
GWEI_TO_WEI = U256(10**9)

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

126
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
127
    "0x00000961Ef480Eb55e80D19ad83579A64c007002"
128
)

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

129
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
130
    "0x0000BBdDc7CE488642fb579F8B00f3a590007251"
131
)

HISTORY_STORAGE_ADDRESS

132
HISTORY_STORAGE_ADDRESS = hex_to_address(
133
    "0x0000F90827F1C53a10cb7A02335B175320002935"
134
)

MAX_BLOCK_SIZE

135
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

136
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

137
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

138
BLOB_COUNT_LIMIT = 6

ChainContext

Chain context needed for block execution.

141
@slotted_freezable
142
@dataclass
class ChainContext:

chain_id

Identify the chain for transaction signature recovery.

148
    chain_id: U64

block_hashes

Recent ancestor hashes (up to 256) for the BLOCKHASH opcode.

151
    block_hashes: List[Hash32]

parent_header

Parent header used for header validation and system contracts.

154
    parent_header: Header | PreviousHeader

BlockChain

History and current state of the block chain.

158
@dataclass
class BlockChain:

blocks

164
    blocks: List[Block]

state

165
    state: State

chain_id

166
    chain_id: U64

apply_fork

Transforms the state from the previous hard fork (old) into the block chain object for this hard fork and returns it.

When forks need to implement an irregular state transition, this function is used to handle the irregularity. See the :ref:DAO Fork <dao-fork> for an example.

Parameters

old : Previous block chain object.

Returns

new : BlockChain Upgraded block chain object for this hard fork.

def apply_fork(old: BlockChain) -> BlockChain:
170
    """
171
    Transforms the state from the previous hard fork (`old`) into the block
172
    chain object for this hard fork and returns it.
173
174
    When forks need to implement an irregular state transition, this function
175
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
176
    an example.
177
178
    Parameters
179
    ----------
180
    old :
181
        Previous block chain object.
182
183
    Returns
184
    -------
185
    new : `BlockChain`
186
        Upgraded block chain object for this hard fork.
187
188
    """
189
    return old

get_last_256_block_hashes

Obtain the list of hashes of the previous 256 blocks in order of increasing block number.

This function will return less hashes for the first 256 blocks.

The BLOCKHASH opcode needs to access the latest hashes on the chain, therefore this function retrieves them.

Parameters

chain : History and current state.

Returns

recent_block_hashes : List[Hash32] Hashes of the recent 256 blocks in order of increasing block number.

def get_last_256_block_hashes(chain: BlockChain) -> List[Hash32]:
193
    """
194
    Obtain the list of hashes of the previous 256 blocks in order of
195
    increasing block number.
196
197
    This function will return less hashes for the first 256 blocks.
198
199
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
200
    therefore this function retrieves them.
201
202
    Parameters
203
    ----------
204
    chain :
205
        History and current state.
206
207
    Returns
208
    -------
209
    recent_block_hashes : `List[Hash32]`
210
        Hashes of the recent 256 blocks in order of increasing block number.
211
212
    """
213
    recent_blocks = chain.blocks[-255:]
214
    # TODO: This function has not been tested rigorously
215
    if len(recent_blocks) == 0:
216
        return []
217
218
    recent_block_hashes = []
219
220
    for block in recent_blocks:
221
        prev_block_hash = block.header.parent_hash
222
        recent_block_hashes.append(prev_block_hash)
223
224
    # We are computing the hash only for the most recent block and not for
225
    # the rest of the blocks as they have successors which have the hash of
226
    # the current block as parent hash.
227
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
228
    recent_block_hashes.append(most_recent_block_hash)
229
230
    return recent_block_hashes

state_transition

Attempts to apply a block to an existing block chain.

All parts of the block's contents need to be verified before being added to the chain. Blocks are verified by ensuring that the contents of the block make logical sense with the contents of the parent block. The information in the block's header must also match the corresponding information in the block.

To implement Ethereum, in theory clients are only required to store the most recent 255 blocks of the chain since as far as execution is concerned, only those blocks are accessed. Practically, however, clients should store more blocks to handle reorgs.

Parameters

chain : History and current state. block : Block to apply to chain.

def state_transition(chain: BlockChain, ​​block: Block) -> None:
234
    """
235
    Attempts to apply a block to an existing block chain.
236
237
    All parts of the block's contents need to be verified before being added
238
    to the chain. Blocks are verified by ensuring that the contents of the
239
    block make logical sense with the contents of the parent block. The
240
    information in the block's header must also match the corresponding
241
    information in the block.
242
243
    To implement Ethereum, in theory clients are only required to store the
244
    most recent 255 blocks of the chain since as far as execution is
245
    concerned, only those blocks are accessed. Practically, however, clients
246
    should store more blocks to handle reorgs.
247
248
    Parameters
249
    ----------
250
    chain :
251
        History and current state.
252
    block :
253
        Block to apply to `chain`.
254
255
    """
256
    chain_context = ChainContext(
257
        chain_id=chain.chain_id,
258
        block_hashes=get_last_256_block_hashes(chain),
259
        parent_header=chain.blocks[-1].header,
260
    )
261
262
    block_diff = execute_block(block, chain.state, chain_context)
263
264
    apply_changes_to_state(chain.state, block_diff)
265
    chain.blocks.append(block)
266
    if len(chain.blocks) > 255:
267
        # Real clients have to store more blocks to deal with reorgs, but the
268
        # protocol only requires the last 255
269
        chain.blocks = chain.blocks[-255:]

execute_block

Execute a block and validate the resulting roots against the header.

This method is idempotent.

Parameters

block : Block to validate and execute. pre_state : Pre-execution state provider. chain_context : Chain context that the block may need during execution.

Returns

block_diff : BlockDiff Account, storage, and code changes produced by block execution.

def execute_block(block: Block, ​​pre_state: State, ​​chain_context: ChainContext) -> BlockDiff:
277
    """
278
    Execute a block and validate the resulting roots against the header.
279
280
    This method is idempotent.
281
282
    Parameters
283
    ----------
284
    block :
285
        Block to validate and execute.
286
    pre_state :
287
        Pre-execution state provider.
288
    chain_context :
289
        Chain context that the block may need during execution.
290
291
    Returns
292
    -------
293
    block_diff : `BlockDiff`
294
        Account, storage, and code changes produced by block execution.
295
296
    """
297
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
298
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
299
300
    parent_header = chain_context.parent_header
301
    validate_header(parent_header, block.header)
302
303
    if block.ommers != ():
304
        raise InvalidBlock
305
306
    block_state = BlockState(pre_state=pre_state)
307
308
    block_env = vm.BlockEnvironment(
309
        chain_id=chain_context.chain_id,
310
        state=block_state,
311
        block_gas_limit=block.header.gas_limit,
312
        block_hashes=chain_context.block_hashes,
313
        coinbase=block.header.coinbase,
314
        number=block.header.number,
315
        base_fee_per_gas=block.header.base_fee_per_gas,
316
        time=block.header.timestamp,
317
        prev_randao=block.header.prev_randao,
318
        excess_blob_gas=block.header.excess_blob_gas,
319
        parent_beacon_block_root=block.header.parent_beacon_block_root,
320
        block_access_list_builder=BlockAccessListBuilder(),
321
        slot_number=block.header.slot_number,
322
    )
323
324
    block_output = apply_body(
325
        block_env=block_env,
326
        transactions=block.transactions,
327
        withdrawals=block.withdrawals,
328
    )
329
    block_diff = extract_block_diff(block_state)
330
    block_state_root, _ = pre_state.compute_state_root_and_trie_changes(
331
        block_diff.account_changes, block_diff.storage_changes
332
    )
333
    transactions_root = root(block_output.transactions_trie)
334
    receipt_root = root(block_output.receipts_trie)
335
    block_logs_bloom = logs_bloom(block_output.block_logs)
336
    withdrawals_root = root(block_output.withdrawals_trie)
337
    requests_hash = compute_requests_hash(block_output.requests)
338
    computed_block_access_list_hash = hash_block_access_list(
339
        block_output.block_access_list
340
    )
341
342
    block_gas_used = max(
343
        block_output.block_gas_used,
344
        block_output.block_state_gas_used,
345
    )
346
    if block_gas_used != block.header.gas_used:
347
        raise InvalidBlock(f"{block_gas_used} != {block.header.gas_used}")
348
    if transactions_root != block.header.transactions_root:
349
        raise InvalidBlock
350
    if block_state_root != block.header.state_root:
351
        raise InvalidBlock
352
    if receipt_root != block.header.receipt_root:
353
        raise InvalidBlock
354
    if block_logs_bloom != block.header.bloom:
355
        raise InvalidBlock
356
    if withdrawals_root != block.header.withdrawals_root:
357
        raise InvalidBlock
358
    if block_output.blob_gas_used != block.header.blob_gas_used:
359
        raise InvalidBlock
360
    if requests_hash != block.header.requests_hash:
361
        raise InvalidBlock
362
    if computed_block_access_list_hash != block.header.block_access_list_hash:
363
        raise InvalidBlock("Invalid block access list hash")
364
365
    return block_diff

calculate_base_fee_per_gas

Calculates the base fee per gas for the block.

Parameters

block_gas_limit : Gas limit of the block for which the base fee is being calculated. parent_gas_limit : Gas limit of the parent block. parent_gas_used : Gas used in the parent block. parent_base_fee_per_gas : Base fee per gas of the parent block.

Returns

base_fee_per_gas : Uint Base fee per gas for the block.

def calculate_base_fee_per_gas(block_gas_limit: Uint, ​​parent_gas_limit: Uint, ​​parent_gas_used: Uint, ​​parent_base_fee_per_gas: Uint) -> Uint:
374
    """
375
    Calculates the base fee per gas for the block.
376
377
    Parameters
378
    ----------
379
    block_gas_limit :
380
        Gas limit of the block for which the base fee is being calculated.
381
    parent_gas_limit :
382
        Gas limit of the parent block.
383
    parent_gas_used :
384
        Gas used in the parent block.
385
    parent_base_fee_per_gas :
386
        Base fee per gas of the parent block.
387
388
    Returns
389
    -------
390
    base_fee_per_gas : `Uint`
391
        Base fee per gas for the block.
392
393
    """
394
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
395
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
396
        raise InvalidBlock
397
398
    if parent_gas_used == parent_gas_target:
399
        expected_base_fee_per_gas = parent_base_fee_per_gas
400
    elif parent_gas_used > parent_gas_target:
401
        gas_used_delta = parent_gas_used - parent_gas_target
402
403
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
404
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
405
406
        base_fee_per_gas_delta = max(
407
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
408
            Uint(1),
409
        )
410
411
        expected_base_fee_per_gas = (
412
            parent_base_fee_per_gas + base_fee_per_gas_delta
413
        )
414
    else:
415
        gas_used_delta = parent_gas_target - parent_gas_used
416
417
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
418
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
419
420
        base_fee_per_gas_delta = (
421
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
422
        )
423
424
        expected_base_fee_per_gas = (
425
            parent_base_fee_per_gas - base_fee_per_gas_delta
426
        )
427
428
    return Uint(expected_base_fee_per_gas)

validate_header

Verify a block header against its parent.

In order to consider a block's header valid, the logic for the quantities in the header should match the logic for the block itself. For example the header timestamp should be greater than the block's parent timestamp because the block was created after the parent block. Additionally, the block's number should be directly following the parent block's number since it is the next block in the sequence.

Parameters

parent_header : Header of the parent block. header : Header to check for correctness.

def validate_header(parent_header: Header | PreviousHeader, ​​header: Header) -> None:
434
    """
435
    Verify a block header against its parent.
436
437
    In order to consider a block's header valid, the logic for the
438
    quantities in the header should match the logic for the block itself.
439
    For example the header timestamp should be greater than the block's parent
440
    timestamp because the block was created *after* the parent block.
441
    Additionally, the block's number should be directly following the parent
442
    block's number since it is the next block in the sequence.
443
444
    Parameters
445
    ----------
446
    parent_header :
447
        Header of the parent block.
448
    header :
449
        Header to check for correctness.
450
451
    """
452
    if header.number < Uint(1):
453
        raise InvalidBlock
454
455
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
456
    if header.excess_blob_gas != excess_blob_gas:
457
        raise InvalidBlock
458
459
    if header.gas_used > header.gas_limit:
460
        raise InvalidBlock
461
462
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
463
        header.gas_limit,
464
        parent_header.gas_limit,
465
        parent_header.gas_used,
466
        parent_header.base_fee_per_gas,
467
    )
468
    if expected_base_fee_per_gas != header.base_fee_per_gas:
469
        raise InvalidBlock
470
    if header.timestamp <= parent_header.timestamp:
471
        raise InvalidBlock
472
    if header.number != parent_header.number + Uint(1):
473
        raise InvalidBlock
474
    if len(header.extra_data) > 32:
475
        raise InvalidBlock
476
    if header.difficulty != 0:
477
        raise InvalidBlock
478
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
479
        raise InvalidBlock
480
    if header.ommers_hash != EMPTY_OMMER_HASH:
481
        raise InvalidBlock
482
483
    block_parent_hash = keccak256(rlp.encode(parent_header))
484
    if header.parent_hash != block_parent_hash:
485
        raise InvalidBlock

check_transaction

Check if the transaction is includable in the block.

Parameters

block_env : The block scoped environment. block_output : The block output for the current block. tx : The transaction. tx_state : The transaction state tracker. intrinsic : The transaction's intrinsic gas cost, split into regular and state components.

Returns

sender_address : The sender of the transaction. effective_gas_price : The price to charge for gas when the transaction is executed. blob_versioned_hashes : The blob versioned hashes of the transaction. tx_blob_gas_used: The blob gas used by the transaction.

Raises

InvalidBlock : If the transaction is not includable. GasUsedExceedsLimitError : If the gas used by the transaction exceeds the block's gas limit. NonceMismatchError : If the nonce of the transaction is not equal to the sender's nonce. InsufficientBalanceError : If the sender's balance is not enough to pay for the transaction. InvalidSenderError : If the transaction is from an address that does not exist anymore. PriorityFeeGreaterThanMaxFeeError : If the priority fee is greater than the maximum fee per gas. InsufficientMaxFeePerGasError : If the maximum fee per gas is insufficient for the transaction. InsufficientMaxFeePerBlobGasError : If the maximum fee per blob gas is insufficient for the transaction. BlobGasLimitExceededError : If the blob gas used by the transaction exceeds the block's blob gas limit. InvalidBlobVersionedHashError : If the transaction contains a blob versioned hash with an invalid version. NoBlobDataError : If the transaction is a type 3 but has no blobs. BlobCountExceededError : If the transaction is a type 3 and has more blobs than the limit. TransactionTypeContractCreationError: If the transaction type is not allowed to create contracts. EmptyAuthorizationListError : If the transaction is a SetCodeTransaction and the authorization list is empty.

def check_transaction(block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.amsterdam.vm.BlockOutput, ​​tx: Transaction, ​​tx_state: TransactionState, ​​intrinsic: IntrinsicGasCost) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
495
    """
496
    Check if the transaction is includable in the block.
497
498
    Parameters
499
    ----------
500
    block_env :
501
        The block scoped environment.
502
    block_output :
503
        The block output for the current block.
504
    tx :
505
        The transaction.
506
    tx_state :
507
        The transaction state tracker.
508
    intrinsic :
509
        The transaction's intrinsic gas cost, split into regular and
510
        state components.
511
512
    Returns
513
    -------
514
    sender_address :
515
        The sender of the transaction.
516
    effective_gas_price :
517
        The price to charge for gas when the transaction is executed.
518
    blob_versioned_hashes :
519
        The blob versioned hashes of the transaction.
520
    tx_blob_gas_used:
521
        The blob gas used by the transaction.
522
523
    Raises
524
    ------
525
    InvalidBlock :
526
        If the transaction is not includable.
527
    GasUsedExceedsLimitError :
528
        If the gas used by the transaction exceeds the block's gas limit.
529
    NonceMismatchError :
530
        If the nonce of the transaction is not equal to the sender's nonce.
531
    InsufficientBalanceError :
532
        If the sender's balance is not enough to pay for the transaction.
533
    InvalidSenderError :
534
        If the transaction is from an address that does not exist anymore.
535
    PriorityFeeGreaterThanMaxFeeError :
536
        If the priority fee is greater than the maximum fee per gas.
537
    InsufficientMaxFeePerGasError :
538
        If the maximum fee per gas is insufficient for the transaction.
539
    InsufficientMaxFeePerBlobGasError :
540
        If the maximum fee per blob gas is insufficient for the transaction.
541
    BlobGasLimitExceededError :
542
        If the blob gas used by the transaction exceeds the block's blob gas
543
        limit.
544
    InvalidBlobVersionedHashError :
545
        If the transaction contains a blob versioned hash with an invalid
546
        version.
547
    NoBlobDataError :
548
        If the transaction is a type 3 but has no blobs.
549
    BlobCountExceededError :
550
        If the transaction is a type 3 and has more blobs than the limit.
551
    TransactionTypeContractCreationError:
552
        If the transaction type is not allowed to create contracts.
553
    EmptyAuthorizationListError :
554
        If the transaction is a SetCodeTransaction and the authorization list
555
        is empty.
556
557
    """
558
    # Per-tx 2D gas inclusion check: for each dimension the worst-case
559
    # contribution must fit in the remaining budget.  Block-end
560
    # validation still enforces
561
    # max(block_regular_gas_used, block_state_gas_used) <= gas_limit.
562
    regular_gas_available = (
563
        block_env.block_gas_limit - block_output.block_gas_used
564
    )
565
    state_gas_available = (
566
        block_env.block_gas_limit - block_output.block_state_gas_used
567
    )
568
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
569
570
    # Worst-case regular contribution: tx.gas minus the portion that
571
    # must go to intrinsic state gas, capped at TX_MAX_GAS_LIMIT.
572
    if min(TX_MAX_GAS_LIMIT, tx.gas - intrinsic.state) > (
573
        regular_gas_available
574
    ):
575
        raise GasUsedExceedsLimitError("gas used exceeds limit")
576
577
    # Worst-case state contribution: tx.gas minus the portion that
578
    # must go to intrinsic regular gas.
579
    if tx.gas - intrinsic.regular > state_gas_available:
580
        raise GasUsedExceedsLimitError("gas used exceeds limit")
581
582
    tx_blob_gas_used = calculate_total_blob_gas(tx)
583
    if tx_blob_gas_used > blob_gas_available:
584
        raise BlobGasLimitExceededError("blob gas limit exceeded")
585
586
    sender_address = recover_sender(block_env.chain_id, tx)
587
    sender_account = get_account(tx_state, sender_address)
588
589
    if isinstance(
590
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
591
    ):
592
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
593
            raise PriorityFeeGreaterThanMaxFeeError(
594
                "priority fee greater than max fee"
595
            )
596
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
597
            raise InsufficientMaxFeePerGasError(
598
                tx.max_fee_per_gas, block_env.base_fee_per_gas
599
            )
600
601
        priority_fee_per_gas = min(
602
            tx.max_priority_fee_per_gas,
603
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
604
        )
605
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
606
        max_gas_fee = tx.gas * tx.max_fee_per_gas
607
    else:
608
        if tx.gas_price < block_env.base_fee_per_gas:
609
            raise InvalidBlock
610
        effective_gas_price = tx.gas_price
611
        max_gas_fee = tx.gas * tx.gas_price
612
613
    if isinstance(tx, BlobTransaction):
614
        blob_count = len(tx.blob_versioned_hashes)
615
        if blob_count == 0:
616
            raise NoBlobDataError("no blob data in transaction")
617
        if blob_count > BLOB_COUNT_LIMIT:
618
            raise BlobCountExceededError(
619
                f"Tx has {blob_count} blobs. Max allowed: {BLOB_COUNT_LIMIT}"
620
            )
621
        for blob_versioned_hash in tx.blob_versioned_hashes:
622
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
623
                raise InvalidBlobVersionedHashError(
624
                    "invalid blob versioned hash"
625
                )
626
627
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
628
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
629
            raise InsufficientMaxFeePerBlobGasError(
630
                "insufficient max fee per blob gas"
631
            )
632
633
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
634
            tx.max_fee_per_blob_gas
635
        )
636
        blob_versioned_hashes = tx.blob_versioned_hashes
637
    else:
638
        blob_versioned_hashes = ()
639
640
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
641
        if not isinstance(tx.to, Address):
642
            raise TransactionTypeContractCreationError(tx)
643
644
    if isinstance(tx, SetCodeTransaction):
645
        if not any(tx.authorizations):
646
            raise EmptyAuthorizationListError("empty authorization list")
647
648
    if sender_account.nonce > Uint(tx.nonce):
649
        raise NonceMismatchError("nonce too low")
650
    elif sender_account.nonce < Uint(tx.nonce):
651
        raise NonceMismatchError("nonce too high")
652
653
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
654
        raise InsufficientBalanceError("insufficient sender balance")
655
    sender_code = get_code(tx_state, sender_account.code_hash)
656
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
657
        sender_code
658
    ):
659
        raise InvalidSenderError("not EOA")
660
661
    return (
662
        sender_address,
663
        effective_gas_price,
664
        blob_versioned_hashes,
665
        tx_blob_gas_used,
666
    )

make_receipt

Make the receipt for a transaction that was executed.

Parameters

tx : The executed transaction. error : Error in the top level frame of the transaction, if any. cumulative_gas_used : The total gas used so far in the block after the transaction was executed. This is the gas used after refunds. logs : The logs produced by the transaction.

Returns

receipt : The receipt for the transaction.

def make_receipt(tx: Transaction, ​​error: Optional[EthereumException], ​​cumulative_gas_used: Uint, ​​logs: Tuple[Log, ...]) -> Bytes | Receipt:
675
    """
676
    Make the receipt for a transaction that was executed.
677
678
    Parameters
679
    ----------
680
    tx :
681
        The executed transaction.
682
    error :
683
        Error in the top level frame of the transaction, if any.
684
    cumulative_gas_used :
685
        The total gas used so far in the block after the transaction was
686
        executed. This is the gas used after refunds.
687
    logs :
688
        The logs produced by the transaction.
689
690
    Returns
691
    -------
692
    receipt :
693
        The receipt for the transaction.
694
695
    """
696
    receipt = Receipt(
697
        succeeded=error is None,
698
        cumulative_gas_used=cumulative_gas_used,
699
        bloom=logs_bloom(logs),
700
        logs=logs,
701
    )
702
703
    return encode_receipt(tx, receipt)

process_checked_system_transaction

Process a system transaction and raise an error if the contract does not contain code or if the transaction fails.

Parameters

block_env : The block scoped environment. target_address : Address of the contract to call. data : Data to pass to the contract.

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

def process_checked_system_transaction(block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
711
    """
712
    Process a system transaction and raise an error if the contract does not
713
    contain code or if the transaction fails.
714
715
    Parameters
716
    ----------
717
    block_env :
718
        The block scoped environment.
719
    target_address :
720
        Address of the contract to call.
721
    data :
722
        Data to pass to the contract.
723
724
    Returns
725
    -------
726
    system_tx_output : `MessageCallOutput`
727
        Output of processing the system transaction.
728
729
    """
730
    # Read through BlockState (not pre-state) so that a system contract
731
    # deployed by an earlier transaction in the same block is visible.
732
    # See EIP-7002 and EIP-7251 for this edge case.
733
    #
734
    # This read is not recorded in the state tracker.
735
    # However, this is fine because `process_unchecked_system_transaction`
736
    # does its own get_account on the TransactionState that we do incorporate
737
    # into BlockState.
738
    untracked_state = TransactionState(parent=block_env.state)
739
    system_contract_code = get_code(
740
        untracked_state,
741
        get_account(untracked_state, target_address).code_hash,
742
    )
743
744
    if len(system_contract_code) == 0:
745
        raise InvalidBlock(
746
            f"System contract address {target_address.hex()} does not "
747
            "contain code"
748
        )
749
750
    system_tx_output = process_unchecked_system_transaction(
751
        block_env,
752
        target_address,
753
        data,
754
    )
755
756
    if system_tx_output.error:
757
        raise InvalidBlock(
758
            f"System contract ({target_address.hex()}) call failed: "
759
            f"{system_tx_output.error}"
760
        )
761
762
    return system_tx_output

process_unchecked_system_transaction

Process a system transaction without checking if the contract contains code or if the transaction fails.

Parameters

block_env : The block scoped environment. target_address : Address of the contract to call. data : Data to pass to the contract.

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

def process_unchecked_system_transaction(block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
770
    """
771
    Process a system transaction without checking if the contract contains
772
    code or if the transaction fails.
773
774
    Parameters
775
    ----------
776
    block_env :
777
        The block scoped environment.
778
    target_address :
779
        Address of the contract to call.
780
    data :
781
        Data to pass to the contract.
782
783
    Returns
784
    -------
785
    system_tx_output : `MessageCallOutput`
786
        Output of processing the system transaction.
787
788
    """
789
    system_tx_state = TransactionState(parent=block_env.state)
790
    system_contract_code = get_code(
791
        system_tx_state,
792
        get_account(system_tx_state, target_address).code_hash,
793
    )
794
795
    tx_env = vm.TransactionEnvironment(
796
        origin=SYSTEM_ADDRESS,
797
        gas_price=block_env.base_fee_per_gas,
798
        gas=SYSTEM_TRANSACTION_GAS,
799
        state_gas_reservoir=Uint(0),
800
        access_list_addresses=set(),
801
        access_list_storage_keys=set(),
802
        state=system_tx_state,
803
        blob_versioned_hashes=(),
804
        authorizations=(),
805
        index_in_block=None,
806
        tx_hash=None,
807
        intrinsic_regular_gas=Uint(0),
808
        intrinsic_state_gas=Uint(0),
809
    )
810
811
    system_tx_message = Message(
812
        block_env=block_env,
813
        tx_env=tx_env,
814
        caller=SYSTEM_ADDRESS,
815
        target=target_address,
816
        gas=SYSTEM_TRANSACTION_GAS,
817
        state_gas_reservoir=Uint(0),
818
        value=U256(0),
819
        data=data,
820
        code=system_contract_code,
821
        depth=Uint(0),
822
        current_target=target_address,
823
        code_address=target_address,
824
        should_transfer_value=False,
825
        is_static=False,
826
        accessed_addresses=set(),
827
        accessed_storage_keys=set(),
828
        disable_precompiles=False,
829
        parent_evm=None,
830
    )
831
832
    system_tx_output = process_message_call(system_tx_message)
833
834
    incorporate_tx_into_block(
835
        system_tx_state, block_env.block_access_list_builder
836
    )
837
838
    return system_tx_output

apply_body

Executes a block.

Many of the contents of a block are stored in data structures called tries. There is a transactions trie which is similar to a ledger of the transactions stored in the current block. There is also a receipts trie which stores the results of executing a transaction, like the post state and gas used. This function creates and executes the block that is to be added to the chain.

Parameters

block_env : The block scoped environment. transactions : Transactions included in the block. withdrawals : Withdrawals to be processed in the current block.

Returns

block_output : The block output for the current block.

def apply_body(block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.forks.amsterdam.vm.BlockOutput:
846
    """
847
    Executes a block.
848
849
    Many of the contents of a block are stored in data structures called
850
    tries. There is a transactions trie which is similar to a ledger of the
851
    transactions stored in the current block. There is also a receipts trie
852
    which stores the results of executing a transaction, like the post state
853
    and gas used. This function creates and executes the block that is to be
854
    added to the chain.
855
856
    Parameters
857
    ----------
858
    block_env :
859
        The block scoped environment.
860
    transactions :
861
        Transactions included in the block.
862
    withdrawals :
863
        Withdrawals to be processed in the current block.
864
865
    Returns
866
    -------
867
    block_output :
868
        The block output for the current block.
869
870
    """
871
    block_output = vm.BlockOutput()
872
873
    process_unchecked_system_transaction(
874
        block_env=block_env,
875
        target_address=BEACON_ROOTS_ADDRESS,
876
        data=block_env.parent_beacon_block_root,
877
    )
878
879
    process_unchecked_system_transaction(
880
        block_env=block_env,
881
        target_address=HISTORY_STORAGE_ADDRESS,
882
        data=block_env.block_hashes[-1],  # The parent hash
883
    )
884
885
    for i, tx in enumerate(map(decode_transaction, transactions)):
886
        process_transaction(block_env, block_output, tx, Uint(i))
887
888
    # EIP-7928: Post-execution operations use index N+1
889
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
890
        ulen(transactions) + Uint(1)
891
    )
892
893
    process_withdrawals(block_env, block_output, withdrawals)
894
895
    process_general_purpose_requests(
896
        block_env=block_env,
897
        block_output=block_output,
898
    )
899
900
    block_output.block_access_list = build_block_access_list(
901
        block_env.block_access_list_builder, block_env.state
902
    )
903
904
    # Validate block access list gas limit constraint (EIP-7928)
905
    validate_block_access_list_gas_limit(
906
        block_access_list=block_output.block_access_list,
907
        block_gas_limit=block_env.block_gas_limit,
908
    )
909
910
    return block_output

process_general_purpose_requests

Process all the requests in the block.

Parameters

block_env : The execution environment for the Block. block_output : The block output for the current block.

def process_general_purpose_requests(block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.amsterdam.vm.BlockOutput) -> None:
917
    """
918
    Process all the requests in the block.
919
920
    Parameters
921
    ----------
922
    block_env :
923
        The execution environment for the Block.
924
    block_output :
925
        The block output for the current block.
926
927
    """
928
    # Requests are to be in ascending order of request type
929
    deposit_requests = parse_deposit_requests(block_output)
930
    requests_from_execution = block_output.requests
931
    if len(deposit_requests) > 0:
932
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
933
934
    system_withdrawal_tx_output = process_checked_system_transaction(
935
        block_env=block_env,
936
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
937
        data=b"",
938
    )
939
940
    if len(system_withdrawal_tx_output.return_data) > 0:
941
        requests_from_execution.append(
942
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
943
        )
944
945
    system_consolidation_tx_output = process_checked_system_transaction(
946
        block_env=block_env,
947
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
948
        data=b"",
949
    )
950
951
    if len(system_consolidation_tx_output.return_data) > 0:
952
        requests_from_execution.append(
953
            CONSOLIDATION_REQUEST_TYPE
954
            + system_consolidation_tx_output.return_data
955
        )

process_transaction

Execute a transaction against the provided environment.

This function processes the actions needed to execute a transaction. It decrements the sender's account balance after calculating the gas fee and refunds them the proper amount after execution. Calling contracts, deploying code, and incrementing nonces are all examples of actions that happen within this function or from a call made within this function.

Accounts that are marked for deletion are processed and destroyed after execution.

Parameters

block_env : Environment for the Ethereum Virtual Machine. block_output : The block output for the current block. tx : Transaction to execute. index: Index of the transaction in the block.

def process_transaction(block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.amsterdam.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
964
    """
965
    Execute a transaction against the provided environment.
966
967
    This function processes the actions needed to execute a transaction.
968
    It decrements the sender's account balance after calculating the gas fee
969
    and refunds them the proper amount after execution. Calling contracts,
970
    deploying code, and incrementing nonces are all examples of actions that
971
    happen within this function or from a call made within this function.
972
973
    Accounts that are marked for deletion are processed and destroyed after
974
    execution.
975
976
    Parameters
977
    ----------
978
    block_env :
979
        Environment for the Ethereum Virtual Machine.
980
    block_output :
981
        The block output for the current block.
982
    tx :
983
        Transaction to execute.
984
    index:
985
        Index of the transaction in the block.
986
987
    """
988
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
989
        index + Uint(1)
990
    )
991
    tx_state = TransactionState(parent=block_env.state)
992
993
    trie_set(
994
        block_output.transactions_trie,
995
        rlp.encode(index),
996
        encode_transaction(tx),
997
    )
998
999
    intrinsic = validate_transaction(tx)
1000
1001
    intrinsic_gas = intrinsic.regular + intrinsic.state
1002
1003
    (
1004
        sender,
1005
        effective_gas_price,
1006
        blob_versioned_hashes,
1007
        tx_blob_gas_used,
1008
    ) = check_transaction(
1009
        block_env=block_env,
1010
        block_output=block_output,
1011
        tx=tx,
1012
        tx_state=tx_state,
1013
        intrinsic=intrinsic,
1014
    )
1015
1016
    sender_account = get_account(tx_state, sender)
1017
1018
    if isinstance(tx, BlobTransaction):
1019
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
1020
    else:
1021
        blob_gas_fee = Uint(0)
1022
1023
    effective_gas_fee = tx.gas * effective_gas_price
1024
1025
    # Split execution gas into gas_left (capped by remaining regular gas
1026
    # budget) and state_gas_reservoir.
1027
    execution_gas = tx.gas - intrinsic_gas
1028
    regular_gas_budget = TX_MAX_GAS_LIMIT - intrinsic.regular
1029
    gas = min(regular_gas_budget, execution_gas)
1030
    state_gas_reservoir = Uint(execution_gas - gas)
1031
1032
    increment_nonce(tx_state, sender)
1033
1034
    sender_balance_after_gas_fee = (
1035
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
1036
    )
1037
    set_account_balance(tx_state, sender, U256(sender_balance_after_gas_fee))
1038
1039
    access_list_addresses = set()
1040
    access_list_storage_keys = set()
1041
    access_list_addresses.add(block_env.coinbase)
1042
    if has_access_list(tx):
1043
        for access in tx.access_list:
1044
            access_list_addresses.add(access.account)
1045
            for slot in access.slots:
1046
                access_list_storage_keys.add((access.account, slot))
1047
1048
    authorizations: Tuple[Authorization, ...] = ()
1049
    if isinstance(tx, SetCodeTransaction):
1050
        authorizations = tx.authorizations
1051
1052
    tx_env = vm.TransactionEnvironment(
1053
        origin=sender,
1054
        gas_price=effective_gas_price,
1055
        gas=gas,
1056
        state_gas_reservoir=state_gas_reservoir,
1057
        access_list_addresses=access_list_addresses,
1058
        access_list_storage_keys=access_list_storage_keys,
1059
        state=tx_state,
1060
        blob_versioned_hashes=blob_versioned_hashes,
1061
        authorizations=authorizations,
1062
        index_in_block=index,
1063
        tx_hash=get_transaction_hash(encode_transaction(tx)),
1064
        intrinsic_regular_gas=intrinsic.regular,
1065
        intrinsic_state_gas=intrinsic.state,
1066
    )
1067
1068
    message = prepare_message(
1069
        block_env,
1070
        tx_env,
1071
        tx,
1072
    )
1073
1074
    tx_output = process_message_call(message)
1075
1076
    if tx_output.error is not None:
1077
        tx_output.state_gas_left += tx_output.state_gas_used
1078
        tx_output.state_gas_used = Uint(0)
1079
    else:
1080
        # Refund state gas for accounts created and destroyed in the
1081
        # same tx (EIP-6780). Covers account, storage, and code.
1082
        for address in tx_output.accounts_to_delete:
1083
            if address in tx_state.created_accounts:
1084
                selfdestruct_refund = (
1085
                    STATE_BYTES_PER_NEW_ACCOUNT * COST_PER_STATE_BYTE
1086
                )
1087
                storage = tx_state.storage_writes.get(address, {})
1088
                created_slots = sum(1 for v in storage.values() if v != 0)
1089
                selfdestruct_refund += (
1090
                    Uint(created_slots)
1091
                    * STATE_BYTES_PER_STORAGE_SET
1092
                    * COST_PER_STATE_BYTE
1093
                )
1094
                # EIP-6780 defers account/storage/code removal to
1095
                # tx-end, so `account.code_hash` still points at the
1096
                # deployed code here and `get_code` returns it
1097
                # pre-deletion.
1098
                account = get_account(tx_state, address)
1099
                code = get_code(tx_state, account.code_hash)
1100
                selfdestruct_refund += ulen(code) * COST_PER_STATE_BYTE
1101
                selfdestruct_refund = min(
1102
                    selfdestruct_refund, tx_output.state_gas_used
1103
                )
1104
                tx_output.state_gas_left += selfdestruct_refund
1105
                tx_output.state_gas_used -= selfdestruct_refund
1106
1107
    tx_gas_used_before_refund = (
1108
        tx.gas - tx_output.gas_left - tx_output.state_gas_left
1109
    )
1110
    tx_gas_refund = min(
1111
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
1112
    )
1113
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
1114
1115
    # Transactions with less execution_gas_used than the floor pay at the
1116
    # floor cost.
1117
    tx_gas_used_after_refund = max(
1118
        tx_gas_used_after_refund, intrinsic.calldata_floor
1119
    )
1120
1121
    tx_gas_left = tx.gas - tx_gas_used_after_refund
1122
    gas_refund_amount = tx_gas_left * effective_gas_price
1123
1124
    # For non-1559 transactions effective_gas_price == tx.gas_price
1125
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
1126
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
1127
1128
    # refund gas
1129
    sender_balance_after_refund = get_account(tx_state, sender).balance + U256(
1130
        gas_refund_amount
1131
    )
1132
    set_account_balance(tx_state, sender, sender_balance_after_refund)
1133
1134
    # transfer miner fees
1135
    coinbase_balance_after_mining_fee = get_account(
1136
        tx_state, block_env.coinbase
1137
    ).balance + U256(transaction_fee)
1138
1139
    set_account_balance(
1140
        tx_state, block_env.coinbase, coinbase_balance_after_mining_fee
1141
    )
1142
1143
    # EIP-7708: Emit burn logs for balances held by accounts marked for
1144
    # deletion AFTER miner fee transfer.
1145
    finalization_logs: List[Log] = []
1146
    for address in sorted(tx_output.accounts_to_delete):
1147
        balance = get_account(tx_state, address).balance
1148
        if balance > U256(0):
1149
            padded_address = left_pad_zero_bytes(address, 32)
1150
            finalization_logs.append(
1151
                Log(
1152
                    address=vm.SYSTEM_ADDRESS,
1153
                    topics=(
1154
                        vm.BURN_TOPIC,
1155
                        Hash32(padded_address),
1156
                    ),
1157
                    data=balance.to_be_bytes32(),
1158
                )
1159
            )
1160
1161
    all_logs = tx_output.logs + tuple(finalization_logs)
1162
1163
    if coinbase_balance_after_mining_fee == 0 and account_exists_and_is_empty(
1164
        tx_state, block_env.coinbase
1165
    ):
1166
        destroy_account(tx_state, block_env.coinbase)
1167
1168
    tx_regular_gas = tx_env.intrinsic_regular_gas + tx_output.regular_gas_used
1169
    tx_state_gas = tx_env.intrinsic_state_gas + tx_output.state_gas_used
1170
    block_output.block_gas_used += max(
1171
        tx_regular_gas, intrinsic.calldata_floor
1172
    )
1173
    block_output.block_state_gas_used += tx_state_gas
1174
    block_output.blob_gas_used += tx_blob_gas_used
1175
1176
    block_output.cumulative_gas_used += tx_gas_used_after_refund
1177
    receipt = make_receipt(
1178
        tx,
1179
        tx_output.error,
1180
        block_output.cumulative_gas_used,
1181
        all_logs,
1182
    )
1183
1184
    receipt_key = rlp.encode(Uint(index))
1185
    block_output.receipt_keys += (receipt_key,)
1186
1187
    trie_set(
1188
        block_output.receipts_trie,
1189
        receipt_key,
1190
        receipt,
1191
    )
1192
1193
    block_output.block_logs += all_logs
1194
1195
    for address in tx_output.accounts_to_delete:
1196
        destroy_account(tx_state, address)
1197
1198
    incorporate_tx_into_block(tx_state, block_env.block_access_list_builder)

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.amsterdam.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
1206
    """
1207
    Increase the balance of the withdrawing account.
1208
    """
1209
    wd_state = TransactionState(parent=block_env.state)
1210
1211
    for i, wd in enumerate(withdrawals):
1212
        trie_set(
1213
            block_output.withdrawals_trie,
1214
            rlp.encode(Uint(i)),
1215
            rlp.encode(wd),
1216
        )
1217
1218
        current_balance = get_account(wd_state, wd.address).balance
1219
        new_balance = current_balance + wd.amount * GWEI_TO_WEI
1220
        set_account_balance(wd_state, wd.address, new_balance)
1221
1222
    incorporate_tx_into_block(wd_state, block_env.block_access_list_builder)

check_gas_limit

Validates the gas limit for a block.

The bounds of the gas limit, max_adjustment_delta, is set as the quotient of the parent block's gas limit and the LIMIT_ADJUSTMENT_FACTOR. Therefore, if the gas limit that is passed through as a parameter is greater than or equal to the sum of the parent's gas and the adjustment delta then the limit for gas is too high and fails this function's check. Similarly, if the limit is less than or equal to the difference of the parent's gas and the adjustment delta or the predefined LIMIT_MINIMUM then this function's check fails because the gas limit doesn't allow for a sufficient or reasonable amount of gas to be used on a block.

Parameters

gas_limit : Gas limit to validate.

parent_gas_limit : Gas limit of the parent block.

Returns

check : bool True if gas limit constraints are satisfied, False otherwise.

def check_gas_limit(gas_limit: Uint, ​​parent_gas_limit: Uint) -> bool:
1226
    """
1227
    Validates the gas limit for a block.
1228
1229
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1230
    quotient of the parent block's gas limit and the
1231
    ``LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is passed
1232
    through as a parameter is greater than or equal to the *sum* of the
1233
    parent's gas and the adjustment delta then the limit for gas is too high
1234
    and fails this function's check. Similarly, if the limit is less than or
1235
    equal to the *difference* of the parent's gas and the adjustment delta *or*
1236
    the predefined ``LIMIT_MINIMUM`` then this function's check fails because
1237
    the gas limit doesn't allow for a sufficient or reasonable amount of gas to
1238
    be used on a block.
1239
1240
    Parameters
1241
    ----------
1242
    gas_limit :
1243
        Gas limit to validate.
1244
1245
    parent_gas_limit :
1246
        Gas limit of the parent block.
1247
1248
    Returns
1249
    -------
1250
    check : `bool`
1251
        True if gas limit constraints are satisfied, False otherwise.
1252
1253
    """
1254
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
1255
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1256
        return False
1257
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1258
        return False
1259
    if gas_limit < GasCosts.LIMIT_MINIMUM:
1260
        return False
1261
1262
    return True