ethereum.forks.prague.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

95
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

96
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

98
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

99
BEACON_ROOTS_ADDRESS = hex_to_address(
100
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
101
)

SYSTEM_TRANSACTION_GAS

102
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

103
MAX_BLOB_GAS_PER_BLOCK: Final[U64] = U64(1179648)

VERSIONED_HASH_VERSION_KZG

104
VERSIONED_HASH_VERSION_KZG = b"\x01"

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

106
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
107
    "0x00000961Ef480Eb55e80D19ad83579A64c007002"
108
)

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

109
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
110
    "0x0000BBdDc7CE488642fb579F8B00f3a590007251"
111
)

HISTORY_STORAGE_ADDRESS

112
HISTORY_STORAGE_ADDRESS = hex_to_address(
113
    "0x0000F90827F1C53a10cb7A02335B175320002935"
114
)

BlockChain

History and current state of the block chain.

117
@final
118
@dataclass
class BlockChain:

blocks

124
    blocks: List[Block]

state

125
    state: State

chain_id

126
    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:
130
    <snip>
149
    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]:
153
    <snip>
173
    recent_blocks = chain.blocks[-255:]
174
    # TODO: This function has not been tested rigorously
175
    if len(recent_blocks) == 0:
176
        return []
177
178
    recent_block_hashes = []
179
180
    for block in recent_blocks:
181
        prev_block_hash = block.header.parent_hash
182
        recent_block_hashes.append(prev_block_hash)
183
184
    # We are computing the hash only for the most recent block and not for
185
    # the rest of the blocks as they have successors which have the hash of
186
    # the current block as parent hash.
187
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
188
    recent_block_hashes.append(most_recent_block_hash)
189
190
    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:
194
    <snip>
216
    validate_header(chain, block.header)
217
    if block.ommers != ():
218
        raise InvalidBlock
219
220
    block_state = BlockState(pre_state=chain.state)
221
222
    block_env = vm.BlockEnvironment(
223
        chain_id=chain.chain_id,
224
        state=block_state,
225
        block_gas_limit=block.header.gas_limit,
226
        block_hashes=get_last_256_block_hashes(chain),
227
        coinbase=block.header.coinbase,
228
        number=block.header.number,
229
        base_fee_per_gas=block.header.base_fee_per_gas,
230
        time=block.header.timestamp,
231
        prev_randao=block.header.prev_randao,
232
        excess_blob_gas=block.header.excess_blob_gas,
233
        parent_beacon_block_root=block.header.parent_beacon_block_root,
234
    )
235
236
    block_output = apply_body(
237
        block_env=block_env,
238
        transactions=block.transactions,
239
        withdrawals=block.withdrawals,
240
    )
241
    block_diff = extract_block_diff(block_state)
242
    block_state_root = chain.state.compute_state_root(block_diff)
243
    transactions_root = root(block_output.transactions_trie)
244
    receipt_root = root(block_output.receipts_trie)
245
    block_logs_bloom = logs_bloom(block_output.block_logs)
246
    withdrawals_root = root(block_output.withdrawals_trie)
247
    requests_hash = compute_requests_hash(block_output.requests)
248
249
    if block_output.block_gas_used != block.header.gas_used:
250
        raise InvalidBlock(
251
            f"{block_output.block_gas_used} != {block.header.gas_used}"
252
        )
253
    if transactions_root != block.header.transactions_root:
254
        raise InvalidBlock
255
    if block_state_root != block.header.state_root:
256
        raise InvalidBlock
257
    if receipt_root != block.header.receipt_root:
258
        raise InvalidBlock
259
    if block_logs_bloom != block.header.bloom:
260
        raise InvalidBlock
261
    if withdrawals_root != block.header.withdrawals_root:
262
        raise InvalidBlock
263
    if block_output.blob_gas_used != block.header.blob_gas_used:
264
        raise InvalidBlock
265
    if requests_hash != block.header.requests_hash:
