ethereum.forks.bpo4.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

92
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

93
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

95
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

96
BEACON_ROOTS_ADDRESS = hex_to_address(
97
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
98
)

SYSTEM_TRANSACTION_GAS

99
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

100
MAX_BLOB_GAS_PER_BLOCK = GasCosts.BLOB_SCHEDULE_MAX * GasCosts.PER_BLOB

VERSIONED_HASH_VERSION_KZG

101
VERSIONED_HASH_VERSION_KZG = b"\x01"

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

103
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
104
    "0x00000961Ef480Eb55e80D19ad83579A64c007002"
105
)

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

106
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
107
    "0x0000BBdDc7CE488642fb579F8B00f3a590007251"
108
)

HISTORY_STORAGE_ADDRESS

109
HISTORY_STORAGE_ADDRESS = hex_to_address(
110
    "0x0000F90827F1C53a10cb7A02335B175320002935"
111
)

MAX_BLOCK_SIZE

112
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

113
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

114
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

115
BLOB_COUNT_LIMIT = 6

BlockChain

History and current state of the block chain.

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
    """
131
    Transforms the state from the previous hard fork (`old`) into the block
132
    chain object for this hard fork and returns it.
133
134
    When forks need to implement an irregular state transition, this function
135
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
136
    an example.
137
138
    Parameters
139
    ----------
140
    old :
141
        Previous block chain object.
142
143
    Returns
144
    -------
145
    new : `BlockChain`
146
        Upgraded block chain object for this hard fork.
147
148
    """
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
    """
154
    Obtain the list of hashes of the previous 256 blocks in order of
155
    increasing block number.
156
157
    This function will return less hashes for the first 256 blocks.
158
159
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
160
    therefore this function retrieves them.
161
162
    Parameters
163
    ----------
164
    chain :
165
        History and current state.
166
167
    Returns
168
    -------
169
    recent_block_hashes : `List[Hash32]`
170
        Hashes of the recent 256 blocks in order of increasing block number.
171
172
    """
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
    """
195
    Attempts to apply a block to an existing block chain.
196
197
    All parts of the block's contents need to be verified before being added
198
    to the chain. Blocks are verified by ensuring that the contents of the
199
    block make logical sense with the contents of the parent block. The
200
    information in the block's header must also match the corresponding
201
    information in the block.
202
203
    To implement Ethereum, in theory clients are only required to store the
204
    most recent 255 blocks of the chain since as far as execution is
205
    concerned, only those blocks are accessed. Practically, however, clients
206
    should store more blocks to handle reorgs.
207
208
    Parameters
209
    ----------
210
    chain :
211
        History and current state.
212
    block :
213
        Block to apply to `chain`.
214
215
    """
216
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
217
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
218
219
    validate_header(chain, block.header)
220
    if block.ommers != ():
221
        raise InvalidBlock
222
223
    block_env = vm.BlockEnvironment(
224
        chain_id=chain.chain_id,
225
        state=chain.state,
226
        block_gas_limit=block.header.gas_limit,
227
        block_hashes=get_last_256_block_hashes(chain),
228
        coinbase=block.header.coinbase,
229
        number=block.header.number,
230
        base_fee_per_gas=block.header.base_fee_per_gas,
231
        time=block.header.timestamp,
232
        prev_randao=block.header.prev_randao,
233
        excess_blob_gas=block.header.excess_blob_gas,
234
        parent_beacon_block_root=block.header.parent_beacon_block_root,
235
    )
236
237
    block_output = apply_body(
238
        block_env=block_env,
239
        transactions=block.transactions,
240
        withdrawals=block.withdrawals,
241
    )
242
    block_state_root = state_root(block_env.state)
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
    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:]

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:
281
    """
282
    Calculates the base fee per gas for the block.
283
284
    Parameters
285
    ----------
286
    block_gas_limit :
287
        Gas limit of the block for which the base fee is being calculated.
288
    parent_gas_limit :
289
        Gas limit of the parent block.
290
    parent_gas_used :
291
        Gas used in the parent block.
292
    parent_base_fee_per_gas :
293
        Base fee per gas of the parent block.
294
295
    Returns
296
    -------
297
    base_fee_per_gas : `Uint`
298
        Base fee per gas for the block.
299
300
    """
301
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
302
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
303
        raise InvalidBlock
304
305
    if parent_gas_used == parent_gas_target:
306
        expected_base_fee_per_gas = parent_base_fee_per_gas
