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

100
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

101
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

103
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

104
BEACON_ROOTS_ADDRESS = hex_to_address(
105
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
106
)

SYSTEM_TRANSACTION_GAS

107
SYSTEM_TRANSACTION_GAS = ExecutionGas(Uint(30000000))

SYSTEM_MAX_SSTORES_PER_CALL

Upper bound on the number of new storage slots a single system call is expected to write.

108
SYSTEM_MAX_SSTORES_PER_CALL = Uint(16)

GWEI_TO_WEI

113
GWEI_TO_WEI = U256(10**9)

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

115
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
116
    "0x00000961Ef480Eb55e80D19ad83579A64c007002"
117
)

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

118
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
119
    "0x0000BBdDc7CE488642fb579F8B00f3a590007251"
120
)

BUILDER_DEPOSIT_CONTRACT_ADDRESS

121
BUILDER_DEPOSIT_CONTRACT_ADDRESS = hex_to_address(
122
    "0x0000BFF46984E3725691FA540A8C7589300D8282"
123
)

BUILDER_EXIT_CONTRACT_ADDRESS

124
BUILDER_EXIT_CONTRACT_ADDRESS = hex_to_address(
125
    "0x000064D678505AD48F8CCB093BC65613800E8282"
126
)

HISTORY_STORAGE_ADDRESS

127
HISTORY_STORAGE_ADDRESS = hex_to_address(
128
    "0x0000F90827F1C53a10cb7A02335B175320002935"
129
)

MAX_BLOCK_SIZE

130
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

131
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

132
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

ChainContext

Chain context needed for block execution.

135
@final
136
@slotted_freezable
137
@dataclass
class ChainContext:

chain_id

Identify the chain for transaction signature recovery.

143
    chain_id: U64

block_hashes

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

146
    block_hashes: List[Hash32]

parent_header

Parent header used for header validation and system contracts.

149
    parent_header: Header | PreviousHeader

BlockChain

History and current state of the block chain.

153
@final
154
@dataclass
class BlockChain:

blocks

160
    blocks: List[Block]

state

161
    state: State

chain_id

162
    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:
166
    <snip>
185
    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]:
189
    <snip>
209
    recent_blocks = chain.blocks[-255:]
210
    if len(recent_blocks) == 0:
211
        return []
212
213
    recent_block_hashes = []
214
215
    for block in recent_blocks:
216
        prev_block_hash = block.header.parent_hash
217
        recent_block_hashes.append(prev_block_hash)
218
219
    # We are computing the hash only for the most recent block and not for
220
    # the rest of the blocks as they have successors which have the hash of
221
    # the current block as parent hash.
222
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
223
    recent_block_hashes.append(most_recent_block_hash)
224
225
    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:
229
    <snip>
251
    chain_context = ChainContext(
252
        chain_id=chain.chain_id,
253
        block_hashes=get_last_256_block_hashes(chain),
254
        parent_header=chain.blocks[-1].header,
255
    )
256
257
    block_diff = execute_block(block, chain.state, chain_context)
258
259
    apply_changes_to_state(chain.state, block_diff)
260
    chain.blocks.append(block)
261
    if len(chain.blocks) > 255:
262
        # Real clients have to store more blocks to deal with reorgs, but the
263
        # protocol only requires the last 255
264
        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:
272
    <snip>
292
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
293
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
294
295
    parent_header = chain_context.parent_header
296
    validate_header(parent_header, block.header)
297
298
    if block.ommers != ():
299
        raise InvalidBlock
300
301
    block_state = BlockState(pre_state=pre_state)
302
303
    block_env = vm.BlockEnvironment(
304
        chain_id=chain_context.chain_id,
305
        state=block_state,
306
        block_gas_limit=block.header.gas_limit,
307
        block_hashes=chain_context.block_hashes,
308
        coinbase=block.header.coinbase,
309
        number=block.header.number,
310
        base_fee_per_gas=block.header.base_fee_per_gas,
311
        time=block.header.timestamp,
312
        prev_randao=block.header.prev_randao,
313
        excess_blob_gas=block.header.excess_blob_gas,
314
        parent_beacon_block_root=block.header.parent_beacon_block_root,
315
        block_access_list_builder=BlockAccessListBuilder(),
316
        slot_number=block.header.slot_number,
317
    )