266
        raise InvalidBlock
267
268
    apply_changes_to_state(chain.state, block_diff)
269
    chain.blocks.append(block)
270
    if len(chain.blocks) > 255:
271
        # Real clients have to store more blocks to deal with reorgs, but the
272
        # protocol only requires the last 255
273
        chain.blocks = chain.blocks[-255:]

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:
282
    <snip>
302
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
303
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
304
        raise InvalidBlock
305
306
    if parent_gas_used == parent_gas_target:
307
        expected_base_fee_per_gas = parent_base_fee_per_gas
308
    elif parent_gas_used > parent_gas_target:
309
        gas_used_delta = parent_gas_used - parent_gas_target
310
311
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
312
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
313
314
        base_fee_per_gas_delta = max(
315
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
316
            Uint(1),
317
        )
318
319
        expected_base_fee_per_gas = (
320
            parent_base_fee_per_gas + base_fee_per_gas_delta
321
        )
322
    else:
323
        gas_used_delta = parent_gas_target - parent_gas_used
324
325
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
326
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
327
328
        base_fee_per_gas_delta = (
329
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
330
        )
331
332
        expected_base_fee_per_gas = (
333
            parent_base_fee_per_gas - base_fee_per_gas_delta
334
        )
335
336
    return Uint(expected_base_fee_per_gas)

validate_header

Verifies a block header.

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

chain : History and current state. header : Header to check for correctness.

def validate_header(chain: BlockChain, ​​header: Header) -> None:
340
    <snip>
358
    if header.number < Uint(1):
359
        raise InvalidBlock
360
361
    parent_header = chain.blocks[-1].header
362
363
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
364
    if header.excess_blob_gas != excess_blob_gas:
365
        raise InvalidBlock
366
367
    if header.gas_used > header.gas_limit:
368
        raise InvalidBlock
369
370
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
371
        header.gas_limit,
372
        parent_header.gas_limit,
373
        parent_header.gas_used,
374
        parent_header.base_fee_per_gas,
375
    )
376
    if expected_base_fee_per_gas != header.base_fee_per_gas:
377
        raise InvalidBlock
378
    if header.timestamp <= parent_header.timestamp:
379
        raise InvalidBlock
380
    if header.number != parent_header.number + Uint(1):
381
        raise InvalidBlock
382
    if len(header.extra_data) > 32:
383
        raise InvalidBlock
384
    if header.difficulty != 0:
385
        raise InvalidBlock
386
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
387
        raise InvalidBlock
388
    if header.ommers_hash != EMPTY_OMMER_HASH:
389
        raise InvalidBlock
390
391
    block_parent_hash = keccak256(rlp.encode(parent_header))
392
    if header.parent_hash != block_parent_hash:
393
        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.

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. 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.prague.vm.BlockEnvironment, ​​block_output: ethereum.forks.prague.vm.BlockOutput, ​​tx: Transaction, ​​tx_state: TransactionState) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
402
    <snip>
460
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
461
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
462
463
    if tx.gas > gas_available:
464
        raise GasUsedExceedsLimitError("gas used exceeds limit")
465
466
    tx_blob_gas_used = calculate_total_blob_gas(tx)
467
    if tx_blob_gas_used > blob_gas_available:
468
        raise BlobGasLimitExceededError("blob gas limit exceeded")
469
470
    tx_chain_id = chain_id(tx)
471
    if tx_chain_id is not None and tx_chain_id != block_env.chain_id:
472
        raise WrongChainIdError(
473
            expected=block_env.chain_id,
474
            actual=tx_chain_id,
475
        )
476
477
    sender_address = recover_sender(tx)
478
    sender_account = get_account(tx_state, sender_address)
479
480
    if isinstance(tx, FeeMarketCapableTransaction):
481
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
482
            raise PriorityFeeGreaterThanMaxFeeError(
483
                "priority fee greater than max fee"
484
            )
