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

107
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

108
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

110
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

111
BEACON_ROOTS_ADDRESS = hex_to_address(
112
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
113
)

SYSTEM_TRANSACTION_GAS

114
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.

115
SYSTEM_MAX_SSTORES_PER_CALL = Uint(16)

GWEI_TO_WEI

120
GWEI_TO_WEI = U256(10**9)

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

122
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
123
    "0x00000961Ef480Eb55e80D19ad83579A64c007002"
124
)

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

125
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
126
    "0x0000BBdDc7CE488642fb579F8B00f3a590007251"
127
)

BUILDER_DEPOSIT_CONTRACT_ADDRESS

128
BUILDER_DEPOSIT_CONTRACT_ADDRESS = hex_to_address(
129
    "0x0000BFF46984E3725691FA540A8C7589300D8282"
130
)

BUILDER_EXIT_CONTRACT_ADDRESS

131
BUILDER_EXIT_CONTRACT_ADDRESS = hex_to_address(
132
    "0x000064D678505AD48F8CCB093BC65613800E8282"
133
)

HISTORY_STORAGE_ADDRESS

134
HISTORY_STORAGE_ADDRESS = hex_to_address(
135
    "0x0000F90827F1C53a10cb7A02335B175320002935"
136
)

MAX_BLOCK_SIZE

137
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

138
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

139
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

ChainContext

Chain context needed for block execution.

142
@final
143
@slotted_freezable
144
@dataclass
class ChainContext:

chain_id

Identify the chain for transaction signature recovery.

150
    chain_id: U64

block_hashes

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

153
    block_hashes: List[Hash32]

parent_header

Parent header used for header validation and system contracts.

156
    parent_header: Header | PreviousHeader

BlockChain

History and current state of the block chain.

160
@final
161
@dataclass
class BlockChain:

blocks

167
    blocks: List[Block]

state

168
    state: State

chain_id

169
    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:
173
    <snip>
192
    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]:
196
    <snip>
216
    recent_blocks = chain.blocks[-255:]
217
    # TODO: This function has not been tested rigorously
218
    if len(recent_blocks) == 0:
219
        return []
220
221
    recent_block_hashes = []
222
223
    for block in recent_blocks:
224
        prev_block_hash = block.header.parent_hash
225
        recent_block_hashes.append(prev_block_hash)
226
227
    # We are computing the hash only for the most recent block and not for
228
    # the rest of the blocks as they have successors which have the hash of
229
    # the current block as parent hash.
230
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
231
    recent_block_hashes.append(most_recent_block_hash)
232
233
    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:
237
    <snip>
259
    chain_context = ChainContext(
260
        chain_id=chain.chain_id,
261
        block_hashes=get_last_256_block_hashes(chain),
262
        parent_header=chain.blocks[-1].header,
263
    )
264
265
    block_diff = execute_block(block, chain.state, chain_context)
266
267
    apply_changes_to_state(chain.state, block_diff)
268
    chain.blocks.append(block)
269
    if len(chain.blocks) > 255:
270
        # Real clients have to store more blocks to deal with reorgs, but the
271
        # protocol only requires the last 255
272
        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:
280
    <snip>
300
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
301
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
302
303
    parent_header = chain_context.parent_header
304
    validate_header(parent_header, block.header)
305
306
    if block.ommers != ():
307
        raise InvalidBlock
308
309
    block_state = BlockState(pre_state=pre_state)
310
311
    block_env = vm.BlockEnvironment(
312
        chain_id=chain_context.chain_id,
313
        state=block_state,
314
        block_gas_limit=block.header.gas_limit,
315
        block_hashes=chain_context.block_hashes,
316
        coinbase=block.header.coinbase,
317
        number=block.header.number,
318
        base_fee_per_gas=block.header.base_fee_per_gas,
319
        time=block.header.timestamp,
320
        prev_randao=block.header.prev_randao,
321
        excess_blob_gas=block.header.excess_blob_gas,
322
        parent_beacon_block_root=block.header.parent_beacon_block_root,
323
        block_access_list_builder=BlockAccessListBuilder(),
324
        slot_number=block.header.slot_number,
325
    )
326
327
    block_output = apply_body(
328
        block_env=block_env,
329
        transactions=block.transactions,
330
        withdrawals=block.withdrawals,
331
    )