318
319
    block_output = apply_body(
320
        block_env=block_env,
321
        transactions=block.transactions,
322
        withdrawals=block.withdrawals,
323
    )
324
    block_diff = extract_block_diff(block_state)
325
    block_state_root = pre_state.compute_state_root(block_diff)
326
    transactions_root = root(block_output.transactions_trie)
327
    receipt_root = root(block_output.receipts_trie)
328
    block_logs_bloom = logs_bloom(block_output.block_logs)
329
    withdrawals_root = root(block_output.withdrawals_trie)
330
    requests_hash = compute_requests_hash(block_output.requests)
331
    computed_block_access_list_hash = hash_block_access_list(
332
        block_output.block_access_list
333
    )
334
335
    block_gas_used = max(
336
        block_output.block_gas_used,
337
        block_output.block_state_gas_used,
338
    )
339
    if block_gas_used != block.header.gas_used:
340
        raise InvalidBlock(f"{block_gas_used} != {block.header.gas_used}")
341
    if transactions_root != block.header.transactions_root:
342
        raise InvalidBlock
343
    if block_state_root != block.header.state_root:
344
        raise InvalidBlock
345
    if receipt_root != block.header.receipt_root:
346
        raise InvalidBlock
347
    if block_logs_bloom != block.header.bloom:
348
        raise InvalidBlock
349
    if withdrawals_root != block.header.withdrawals_root:
350
        raise InvalidBlock
351
    if block_output.blob_gas_used != block.header.blob_gas_used:
352
        raise InvalidBlock
353
    if requests_hash != block.header.requests_hash:
354
        raise InvalidBlock
355
    if computed_block_access_list_hash != block.header.block_access_list_hash:
356
        raise InvalidBlock("Invalid block access list hash")
357
358
    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:
367
    <snip>
387
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
388
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
389
        raise InvalidBlock
390
391
    if parent_gas_used == parent_gas_target:
392
        expected_base_fee_per_gas = parent_base_fee_per_gas
393
    elif parent_gas_used > parent_gas_target:
394
        gas_used_delta = parent_gas_used - parent_gas_target
395
396
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
397
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
398
399
        base_fee_per_gas_delta = max(
400
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
401
            Uint(1),
402
        )
403
404
        expected_base_fee_per_gas = (
405
            parent_base_fee_per_gas + base_fee_per_gas_delta
406
        )
407
    else:
408
        gas_used_delta = parent_gas_target - parent_gas_used
409
410
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
411
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
412
413
        base_fee_per_gas_delta = (
414
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
415
        )
416
417
        expected_base_fee_per_gas = (
418
            parent_base_fee_per_gas - base_fee_per_gas_delta
419
        )
420
421
    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:
427
    <snip>
445
    if header.number < Uint(1):
446
        raise InvalidBlock
447
448
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
449
    if header.excess_blob_gas != excess_blob_gas:
450
        raise InvalidBlock
451
452
    if header.gas_used > header.gas_limit:
453
        raise InvalidBlock
454
455
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
456
        header.gas_limit,
457
        parent_header.gas_limit,
458
        parent_header.gas_used,
459
        parent_header.base_fee_per_gas,
460
    )
461
    if expected_base_fee_per_gas != header.base_fee_per_gas:
462
        raise InvalidBlock
463
    if header.timestamp <= parent_header.timestamp:
464
        raise InvalidBlock
465
    if header.number != parent_header.number + Uint(1):
466
        raise InvalidBlock
467
    if len(header.extra_data) > 32:
468
        raise InvalidBlock
469
    if header.difficulty != 0:
470
        raise InvalidBlock
471
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
472
        raise InvalidBlock