485
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
486
            raise InsufficientMaxFeePerGasError(
487
                tx.max_fee_per_gas, block_env.base_fee_per_gas
488
            )
489
490
        priority_fee_per_gas = min(
491
            tx.max_priority_fee_per_gas,
492
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
493
        )
494
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
495
        max_gas_fee = tx.gas * tx.max_fee_per_gas
496
    else:
497
        if tx.gas_price < block_env.base_fee_per_gas:
498
            raise InvalidBlock
499
        effective_gas_price = tx.gas_price
500
        max_gas_fee = tx.gas * tx.gas_price
501
502
    if isinstance(tx, BlobTransaction):
503
        if len(tx.blob_versioned_hashes) == 0:
504
            raise NoBlobDataError("no blob data in transaction")
505
        for blob_versioned_hash in tx.blob_versioned_hashes:
506
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
507
                raise InvalidBlobVersionedHashError(
508
                    "invalid blob versioned hash"
509
                )
510
511
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
512
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
513
            raise InsufficientMaxFeePerBlobGasError(
514
                "insufficient max fee per blob gas"
515
            )
516
517
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
518
            tx.max_fee_per_blob_gas
519
        )
520
        blob_versioned_hashes = tx.blob_versioned_hashes
521
    else:
522
        blob_versioned_hashes = ()
523
524
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
525
        if not isinstance(tx.to, Address):
526
            raise TransactionTypeContractCreationError(tx)
527
528
    if isinstance(tx, SetCodeTransaction):
529
        if not any(tx.authorizations):
530
            raise EmptyAuthorizationListError("empty authorization list")
531
532
    if sender_account.nonce > Uint(tx.nonce):
533
        raise NonceMismatchError("nonce too low")
534
    elif sender_account.nonce < Uint(tx.nonce):
535
        raise NonceMismatchError("nonce too high")
536
537
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
538
        raise InsufficientBalanceError("insufficient sender balance")
539
    sender_code = get_code(tx_state, sender_account.code_hash)
540
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
541
        sender_code
542
    ):
543
        raise InvalidSenderError("not EOA")
544
545
    return (
546
        sender_address,
547
        effective_gas_price,
548
        blob_versioned_hashes,
549
        tx_blob_gas_used,
550
    )

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. 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:
559
    <snip>
580
    receipt = Receipt(
581
        succeeded=error is None,
582
        cumulative_gas_used=cumulative_gas_used,
583
        bloom=logs_bloom(logs),
584
        logs=logs,
585
    )
586
587
    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.prague.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
595
    <snip>
614
    # Pre-check that the system contract has code. We use a throwaway
615
    # TransactionState here that is *never* propagated back to BlockState
616
    # (no incorporate_tx_into_block call); the same get_account / get_code
617
    # lookups are performed and properly tracked by
618
    # process_unchecked_system_transaction below, which this function
619
    # always calls. Reading via a TransactionState (rather than directly
620
    # against pre_state) lets us see system contracts deployed earlier in
621
    # the same block — see EIP-7002 and EIP-7251 for this edge case.
622
    untracked_state = TransactionState(parent=block_env.state)
623
    system_contract_code = get_code(
624
        untracked_state,
625
        get_account(untracked_state, target_address).code_hash,
626
    )
627
628
    if len(system_contract_code) == 0:
629
        raise InvalidBlock(
630
            f"System contract address {target_address.hex()} does not "
631
            "contain code"
632
        )
633
634
    system_tx_output = process_unchecked_system_transaction(
635
        block_env,
636
        target_address,
637
        data,
638
    )
639
640
    if system_tx_output.error:
641
        raise InvalidBlock(
642
            f"System contract ({target_address.hex()}) call failed: "
643
            f"{system_tx_output.error}"
644
        )
645
646
    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.prague.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
654
    <snip>
673
    system_tx_state = TransactionState(parent=block_env.state)
674
    system_contract_code = get_code(
675
        system_tx_state,
676
        get_account(system_tx_state, target_address).code_hash,
677
    )