307
    elif parent_gas_used > parent_gas_target:
308
        gas_used_delta = parent_gas_used - parent_gas_target
309
310
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
311
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
312
313
        base_fee_per_gas_delta = max(
314
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
315
            Uint(1),
316
        )
317
318
        expected_base_fee_per_gas = (
319
            parent_base_fee_per_gas + base_fee_per_gas_delta
320
        )
321
    else:
322
        gas_used_delta = parent_gas_target - parent_gas_used
323
324
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
325
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
326
327
        base_fee_per_gas_delta = (
328
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
329
        )
330
331
        expected_base_fee_per_gas = (
332
            parent_base_fee_per_gas - base_fee_per_gas_delta
333
        )
334
335
    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:
339
    """
340
    Verifies a block header.
341
342
    In order to consider a block's header valid, the logic for the
343
    quantities in the header should match the logic for the block itself.
344
    For example the header timestamp should be greater than the block's parent
345
    timestamp because the block was created *after* the parent block.
346
    Additionally, the block's number should be directly following the parent
347
    block's number since it is the next block in the sequence.
348
349
    Parameters
350
    ----------
351
    chain :
352
        History and current state.
353
    header :
354
        Header to check for correctness.
355
356
    """
357
    if header.number < Uint(1):
358
        raise InvalidBlock
359
360
    parent_header = chain.blocks[-1].header
361
362
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
363
    if header.excess_blob_gas != excess_blob_gas:
364
        raise InvalidBlock
365
366
    if header.gas_used > header.gas_limit:
367
        raise InvalidBlock
368
369
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
370
        header.gas_limit,
371
        parent_header.gas_limit,
372
        parent_header.gas_used,
373
        parent_header.base_fee_per_gas,
374
    )
375
    if expected_base_fee_per_gas != header.base_fee_per_gas:
376
        raise InvalidBlock
377
    if header.timestamp <= parent_header.timestamp:
378
        raise InvalidBlock
379
    if header.number != parent_header.number + Uint(1):
380
        raise InvalidBlock
381
    if len(header.extra_data) > 32:
382
        raise InvalidBlock
383
    if header.difficulty != 0:
384
        raise InvalidBlock
385
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
386
        raise InvalidBlock
387
    if header.ommers_hash != EMPTY_OMMER_HASH:
388
        raise InvalidBlock
389
390
    block_parent_hash = keccak256(rlp.encode(parent_header))
391
    if header.parent_hash != block_parent_hash:
392
        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.

Returns

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

Raises

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

def check_transaction(block_env: ethereum.forks.bpo4.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo4.vm.BlockOutput, ​​tx: Transaction) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
400
    """
401
    Check if the transaction is includable in the block.
402
403
    Parameters
404
    ----------
405
    block_env :
406
        The block scoped environment.
407
    block_output :
408
        The block output for the current block.
409
    tx :
410
        The transaction.
411
412
    Returns
413
    -------
414
    sender_address :
415
        The sender of the transaction.
416
    effective_gas_price :
417
        The price to charge for gas when the transaction is executed.
418
    blob_versioned_hashes :
419
        The blob versioned hashes of the transaction.
420
    tx_blob_gas_used:
421
        The blob gas used by the transaction.
422
423
    Raises
424
    ------
425
    InvalidBlock :
426
        If the transaction is not includable.
427
    GasUsedExceedsLimitError :
428
        If the gas used by the transaction exceeds the block's gas limit.
429
    NonceMismatchError :
430
        If the nonce of the transaction is not equal to the sender's nonce.
431
    InsufficientBalanceError :
432
        If the sender's balance is not enough to pay for the transaction.
433
    InvalidSenderError :
434
        If the transaction is from an address that does not exist anymore.
435
    PriorityFeeGreaterThanMaxFeeError :
436
        If the priority fee is greater than the maximum fee per gas.
437
    InsufficientMaxFeePerGasError :
438
        If the maximum fee per gas is insufficient for the transaction.
439
    InsufficientMaxFeePerBlobGasError :
440
        If the maximum fee per blob gas is insufficient for the transaction.
441
    BlobGasLimitExceededError :
442
        If the blob gas used by the transaction exceeds the block's blob gas
443
        limit.
444
    InvalidBlobVersionedHashError :
445
        If the transaction contains a blob versioned hash with an invalid
446
        version.
447
    NoBlobDataError :
448
        If the transaction is a type 3 but has no blobs.
449
    BlobCountExceededError :
450
        If the transaction is a type 3 and has more blobs than the limit.
451
    TransactionTypeContractCreationError:
452
        If the transaction type is not allowed to create contracts.
453
    EmptyAuthorizationListError :
454
        If the transaction is a SetCodeTransaction and the authorization list
455
        is empty.
456
457
    """