473
    if header.ommers_hash != EMPTY_OMMER_HASH:
474
        raise InvalidBlock
475
476
    block_parent_hash = keccak256(rlp.encode(parent_header))
477
    if header.parent_hash != block_parent_hash:
478
        raise InvalidBlock

check_transaction

Admit a raw transaction and build its execution environment.

Recover the sender, statically validate the transaction, and check that it is includable in the block, in that order, so that a transaction invalid in several ways reports the earliest failure.

Parameters

block_env : The block scoped environment. block_output : The block output for the current block. tx : The transaction. index : The index of the current transaction.

Returns

tx_env : The environment for executing the transaction.

Raises

InvalidBlock : If the transaction is not includable. InvalidSignatureError : If the transaction's signature is invalid. InsufficientTransactionGasError : If the transaction does not provide enough gas to cover its intrinsic cost. TransactionGasLimitExceededError : If the transaction's gas limit exceeds TX_MAX_TOTAL_GAS_LIMIT. 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. 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.

def check_transaction(​block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.amsterdam.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint​) -> ethereum.forks.amsterdam.vm.TransactionEnvironment:
487
    <snip>
538
    sender = recover_sender(tx)
539
    intrinsic = validate_transaction(tx, sender)
540
    tx_state = TransactionState(parent=block_env.state)
541
542
    check_block_gas_capacity(
543
        block_env, block_output, tx.gas, calculate_total_blob_gas(tx)
544
    )
545
546
    sender_account = get_account(tx_state, sender)
547
548
    effective_gas_price = calculate_effective_gas_price(
549
        tx, block_env.base_fee_per_gas
550
    )
551
    max_gas_fee = calculate_max_gas_fee(tx, tx.gas)
552
553
    if isinstance(tx, BlobTransaction):
554
        check_max_fee_per_blob_gas(
555
            tx.blob_versioned_hashes,
556
            tx.max_fee_per_blob_gas,
557
            block_env.excess_blob_gas,
558
        )
559
560
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
561
            tx.max_fee_per_blob_gas
562
        )
563
        blob_versioned_hashes = tx.blob_versioned_hashes
564
    else:
565
        blob_versioned_hashes = ()
566
567
    check_nonce(tx, sender_account.nonce)
568
569
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
570
        raise InsufficientBalanceError("insufficient sender balance")
571
    sender_code = get_code(tx_state, sender_account.code_hash)
572
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
573
        sender_code
574
    ):
575
        raise InvalidSenderError("not EOA")
576
577
    # Split the EVM gas into an execution-gas grant (capped by the
578
    # remaining execution-gas budget) and a state gas reservoir.
579
    allocation = allocate_evm_gas(tx.gas, intrinsic)
580
581
    access_list_addresses = set()
582
    access_list_storage_keys = set()
583
    if has_access_list(tx):
584
        for access in tx.access_list:
585
            access_list_addresses.add(access.account)
586
            for slot in access.slots:
587
                access_list_storage_keys.add((access.account, slot))
588
589
    authorizations: Tuple[Authorization, ...] = ()
590
    if isinstance(tx, SetCodeTransaction):
591
        authorizations = tx.authorizations
592
593
    if isinstance(tx.to, Bytes0):
594
        is_create = True
595
        # A creation's frame runs at the address the contract
596
        # deploys to.
597
        recipient = compute_contract_address(sender, sender_account.nonce)
598
    else:
599
        is_create = False
600
        recipient = tx.to
601
602
    accounts_with_paid_writes = {sender}
603
    if is_create or tx.value > U256(0):
604
        accounts_with_paid_writes.add(recipient)