678
679
    tx_env = vm.TransactionEnvironment(
680
        origin=SYSTEM_ADDRESS,
681
        gas_price=block_env.base_fee_per_gas,
682
        gas=SYSTEM_TRANSACTION_GAS,
683
        access_list_addresses=set(),
684
        access_list_storage_keys=set(),
685
        state=system_tx_state,
686
        blob_versioned_hashes=(),
687
        authorizations=(),
688
        index_in_block=None,
689
        tx_hash=None,
690
    )
691
692
    system_tx_message = Message(
693
        block_env=block_env,
694
        tx_env=tx_env,
695
        caller=SYSTEM_ADDRESS,
696
        target=target_address,
697
        gas=SYSTEM_TRANSACTION_GAS,
698
        value=U256(0),
699
        data=data,
700
        code=system_contract_code,
701
        depth=Uint(0),
702
        current_target=target_address,
703
        code_address=target_address,
704
        should_transfer_value=False,
705
        is_static=False,
706
        accessed_addresses=set(),
707
        accessed_storage_keys=set(),
708
        disable_precompiles=False,
709
        parent_evm=None,
710
    )
711
712
    system_tx_output = process_message_call(system_tx_message)
713
714
    incorporate_tx_into_block(system_tx_state)
715
716
    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.prague.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.forks.prague.vm.BlockOutput:
724
    <snip>
749
    block_output = vm.BlockOutput()
750
751
    process_unchecked_system_transaction(
752
        block_env=block_env,
753
        target_address=BEACON_ROOTS_ADDRESS,
754
        data=block_env.parent_beacon_block_root,
755
    )
756
757
    process_unchecked_system_transaction(
758
        block_env=block_env,
759
        target_address=HISTORY_STORAGE_ADDRESS,
760
        data=block_env.block_hashes[-1],  # The parent hash
761
    )
762
763
    for i, tx in enumerate(map(decode_transaction, transactions)):
764
        process_transaction(block_env, block_output, tx, Uint(i))
765
766
    process_withdrawals(block_env, block_output, withdrawals)
767
768
    process_general_purpose_requests(
769
        block_env=block_env,
770
        block_output=block_output,
771
    )
772
773
    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.prague.vm.BlockEnvironment, ​​block_output: ethereum.forks.prague.vm.BlockOutput) -> None:
780
    <snip>
791
    # Requests are to be in ascending order of request type
792
    deposit_requests = parse_deposit_requests(block_output)
793
    requests_from_execution = block_output.requests
794
    if len(deposit_requests) > 0:
795
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
796
797
    system_withdrawal_tx_output = process_checked_system_transaction(
798
        block_env=block_env,
799
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
800
        data=b"",
801
    )
802
803
    if len(system_withdrawal_tx_output.return_data) > 0:
804
        requests_from_execution.append(
805
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
806
        )
807
808
    system_consolidation_tx_output = process_checked_system_transaction(
809
        block_env=block_env,
810
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
811
        data=b"",
812
    )
813
814
    if len(system_consolidation_tx_output.return_data) > 0:
815
        requests_from_execution.append(
816
            CONSOLIDATION_REQUEST_TYPE
817
            + system_consolidation_tx_output.return_data
818
        )

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.prague.vm.BlockEnvironment, ​​block_output: ethereum.forks.prague.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
827
    <snip>
851
    tx_state = TransactionState(parent=block_env.state)
852
853
    trie_set(
854
        block_output.transactions_trie,
855
        rlp.encode(index),
856
        encode_transaction(tx),
857
    )
858
859
    intrinsic = validate_transaction(tx)
860
861
    (
862
        sender,
863
        effective_gas_price,
864
        blob_versioned_hashes,
865
        tx_blob_gas_used,
866
    ) = check_transaction(
867
        block_env=block_env,
868
        block_output=block_output,
869
        tx=tx,
870
        tx_state=tx_state,
871
    )
872
873
    sender_account = get_account(tx_state, sender)