332
    block_diff = extract_block_diff(block_state)
333
    block_state_root = pre_state.compute_state_root(block_diff)
334
    transactions_root = root(block_output.transactions_trie)
335
    receipt_root = root(block_output.receipts_trie)
336
    block_logs_bloom = logs_bloom(block_output.block_logs)
337
    withdrawals_root = root(block_output.withdrawals_trie)
338
    requests_hash = compute_requests_hash(block_output.requests)
339
    computed_block_access_list_hash = hash_block_access_list(
340
        block_output.block_access_list
341
    )
342
343
    block_gas_used = max(
344
        block_output.block_gas_used,
345
        block_output.block_state_gas_used,
346
    )
347
    if block_gas_used != block.header.gas_used:
348
        raise InvalidBlock(f"{block_gas_used} != {block.header.gas_used}")
349
    if transactions_root != block.header.transactions_root:
350
        raise InvalidBlock
351
    if block_state_root != block.header.state_root:
352
        raise InvalidBlock
353
    if receipt_root != block.header.receipt_root:
354
        raise InvalidBlock
355
    if block_logs_bloom != block.header.bloom:
356
        raise InvalidBlock
357
    if withdrawals_root != block.header.withdrawals_root:
358
        raise InvalidBlock
359
    if block_output.blob_gas_used != block.header.blob_gas_used:
360
        raise InvalidBlock
361
    if requests_hash != block.header.requests_hash:
362
        raise InvalidBlock
363
    if computed_block_access_list_hash != block.header.block_access_list_hash:
364
        raise InvalidBlock("Invalid block access list hash")
365
366
    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:
375
    <snip>
395
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
396
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
397
        raise InvalidBlock
398
399
    if parent_gas_used == parent_gas_target:
400
        expected_base_fee_per_gas = parent_base_fee_per_gas
401
    elif parent_gas_used > parent_gas_target:
402
        gas_used_delta = parent_gas_used - parent_gas_target
403
404
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
405
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
406
407
        base_fee_per_gas_delta = max(
408
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
409
            Uint(1),
410
        )
411
412
        expected_base_fee_per_gas = (
413
            parent_base_fee_per_gas + base_fee_per_gas_delta
414
        )
415
    else:
416
        gas_used_delta = parent_gas_target - parent_gas_used
417
418
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
419
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
420
421
        base_fee_per_gas_delta = (
422
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
423
        )
424
425
        expected_base_fee_per_gas = (
426
            parent_base_fee_per_gas - base_fee_per_gas_delta
427
        )
428
429
    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:
435
    <snip>
453
    if header.number < Uint(1):
454
        raise InvalidBlock
455
456
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
457
    if header.excess_blob_gas != excess_blob_gas:
458
        raise InvalidBlock
459
460
    if header.gas_used > header.gas_limit:
461
        raise InvalidBlock
462
463
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
464
        header.gas_limit,
465
        parent_header.gas_limit,
466
        parent_header.gas_used,
467
        parent_header.base_fee_per_gas,
468
    )
469
    if expected_base_fee_per_gas != header.base_fee_per_gas:
470
        raise InvalidBlock
471
    if header.timestamp <= parent_header.timestamp:
472
        raise InvalidBlock
473
    if header.number != parent_header.number + Uint(1):
474
        raise InvalidBlock
475
    if len(header.extra_data) > 32:
476
        raise InvalidBlock
477
    if header.difficulty != 0:
478
        raise InvalidBlock
479
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
480
        raise InvalidBlock
481
    if header.ommers_hash != EMPTY_OMMER_HASH:
482
        raise InvalidBlock
483
484
    block_parent_hash = keccak256(rlp.encode(parent_header))
485
    if header.parent_hash != block_parent_hash:
486
        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. 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:
495
    <snip>
544
    sender = recover_sender(tx)
545
    intrinsic = validate_transaction(tx, sender)
546
    tx_state = TransactionState(parent=block_env.state)
547
548
    check_block_gas_capacity(
549
        block_env, block_output, tx.gas, calculate_total_blob_gas(tx)
550
    )
551
552
    sender_account = get_account(tx_state, sender)
553
554
    effective_gas_price = calculate_effective_gas_price(
555
        tx, block_env.base_fee_per_gas
556
    )