605
606
    return vm.TransactionEnvironment(
607
        origin=sender,
608
        recipient=recipient,
609
        is_create=is_create,
610
        data=tx.data,
611
        value=tx.value,
612
        gas_limit=tx.gas,
613
        effective_gas_price=effective_gas_price,
614
        execution_gas_grant=allocation.execution_gas,
615
        state_gas_reservoir=allocation.state_gas_reservoir,
616
        calldata_floor=intrinsic.calldata_floor,
617
        access_list_addresses=access_list_addresses,
618
        access_list_storage_keys=access_list_storage_keys,
619
        accounts_with_paid_writes=accounts_with_paid_writes,
620
        state=tx_state,
621
        blob_versioned_hashes=blob_versioned_hashes,
622
        authorizations=authorizations,
623
        index_in_block=index,
624
        tx_hash=get_transaction_hash(encode_transaction(tx)),
625
    )

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:
634
    <snip>
655
    receipt = Receipt(
656
        succeeded=error is None,
657
        cumulative_gas_used=cumulative_gas_used,
658
        bloom=logs_bloom(logs),
659
        logs=logs,
660
    )
661
662
    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 : TransactionOutput The settled output of the system transaction.

def process_checked_system_transaction(​block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes​) -> TransactionOutput:
670
    <snip>
689
    # Pre-check that the system contract has code. We use a throwaway
690
    # TransactionState here that is *never* propagated back to BlockState
691
    # (no incorporate_tx_into_block call); the same get_account / get_code
692
    # lookups are performed and properly tracked by
693
    # process_unchecked_system_transaction below, which this function
694
    # always calls. Reading via a TransactionState (rather than directly
695
    # against pre_state) lets us see system contracts deployed earlier in
696
    # the same block — see EIP-7002 and EIP-7251 for this edge case.
697
    untracked_state = TransactionState(parent=block_env.state)
698
    system_contract_code = get_code(
699
        untracked_state,
700
        get_account(untracked_state, target_address).code_hash,
701
    )
702
703
    if len(system_contract_code) == 0:
704
        raise InvalidBlock(
705
            f"System contract address {target_address.hex()} does not "
706
            "contain code"
707
        )
708
709
    system_tx_output = process_unchecked_system_transaction(
710
        block_env,
711
        target_address,
712
        data,
713
    )
714
715
    if system_tx_output.error:
716
        raise InvalidBlock(
717
            f"System contract ({target_address.hex()}) call failed: "
718
            f"{system_tx_output.error}"
719
        )
720
721
    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 : TransactionOutput The settled output of the system transaction.

def process_unchecked_system_transaction(​block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes​) -> TransactionOutput:
729
    <snip>
748
    system_tx_state = TransactionState(parent=block_env.state)
749
750
    tx_env = vm.TransactionEnvironment(
751
        origin=SYSTEM_ADDRESS,
752
        recipient=target_address,
753
        is_create=False,
754
        data=data,
755
        value=U256(0),
756
        gas_limit=SYSTEM_TRANSACTION_GAS,
757
        effective_gas_price=block_env.base_fee_per_gas,
758
        execution_gas_grant=SYSTEM_TRANSACTION_GAS,
759
        state_gas_reservoir=StateGas(
760
            StateGasCosts.STORAGE_SET * SYSTEM_MAX_SSTORES_PER_CALL
761
        ),
762
        calldata_floor=Uint(0),
763
        access_list_addresses=set(),
764
        access_list_storage_keys=set(),
765
        # A system transaction charges no gas, so no write is paid for.
766
        accounts_with_paid_writes=set(),
767
        state=system_tx_state,
768
        blob_versioned_hashes=(),
769
        authorizations=(),
770
        index_in_block=None,
771
        tx_hash=None,
772
    )
773
774
    system_tx_output = process_top_level(block_env, tx_env)
775
776
    incorporate_tx_into_block(
777
        system_tx_state, block_env.block_access_list_builder
778
    )
779
780
    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:
788
    <snip>
813
    block_output = vm.BlockOutput()
814
815
    process_unchecked_system_transaction(
816
        block_env=block_env,
817
        target_address=BEACON_ROOTS_ADDRESS,
818
        data=block_env.parent_beacon_block_root,
819
    )
820
821
    process_unchecked_system_transaction(
822
        block_env=block_env,
823
        target_address=HISTORY_STORAGE_ADDRESS,
824
        data=block_env.block_hashes[-1],  # The parent hash
825
    )