458
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
459
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
460
461
    if tx.gas > gas_available:
462
        raise GasUsedExceedsLimitError("gas used exceeds limit")
463
464
    tx_blob_gas_used = calculate_total_blob_gas(tx)
465
    if tx_blob_gas_used > blob_gas_available:
466
        raise BlobGasLimitExceededError("blob gas limit exceeded")
467
468
    sender_address = recover_sender(block_env.chain_id, tx)
469
    sender_account = get_account(block_env.state, sender_address)
470
471
    if isinstance(
472
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
473
    ):
474
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
475
            raise PriorityFeeGreaterThanMaxFeeError(
476
                "priority fee greater than max fee"
477
            )
478
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
479
            raise InsufficientMaxFeePerGasError(
480
                tx.max_fee_per_gas, block_env.base_fee_per_gas
481
            )
482
483
        priority_fee_per_gas = min(
484
            tx.max_priority_fee_per_gas,
485
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
486
        )
487
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
488
        max_gas_fee = tx.gas * tx.max_fee_per_gas
489
    else:
490
        if tx.gas_price < block_env.base_fee_per_gas:
491
            raise InvalidBlock
492
        effective_gas_price = tx.gas_price
493
        max_gas_fee = tx.gas * tx.gas_price
494
495
    if isinstance(tx, BlobTransaction):
496
        blob_count = len(tx.blob_versioned_hashes)
497
        if blob_count == 0:
498
            raise NoBlobDataError("no blob data in transaction")
499
        if blob_count > BLOB_COUNT_LIMIT:
500
            raise BlobCountExceededError(
501
                f"Tx has {blob_count} blobs. Max allowed: {BLOB_COUNT_LIMIT}"
502
            )
503
        for blob_versioned_hash in tx.blob_versioned_hashes:
504
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
505
                raise InvalidBlobVersionedHashError(
506
                    "invalid blob versioned hash"
507
                )
508
509
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
510
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
511
            raise InsufficientMaxFeePerBlobGasError(
512
                "insufficient max fee per blob gas"
513
            )
514
515
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
516
            tx.max_fee_per_blob_gas
517
        )
518
        blob_versioned_hashes = tx.blob_versioned_hashes
519
    else:
520
        blob_versioned_hashes = ()
521
522
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
523
        if not isinstance(tx.to, Address):
524
            raise TransactionTypeContractCreationError(tx)
525
526
    if isinstance(tx, SetCodeTransaction):
527
        if not any(tx.authorizations):
528
            raise EmptyAuthorizationListError("empty authorization list")
529
530
    if sender_account.nonce > Uint(tx.nonce):
531
        raise NonceMismatchError("nonce too low")
532
    elif sender_account.nonce < Uint(tx.nonce):
533
        raise NonceMismatchError("nonce too high")
534
535
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
536
        raise InsufficientBalanceError("insufficient sender balance")
537
    sender_code = get_code(block_env.state, sender_account.code_hash)
538
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
539
        sender_code
540
    ):
541
        raise InvalidSenderError("not EOA")
542
543
    return (
544
        sender_address,
545
        effective_gas_price,
546
        blob_versioned_hashes,
547
        tx_blob_gas_used,
548
    )

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:
557
    """
558
    Make the receipt for a transaction that was executed.
559
560
    Parameters
561
    ----------
562
    tx :
563
        The executed transaction.
564
    error :
565
        Error in the top level frame of the transaction, if any.
566
    cumulative_gas_used :
567
        The total gas used so far in the block after the transaction was
568
        executed.
569
    logs :
570
        The logs produced by the transaction.
571
572
    Returns
573
    -------
574
    receipt :
575
        The receipt for the transaction.
576
577
    """
578
    receipt = Receipt(
579
        succeeded=error is None,
580
        cumulative_gas_used=cumulative_gas_used,
581
        bloom=logs_bloom(logs),
582
        logs=logs,
583
    )
584
585
    return encode_receipt(tx, receipt)

process_system_transaction

Process a system transaction with the given code.