557
    max_gas_fee = calculate_max_gas_fee(tx, tx.gas)
558
559
    if isinstance(tx, BlobTransaction):
560
        check_max_fee_per_blob_gas(
561
            tx.blob_versioned_hashes,
562
            tx.max_fee_per_blob_gas,
563
            block_env.excess_blob_gas,
564
        )
565
566
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
567
            tx.max_fee_per_blob_gas
568
        )
569
        blob_versioned_hashes = tx.blob_versioned_hashes
570
    else:
571
        blob_versioned_hashes = ()
572
573
    check_nonce(tx, sender_account.nonce)
574
575
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
576
        raise InsufficientBalanceError("insufficient sender balance")
577
    sender_code = get_code(tx_state, sender_account.code_hash)
578
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
579
        sender_code
580
    ):
581
        raise InvalidSenderError("not EOA")
582
583
    # Split the EVM gas into an execution-gas grant (capped by the
584
    # remaining execution-gas budget) and a state gas reservoir.
585
    allocation = allocate_evm_gas(tx.gas, intrinsic)
586
587
    access_list_addresses = set()
588
    access_list_storage_keys = set()
589
    if has_access_list(tx):
590
        for access in tx.access_list:
591
            access_list_addresses.add(access.account)
592
            for slot in access.slots:
593
                access_list_storage_keys.add((access.account, slot))
594
595
    authorizations: Tuple[Authorization, ...] = ()
596
    if isinstance(tx, SetCodeTransaction):
597
        authorizations = tx.authorizations
598
599
    if isinstance(tx.to, Bytes0):
600
        is_create = True
601
        # A creation's frame runs at the address the contract
602
        # deploys to.
603
        recipient = compute_contract_address(sender, sender_account.nonce)
604
    else:
605
        is_create = False
606
        recipient = tx.to
607
608
    accounts_with_paid_writes = {sender}
609
    if is_create or tx.value > U256(0):
610
        accounts_with_paid_writes.add(recipient)
611
612
    return vm.TransactionEnvironment(
613
        origin=sender,
614
        recipient=recipient,
615
        is_create=is_create,
616
        data=tx.data,
617
        value=tx.value,
618
        gas_limit=tx.gas,
619
        effective_gas_price=effective_gas_price,
620
        execution_gas_grant=allocation.execution_gas,
621
        state_gas_reservoir=allocation.state_gas_reservoir,
622
        calldata_floor=intrinsic.calldata_floor,
623
        access_list_addresses=access_list_addresses,
624
        access_list_storage_keys=access_list_storage_keys,
625
        accounts_with_paid_writes=accounts_with_paid_writes,
626
        state=tx_state,
627
        blob_versioned_hashes=blob_versioned_hashes,
628
        authorizations=authorizations,
629
        index_in_block=index,
630
        tx_hash=get_transaction_hash(encode_transaction(tx)),
631
    )

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:
640
    <snip>
661
    receipt = Receipt(
662
        succeeded=error is None,
663
        cumulative_gas_used=cumulative_gas_used,
664
        bloom=logs_bloom(logs),
665
        logs=logs,
666
    )
667
668
    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:
676
    <snip>
695
    # Pre-check that the system contract has code. We use a throwaway
696
    # TransactionState here that is *never* propagated back to BlockState
697
    # (no incorporate_tx_into_block call); the same get_account / get_code
698
    # lookups are performed and properly tracked by
699
    # process_unchecked_system_transaction below, which this function
700
    # always calls. Reading via a TransactionState (rather than directly
701
    # against pre_state) lets us see system contracts deployed earlier in
702
    # the same block — see EIP-7002 and EIP-7251 for this edge case.
703
    untracked_state = TransactionState(parent=block_env.state)
704
    system_contract_code = get_code(
705
        untracked_state,
706
        get_account(untracked_state, target_address).code_hash,
707
    )
708
709
    if len(system_contract_code) == 0:
710
        raise InvalidBlock(
711
            f"System contract address {target_address.hex()} does not "
712
            "contain code"
713
        )
714
715
    system_tx_output = process_unchecked_system_transaction(
716
        block_env,
717
        target_address,
718
        data,
719
    )
720
721
    if system_tx_output.error:
722
        raise InvalidBlock(
723
            f"System contract ({target_address.hex()}) call failed: "
724
            f"{system_tx_output.error}"
725
        )
726
727
    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:
735
    <snip>
754
    system_tx_state = TransactionState(parent=block_env.state)
755
756
    tx_env = vm.TransactionEnvironment(
757
        origin=SYSTEM_ADDRESS,
758
        recipient=target_address,
759
        is_create=False,
760
        data=data,
761
        value=U256(0),
762
        gas_limit=SYSTEM_TRANSACTION_GAS,
763
        effective_gas_price=block_env.base_fee_per_gas,
764
        execution_gas_grant=SYSTEM_TRANSACTION_GAS,
765
        state_gas_reservoir=StateGas(
766
            StateGasCosts.STORAGE_SET * SYSTEM_MAX_SSTORES_PER_CALL
767
        ),
768
        calldata_floor=Uint(0),
769
        access_list_addresses=set(),
770
        access_list_storage_keys=set(),
771
        # A system transaction charges no gas, so no write is paid for.
772
        accounts_with_paid_writes=set(),
773
        state=system_tx_state,
774
        blob_versioned_hashes=(),
775
        authorizations=(),
776
        index_in_block=None,
777
        tx_hash=None,
778
    )
779
780
    system_tx_output = process_top_level(block_env, tx_env)
781
782
    incorporate_tx_into_block(
783
        system_tx_state, block_env.block_access_list_builder
784
    )
785
786
    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:
794
    <snip>
819
    block_output = vm.BlockOutput()
820
821
    process_unchecked_system_transaction(
822
        block_env=block_env,
823
        target_address=BEACON_ROOTS_ADDRESS,
824
        data=block_env.parent_beacon_block_root,
825
    )
826
827
    process_unchecked_system_transaction(
828
        block_env=block_env,
829
        target_address=HISTORY_STORAGE_ADDRESS,
830
        data=block_env.block_hashes[-1],  # The parent hash
831
    )
832
833
    for i, tx in enumerate(map(decode_transaction, transactions)):
834
        process_transaction(block_env, block_output, tx, Uint(i))
835
836
    # EIP-7928: Post-execution operations use index N+1
837
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
838
        ulen(transactions) + Uint(1)
839
    )
840
841
    process_withdrawals(block_env, block_output, withdrawals)
842
843
    process_general_purpose_requests(
844
        block_env=block_env,
845
        block_output=block_output,
846
    )
847
848
    block_output.block_access_list = build_block_access_list(
849
        block_env.block_access_list_builder, block_env.state
850
    )
851
852
    # Validate block access list gas limit constraint (EIP-7928)
853
    validate_block_access_list_gas_limit(
854
        block_access_list=block_output.block_access_list,
855
        block_gas_limit=block_env.block_gas_limit,
856
    )
857
858
    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:
865
    <snip>
876
    # Requests are to be in ascending order of request type
877
    deposit_requests = parse_deposit_requests(block_output)
878
    requests_from_execution = block_output.requests
879
    if len(deposit_requests) > 0:
880
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
881
882
    system_withdrawal_tx_output = process_checked_system_transaction(
883
        block_env=block_env,
884
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
885
        data=b"",
886
    )
887
888
    if len(system_withdrawal_tx_output.return_data) > 0:
889
        requests_from_execution.append(
890
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
891
        )
892
893
    system_consolidation_tx_output = process_checked_system_transaction(
894
        block_env=block_env,
895
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
896
        data=b"",
897
    )
898
899
    if len(system_consolidation_tx_output.return_data) > 0:
900
        requests_from_execution.append(
901
            CONSOLIDATION_REQUEST_TYPE
902
            + system_consolidation_tx_output.return_data
903
        )
904
905
    system_builder_deposit_tx_output = process_checked_system_transaction(
906
        block_env=block_env,
907
        target_address=BUILDER_DEPOSIT_CONTRACT_ADDRESS,
908
        data=b"",
909
    )
910
911
    if len(system_builder_deposit_tx_output.return_data) > 0:
912
        requests_from_execution.append(
913
            BUILDER_DEPOSIT_REQUEST_TYPE
914
            + system_builder_deposit_tx_output.return_data
915
        )