826
827
    for i, tx in enumerate(map(decode_transaction, transactions)):
828
        process_transaction(block_env, block_output, tx, Uint(i))
829
830
    # EIP-7928: Post-execution operations use index N+1
831
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
832
        ulen(transactions) + Uint(1)
833
    )
834
835
    process_withdrawals(block_env, block_output, withdrawals)
836
837
    process_general_purpose_requests(
838
        block_env=block_env,
839
        block_output=block_output,
840
    )
841
842
    block_output.block_access_list = build_block_access_list(
843
        block_env.block_access_list_builder, block_env.state
844
    )
845
846
    # Validate block access list gas limit constraint (EIP-7928)
847
    validate_block_access_list_gas_limit(
848
        block_access_list=block_output.block_access_list,
849
        block_gas_limit=block_env.block_gas_limit,
850
    )
851
852
    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:
859
    <snip>
870
    # Requests are to be in ascending order of request type
871
    deposit_requests = parse_deposit_requests(block_output)
872
    requests_from_execution = block_output.requests
873
    if len(deposit_requests) > 0:
874
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
875
876
    system_withdrawal_tx_output = process_checked_system_transaction(
877
        block_env=block_env,
878
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
879
        data=b"",
880
    )
881
882
    if len(system_withdrawal_tx_output.return_data) > 0:
883
        requests_from_execution.append(
884
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
885
        )
886
887
    system_consolidation_tx_output = process_checked_system_transaction(
888
        block_env=block_env,
889
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
890
        data=b"",
891
    )
892
893
    if len(system_consolidation_tx_output.return_data) > 0:
894
        requests_from_execution.append(
895
            CONSOLIDATION_REQUEST_TYPE
896
            + system_consolidation_tx_output.return_data
897
        )
898
899
    system_builder_deposit_tx_output = process_checked_system_transaction(
900
        block_env=block_env,
901
        target_address=BUILDER_DEPOSIT_CONTRACT_ADDRESS,
902
        data=b"",
903
    )
904
905
    if len(system_builder_deposit_tx_output.return_data) > 0:
906
        requests_from_execution.append(
907
            BUILDER_DEPOSIT_REQUEST_TYPE
908
            + system_builder_deposit_tx_output.return_data
909
        )
910
911
    system_builder_exit_tx_output = process_checked_system_transaction(
912
        block_env=block_env,
913
        target_address=BUILDER_EXIT_CONTRACT_ADDRESS,
914
        data=b"",
915
    )
916
917
    if len(system_builder_exit_tx_output.return_data) > 0:
918
        requests_from_execution.append(
919
            BUILDER_EXIT_REQUEST_TYPE
920
            + system_builder_exit_tx_output.return_data
921
        )

update_sender_state

Debit the sender for the transaction's maximum possible gas fee.

Increment the sender's nonce and deduct the largest fee the transaction could incur -- its gas limit priced at the effective gas price, plus the blob fee resolved at inclusion -- up front. Execution later refunds whatever execution gas was not spent.

Parameters

block_env : The block's execution environment. tx_env : The transaction's execution environment. tx : The transaction being charged.

def update_sender_state(​block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​tx_env: ethereum.forks.amsterdam.vm.TransactionEnvironment, ​​tx: Transaction​) -> None:
929
    <snip>
947
    tx_state = tx_env.state
948
    sender = tx_env.origin
949
    sender_account = get_account(tx_state, sender)
950
951
    effective_gas_fee = tx_env.gas_limit * tx_env.effective_gas_price
952
    if isinstance(tx, BlobTransaction):
953
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
954
    else:
955
        blob_gas_fee = Uint(0)
956
957
    increment_nonce(tx_state, sender)
958
959
    sender_balance_after_gas_fee = (
960
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
961
    )
962
    set_account_balance(tx_state, sender, U256(sender_balance_after_gas_fee))

disburse_gas_fees