874
875
    if isinstance(tx, BlobTransaction):
876
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
877
    else:
878
        blob_gas_fee = Uint(0)
879
880
    effective_gas_fee = tx.gas * effective_gas_price
881
882
    gas = tx.gas - intrinsic.regular
883
    increment_nonce(tx_state, sender)
884
885
    sender_balance_after_gas_fee = (
886
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
887
    )
888
    set_account_balance(tx_state, sender, U256(sender_balance_after_gas_fee))
889
890
    access_list_addresses = set()
891
    access_list_storage_keys = set()
892
    access_list_addresses.add(block_env.coinbase)
893
    if has_access_list(tx):
894
        for access in tx.access_list:
895
            access_list_addresses.add(access.account)
896
            for slot in access.slots:
897
                access_list_storage_keys.add((access.account, slot))
898
899
    authorizations: Tuple[Authorization, ...] = ()
900
    if isinstance(tx, SetCodeTransaction):
901
        authorizations = tx.authorizations
902
903
    tx_env = vm.TransactionEnvironment(
904
        origin=sender,
905
        gas_price=effective_gas_price,
906
        gas=gas,
907
        access_list_addresses=access_list_addresses,
908
        access_list_storage_keys=access_list_storage_keys,
909
        state=tx_state,
910
        blob_versioned_hashes=blob_versioned_hashes,
911
        authorizations=authorizations,
912
        index_in_block=index,
913
        tx_hash=get_transaction_hash(encode_transaction(tx)),
914
    )
915
916
    message = prepare_message(block_env, tx_env, tx)
917
918
    tx_output = process_message_call(message)
919
920
    # For EIP-7623 we first calculate the execution_gas_used, which includes
921
    # the execution gas refund.
922
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
923
    tx_gas_refund = min(
924
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
925
    )
926
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
927
928
    # Transactions with less execution_gas_used than the floor pay at the
929
    # floor cost.
930
    tx_gas_used_after_refund = max(
931
        tx_gas_used_after_refund, intrinsic.calldata_floor
932
    )
933
934
    tx_gas_left = tx.gas - tx_gas_used_after_refund
935
    gas_refund_amount = tx_gas_left * effective_gas_price
936
937
    # For non-1559 transactions effective_gas_price == tx.gas_price
938
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
939
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
940
941
    # refund gas
942
    create_ether(tx_state, sender, U256(gas_refund_amount))
943
944
    # transfer miner fees
945
    create_ether(tx_state, block_env.coinbase, U256(transaction_fee))
946
947
    for address in tx_output.accounts_to_delete:
948
        destroy_account(tx_state, address)
949
950
    block_output.block_gas_used += tx_gas_used_after_refund
951
    block_output.blob_gas_used += tx_blob_gas_used
952
953
    receipt = make_receipt(
954
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
955
    )
956
957
    receipt_key = rlp.encode(Uint(index))
958
    block_output.receipt_keys += (receipt_key,)
959
960
    trie_set(
961
        block_output.receipts_trie,
962
        receipt_key,
963
        receipt,
964
    )
965
966
    block_output.block_logs += tx_output.logs
967
968
    incorporate_tx_into_block(tx_state)

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.prague.vm.BlockEnvironment, ​​block_output: ethereum.forks.prague.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
976
    <snip>
979
    wd_state = TransactionState(parent=block_env.state)
980
981
    for i, wd in enumerate(withdrawals):
982
        trie_set(
983
            block_output.withdrawals_trie,
984
            rlp.encode(Uint(i)),
985
            rlp.encode(wd),
986
        )
987
988
        create_ether(wd_state, wd.address, wd.amount * U256(10**9))
989
990
    incorporate_tx_into_block(wd_state)

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:
994
    <snip>
1022
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
1023
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1024
        return False
1025
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1026
        return False
1027
    if gas_limit < GasCosts.LIMIT_MINIMUM:
1028
        return False
1029
1030
    return True