916
917
    system_builder_exit_tx_output = process_checked_system_transaction(
918
        block_env=block_env,
919
        target_address=BUILDER_EXIT_CONTRACT_ADDRESS,
920
        data=b"",
921
    )
922
923
    if len(system_builder_exit_tx_output.return_data) > 0:
924
        requests_from_execution.append(
925
            BUILDER_EXIT_REQUEST_TYPE
926
            + system_builder_exit_tx_output.return_data
927
        )

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:
935
    <snip>
953
    tx_state = tx_env.state
954
    sender = tx_env.origin
955
    sender_account = get_account(tx_state, sender)
956
957
    effective_gas_fee = tx_env.gas_limit * tx_env.effective_gas_price
958
    if isinstance(tx, BlobTransaction):
959
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
960
    else:
961
        blob_gas_fee = Uint(0)
962
963
    increment_nonce(tx_state, sender)
964
965
    sender_balance_after_gas_fee = (
966
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
967
    )
968
    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:
977
    <snip>
998
    tx_state = tx_env.state
999
    gas_refund_amount = settlement.gas_left * tx_env.effective_gas_price
1000
1001
    priority_fee_per_gas = (
1002
        tx_env.effective_gas_price - block_env.base_fee_per_gas
1003
    )
1004
    transaction_fee = settlement.gas_used * priority_fee_per_gas
1005
1006
    create_ether(tx_state, payer, U256(gas_refund_amount))
1007
    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:
1016
    <snip>
1040
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
1041
        index + Uint(1)
1042
    )
1043
1044
    trie_set(
1045
        block_output.transactions_trie,
1046
        rlp.encode(index),
1047
        encode_transaction(tx),
1048
    )
1049
1050
    tx_chain_id = chain_id(tx)
1051
    if tx_chain_id is not None and tx_chain_id != block_env.chain_id:
1052
        raise WrongChainIdError(
1053
            expected=block_env.chain_id,
1054
            actual=tx_chain_id,
1055
        )
1056
1057
    tx_env = check_transaction(block_env, block_output, tx, index)
1058
1059
    update_sender_state(block_env, tx_env, tx)
1060
1061
    tx_output = process_top_level(block_env, tx_env)
1062
1063
    settlement = settle_transaction_gas(
1064
        tx_env.gas_limit,
1065
        tx_env.calldata_floor,
1066
        tx_output.gas_left,
1067
        tx_output.state_gas_left,
1068
        tx_output.refund_counter,
1069
        tx_output.state_gas_used,
1070
    )
1071
1072
    disburse_gas_fees(block_env, tx_env, settlement, tx_env.origin)
1073
1074
    block_output.block_gas_used += settlement.execution_gas_used
1075
    block_output.block_state_gas_used += settlement.state_gas_used
1076
    block_output.blob_gas_used += calculate_total_blob_gas(tx)
1077
1078
    block_output.cumulative_gas_used += settlement.gas_used
1079
    receipt = make_receipt(
1080
        tx, tx_output.error, block_output.cumulative_gas_used, tx_output.logs
1081
    )
1082
1083
    receipt_key = rlp.encode(Uint(index))
1084
    block_output.receipt_keys += (receipt_key,)
1085
1086
    trie_set(
1087
        block_output.receipts_trie,
1088
        receipt_key,
1089
        receipt,
1090
    )
1091
1092
    block_output.block_logs += tx_output.logs
1093
1094
    for address in tx_output.accounts_to_delete:
1095
        clear_account_preserving_balance(tx_env.state, address)
1096
1097
    incorporate_tx_into_block(
1098
        tx_env.state, block_env.block_access_list_builder
1099
    )

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:
1107
    <snip>
1110
    wd_state = TransactionState(parent=block_env.state)
1111
1112
    for i, wd in enumerate(withdrawals):
1113
        trie_set(
1114
            block_output.withdrawals_trie,
1115
            rlp.encode(Uint(i)),
1116
            rlp.encode(wd),
1117
        )
1118
1119
        create_ether(wd_state, wd.address, U256(wd.amount) * GWEI_TO_WEI)
1120
1121
    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:
1125
    <snip>
1153
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
1154
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1155
        return False
1156
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1157
        return False
1158
    if gas_limit < GasCosts.LIMIT_MINIMUM:
1159
        return False
1160
1161
    return True