Refund the payer's unspent gas and pay the priority fee.

Return the gas the transaction did not use to the payer that fronted the maximum fee at inclusion, priced at the effective gas price, and credit the coinbase with the priority fee on the gas that was used.

Parameters

block_env : The block scoped environment. tx_env : The transaction's execution environment. settlement : The settled gas amounts. payer : The account that fronted the maximum gas fee and receives the refund.

def disburse_gas_fees(​block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​tx_env: ethereum.forks.amsterdam.vm.TransactionEnvironment, ​​settlement: TransactionGasSettlement, ​​payer: Address​) -> None:
971
    <snip>
992
    tx_state = tx_env.state
993
    gas_refund_amount = settlement.gas_left * tx_env.effective_gas_price
994
995
    priority_fee_per_gas = (
996
        tx_env.effective_gas_price - block_env.base_fee_per_gas
997
    )
998
    transaction_fee = settlement.gas_used * priority_fee_per_gas
999
1000
    create_ether(tx_state, payer, U256(gas_refund_amount))
1001
    create_ether(tx_state, block_env.coinbase, U256(transaction_fee))

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:
1010
    <snip>
1034
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
1035
        index + Uint(1)
1036
    )
1037
1038
    trie_set(
1039
        block_output.transactions_trie,
1040
        rlp.encode(index),
1041
        encode_transaction(tx),
1042
    )
1043
1044
    tx_chain_id = chain_id(tx)
1045
    if tx_chain_id is not None and tx_chain_id != block_env.chain_id:
1046
        raise WrongChainIdError(
1047
            expected=block_env.chain_id,
1048
            actual=tx_chain_id,
1049
        )
1050
1051
    tx_env = check_transaction(block_env, block_output, tx, index)
1052
1053
    update_sender_state(block_env, tx_env, tx)
1054
1055
    tx_output = process_top_level(block_env, tx_env)
1056
1057
    settlement = settle_transaction_gas(
1058
        tx_env.gas_limit,
1059
        tx_env.calldata_floor,
1060
        tx_output.gas_left,
1061
        tx_output.state_gas_left,
1062
        tx_output.refund_counter,
1063
        tx_output.state_gas_used,
1064
    )
1065
1066
    disburse_gas_fees(block_env, tx_env, settlement, tx_env.origin)
1067
1068
    block_output.block_gas_used += settlement.execution_gas_used
1069
    block_output.block_state_gas_used += settlement.state_gas_used
1070
    block_output.blob_gas_used += calculate_total_blob_gas(tx)
1071
1072
    block_output.cumulative_gas_used += settlement.gas_used
1073
    receipt = make_receipt(
1074
        tx, tx_output.error, block_output.cumulative_gas_used, tx_output.logs
1075
    )
1076
1077
    receipt_key = rlp.encode(Uint(index))
1078
    block_output.receipt_keys += (receipt_key,)
1079
1080
    trie_set(
1081
        block_output.receipts_trie,
1082
        receipt_key,
1083
        receipt,
1084
    )
1085
1086
    block_output.block_logs += tx_output.logs
1087
1088
    for address in tx_output.accounts_to_delete:
1089
        clear_account_preserving_balance(tx_env.state, address)
1090
1091
    incorporate_tx_into_block(
1092
        tx_env.state, block_env.block_access_list_builder
1093
    )

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:
1101
    <snip>
1104
    wd_state = TransactionState(parent=block_env.state)
1105
1106
    for i, wd in enumerate(withdrawals):
1107
        trie_set(
1108
            block_output.withdrawals_trie,
1109
            rlp.encode(Uint(i)),
1110
            rlp.encode(wd),
1111
        )
1112
1113
        create_ether(wd_state, wd.address, U256(wd.amount) * GWEI_TO_WEI)
1114
1115
    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:
1119
    <snip>
1147
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
1148
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1149
        return False
1150
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1151
        return False
1152
    if gas_limit < GasCosts.LIMIT_MINIMUM:
1153
        return False
1154
1155
    return True