Prefer calling process_checked_system_transaction or process_unchecked_system_transaction depending on whether missing code or an execution error should cause the block to be rejected.

Parameters

block_env : The block scoped environment. target_address : Address of the contract to call. system_contract_code : Code 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_system_transaction(block_env: ethereum.forks.bpo4.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
594
    """
595
    Process a system transaction with the given code.
596
597
    Prefer calling `process_checked_system_transaction` or
598
    `process_unchecked_system_transaction` depending on whether missing code or
599
    an execution error should cause the block to be rejected.
600
601
    Parameters
602
    ----------
603
    block_env :
604
        The block scoped environment.
605
    target_address :
606
        Address of the contract to call.
607
    system_contract_code :
608
        Code of the contract to call.
609
    data :
610
        Data to pass to the contract.
611
612
    Returns
613
    -------
614
    system_tx_output : `MessageCallOutput`
615
        Output of processing the system transaction.
616
617
    """
618
    tx_env = vm.TransactionEnvironment(
619
        origin=SYSTEM_ADDRESS,
620
        gas_price=block_env.base_fee_per_gas,
621
        gas=SYSTEM_TRANSACTION_GAS,
622
        access_list_addresses=set(),
623
        access_list_storage_keys=set(),
624
        transient_storage=TransientStorage(),
625
        blob_versioned_hashes=(),
626
        authorizations=(),
627
        index_in_block=None,
628
        tx_hash=None,
629
    )
630
631
    system_tx_message = Message(
632
        block_env=block_env,
633
        tx_env=tx_env,
634
        caller=SYSTEM_ADDRESS,
635
        target=target_address,
636
        gas=SYSTEM_TRANSACTION_GAS,
637
        value=U256(0),
638
        data=data,
639
        code=system_contract_code,
640
        depth=Uint(0),
641
        current_target=target_address,
642
        code_address=target_address,
643
        should_transfer_value=False,
644
        is_static=False,
645
        accessed_addresses=set(),
646
        accessed_storage_keys=set(),
647
        disable_precompiles=False,
648
        parent_evm=None,
649
    )
650
651
    system_tx_output = process_message_call(system_tx_message)
652
653
    return system_tx_output

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.bpo4.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
661
    """
662
    Process a system transaction and raise an error if the contract does not
663
    contain code or if the transaction fails.
664
665
    Parameters
666
    ----------
667
    block_env :
668
        The block scoped environment.
669
    target_address :
670
        Address of the contract to call.
671
    data :
672
        Data to pass to the contract.
673
674
    Returns
675
    -------
676
    system_tx_output : `MessageCallOutput`
677
        Output of processing the system transaction.
678
679
    """
680
    system_contract_code = get_code(
681
        block_env.state,
682
        get_account(block_env.state, target_address).code_hash,
683
    )
684
685
    if len(system_contract_code) == 0:
686
        raise InvalidBlock(
687
            f"System contract address {target_address.hex()} does not "
688
            "contain code"
689
        )
690
691
    system_tx_output = process_system_transaction(
692
        block_env,
693
        target_address,
694
        system_contract_code,
695
        data,
696
    )
697
698
    if system_tx_output.error:
699
        raise InvalidBlock(
700
            f"System contract ({target_address.hex()}) call failed: "
701
            f"{system_tx_output.error}"
702
        )
703
704
    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.bpo4.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
712
    """
713
    Process a system transaction without checking if the contract contains code
714
    or if the transaction fails.
715
716
    Parameters
717
    ----------
718
    block_env :
719
        The block scoped environment.
720
    target_address :
721
        Address of the contract to call.
722
    data :
723
        Data to pass to the contract.
724
725
    Returns
726
    -------
727
    system_tx_output : `MessageCallOutput`
728
        Output of processing the system transaction.
729
730
    """
731
    system_contract_code = get_code(
732
        block_env.state,
733
        get_account(block_env.state, target_address).code_hash,
734
    )
735
    return process_system_transaction(
736
        block_env,
737
        target_address,
738
        system_contract_code,
739
        data,
740
    )

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.bpo4.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.forks.bpo4.vm.BlockOutput:
748
    """
749
    Executes a block.
750
751
    Many of the contents of a block are stored in data structures called
752
    tries. There is a transactions trie which is similar to a ledger of the
753
    transactions stored in the current block. There is also a receipts trie
754
    which stores the results of executing a transaction, like the post state
755
    and gas used. This function creates and executes the block that is to be
756
    added to the chain.
757
758
    Parameters
759
    ----------
760
    block_env :
761
        The block scoped environment.
762
    transactions :
763
        Transactions included in the block.
764
    withdrawals :
765
        Withdrawals to be processed in the current block.
766
767
    Returns
768
    -------
769
    block_output :
770
        The block output for the current block.
771
772
    """
773
    block_output = vm.BlockOutput()
774
775
    process_unchecked_system_transaction(
776
        block_env=block_env,
777
        target_address=BEACON_ROOTS_ADDRESS,
778
        data=block_env.parent_beacon_block_root,
779
    )
780
781
    process_unchecked_system_transaction(
782
        block_env=block_env,
783
        target_address=HISTORY_STORAGE_ADDRESS,
784
        data=block_env.block_hashes[-1],  # The parent hash
785
    )
786
787
    for i, tx in enumerate(map(decode_transaction, transactions)):
788
        process_transaction(block_env, block_output, tx, Uint(i))
789
790
    process_withdrawals(block_env, block_output, withdrawals)
791
792
    process_general_purpose_requests(
793
        block_env=block_env,
794
        block_output=block_output,
795
    )
796
797
    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.bpo4.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo4.vm.BlockOutput) -> None:
804
    """
805
    Process all the requests in the block.
806
807
    Parameters
808
    ----------
809
    block_env :
810
        The execution environment for the Block.
811
    block_output :
812
        The block output for the current block.
813
814
    """
815
    # Requests are to be in ascending order of request type
816
    deposit_requests = parse_deposit_requests(block_output)
817
    requests_from_execution = block_output.requests
818
    if len(deposit_requests) > 0:
819
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
820
821
    system_withdrawal_tx_output = process_checked_system_transaction(
822
        block_env=block_env,
823
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
824
        data=b"",
825
    )
826
827
    if len(system_withdrawal_tx_output.return_data) > 0:
828
        requests_from_execution.append(
829
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
830
        )
831
832
    system_consolidation_tx_output = process_checked_system_transaction(
833
        block_env=block_env,
834
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
835
        data=b"",
836
    )
837
838
    if len(system_consolidation_tx_output.return_data) > 0:
839
        requests_from_execution.append(
840
            CONSOLIDATION_REQUEST_TYPE
841
            + system_consolidation_tx_output.return_data
842
        )

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.bpo4.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo4.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
851
    """
852
    Execute a transaction against the provided environment.
853
854
    This function processes the actions needed to execute a transaction.
855
    It decrements the sender's account balance after calculating the gas fee
856
    and refunds them the proper amount after execution. Calling contracts,
857
    deploying code, and incrementing nonces are all examples of actions that
858
    happen within this function or from a call made within this function.
859
860
    Accounts that are marked for deletion are processed and destroyed after
861
    execution.
862
863
    Parameters
864
    ----------
865
    block_env :
866
        Environment for the Ethereum Virtual Machine.
867
    block_output :
868
        The block output for the current block.
869
    tx :
870
        Transaction to execute.
871
    index:
872
        Index of the transaction in the block.
873
874
    """
875
    trie_set(
876
        block_output.transactions_trie,
877
        rlp.encode(index),
878
        encode_transaction(tx),
879
    )
880
881
    intrinsic_gas, calldata_floor_gas_cost = validate_transaction(tx)
882
883
    (
884
        sender,
885
        effective_gas_price,
886
        blob_versioned_hashes,
887
        tx_blob_gas_used,
888
    ) = check_transaction(
889
        block_env=block_env,
890
        block_output=block_output,
891
        tx=tx,
892
    )
893
894
    sender_account = get_account(block_env.state, sender)
895
896
    if isinstance(tx, BlobTransaction):
897
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
898
    else:
899
        blob_gas_fee = Uint(0)
900
901
    effective_gas_fee = tx.gas * effective_gas_price
902
903
    gas = tx.gas - intrinsic_gas
904
    increment_nonce(block_env.state, sender)
905
906
    sender_balance_after_gas_fee = (
907
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
908
    )
909
    set_account_balance(
910
        block_env.state, sender, U256(sender_balance_after_gas_fee)
911
    )
912
913
    access_list_addresses = set()
914
    access_list_storage_keys = set()
915
    access_list_addresses.add(block_env.coinbase)
916
    if has_access_list(tx):
917
        for access in tx.access_list:
918
            access_list_addresses.add(access.account)
919
            for slot in access.slots:
920
                access_list_storage_keys.add((access.account, slot))
921
922
    authorizations: Tuple[Authorization, ...] = ()
923
    if isinstance(tx, SetCodeTransaction):
924
        authorizations = tx.authorizations
925
926
    tx_env = vm.TransactionEnvironment(
927
        origin=sender,
928
        gas_price=effective_gas_price,
929
        gas=gas,
930
        access_list_addresses=access_list_addresses,
931
        access_list_storage_keys=access_list_storage_keys,
932
        transient_storage=TransientStorage(),
933
        blob_versioned_hashes=blob_versioned_hashes,
934
        authorizations=authorizations,
935
        index_in_block=index,
936
        tx_hash=get_transaction_hash(encode_transaction(tx)),
937
    )
938
939
    message = prepare_message(block_env, tx_env, tx)
940
941
    tx_output = process_message_call(message)
942
943
    # For EIP-7623 we first calculate the execution_gas_used, which includes
944
    # the execution gas refund.
945
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
946
    tx_gas_refund = min(
947
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
948
    )
949
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
950
951
    # Transactions with less execution_gas_used than the floor pay at the
952
    # floor cost.
953
    tx_gas_used_after_refund = max(
954
        tx_gas_used_after_refund, calldata_floor_gas_cost
955
    )
956
957
    tx_gas_left = tx.gas - tx_gas_used_after_refund
958
    gas_refund_amount = tx_gas_left * effective_gas_price
959
960
    # For non-1559 transactions effective_gas_price == tx.gas_price
961
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
962
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
963
964
    # refund gas
965
    sender_balance_after_refund = get_account(
966
        block_env.state, sender
967
    ).balance + U256(gas_refund_amount)
968
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
969
970
    # transfer miner fees
971
    coinbase_balance_after_mining_fee = get_account(
972
        block_env.state, block_env.coinbase
973
    ).balance + U256(transaction_fee)
974
    set_account_balance(
975
        block_env.state,
976
        block_env.coinbase,
977
        coinbase_balance_after_mining_fee,
978
    )
979
980
    for address in tx_output.accounts_to_delete:
981
        destroy_account(block_env.state, address)
982
983
    block_output.block_gas_used += tx_gas_used_after_refund
984
    block_output.blob_gas_used += tx_blob_gas_used
985
986
    receipt = make_receipt(
987
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
988
    )
989
990
    receipt_key = rlp.encode(Uint(index))
991
    block_output.receipt_keys += (receipt_key,)
992
993
    trie_set(
994
        block_output.receipts_trie,
995
        receipt_key,
996
        receipt,
997
    )
998
999
    block_output.block_logs += tx_output.logs

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.bpo4.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo4.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
1007
    """
1008
    Increase the balance of the withdrawing account.
1009
    """
1010
1011
    def increase_recipient_balance(recipient: Account) -> None:
1012
        recipient.balance += wd.amount * U256(10**9)
1013
1014
    for i, wd in enumerate(withdrawals):
1015
        trie_set(
1016
            block_output.withdrawals_trie,
1017
            rlp.encode(Uint(i)),
1018
            rlp.encode(wd),
1019
        )
1020
1021
        modify_state(block_env.state, wd.address, increase_recipient_balance)

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:
1025
    """
1026
    Validates the gas limit for a block.
1027
1028
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1029
    quotient of the parent block's gas limit and the
1030
    ``LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is passed
1031
    through as a parameter is greater than or equal to the *sum* of the
1032
    parent's gas and the adjustment delta then the limit for gas is too high
1033
    and fails this function's check. Similarly, if the limit is less than or
1034
    equal to the *difference* of the parent's gas and the adjustment delta *or*
1035
    the predefined ``LIMIT_MINIMUM`` then this function's check fails because
1036
    the gas limit doesn't allow for a sufficient or reasonable amount of gas to
1037
    be used on a block.
1038
1039
    Parameters
1040
    ----------
1041
    gas_limit :
1042
        Gas limit to validate.
1043
1044
    parent_gas_limit :
1045
        Gas limit of the parent block.
1046
1047
    Returns
1048
    -------
1049
    check : `bool`
1050
        True if gas limit constraints are satisfied, False otherwise.
1051
1052
    """
1053
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
1054
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1055
        return False
1056
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1057
        return False
1058
    if gas_limit < GasCosts.LIMIT_MINIMUM:
1059
        return False
1060
1061
    return True