ethereum.forks.bpo2.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

93
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

94
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

95
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

96
GAS_LIMIT_MINIMUM = Uint(5000)

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 = BLOB_SCHEDULE_MAX * GAS_PER_BLOB

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
)

MAX_BLOCK_SIZE

115
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

116
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

117
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

118
BLOB_COUNT_LIMIT = 6

BlockChain

History and current state of the block chain.

121
@dataclass
class BlockChain:

blocks

127
    blocks: List[Block]

state

128
    state: State

chain_id

129
    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:
133
    """
134
    Transforms the state from the previous hard fork (`old`) into the block
135
    chain object for this hard fork and returns it.
136
137
    When forks need to implement an irregular state transition, this function
138
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
139
    an example.
140
141
    Parameters
142
    ----------
143
    old :
144
        Previous block chain object.
145
146
    Returns
147
    -------
148
    new : `BlockChain`
149
        Upgraded block chain object for this hard fork.
150
151
    """
152
    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]:
156
    """
157
    Obtain the list of hashes of the previous 256 blocks in order of
158
    increasing block number.
159
160
    This function will return less hashes for the first 256 blocks.
161
162
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
163
    therefore this function retrieves them.
164
165
    Parameters
166
    ----------
167
    chain :
168
        History and current state.
169
170
    Returns
171
    -------
172
    recent_block_hashes : `List[Hash32]`
173
        Hashes of the recent 256 blocks in order of increasing block number.
174
175
    """
176
    recent_blocks = chain.blocks[-255:]
177
    # TODO: This function has not been tested rigorously
178
    if len(recent_blocks) == 0:
179
        return []
180
181
    recent_block_hashes = []
182
183
    for block in recent_blocks:
184
        prev_block_hash = block.header.parent_hash
185
        recent_block_hashes.append(prev_block_hash)
186
187
    # We are computing the hash only for the most recent block and not for
188
    # the rest of the blocks as they have successors which have the hash of
189
    # the current block as parent hash.
190
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
191
    recent_block_hashes.append(most_recent_block_hash)
192
193
    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:
197
    """
198
    Attempts to apply a block to an existing block chain.
199
200
    All parts of the block's contents need to be verified before being added
201
    to the chain. Blocks are verified by ensuring that the contents of the
202
    block make logical sense with the contents of the parent block. The
203
    information in the block's header must also match the corresponding
204
    information in the block.
205
206
    To implement Ethereum, in theory clients are only required to store the
207
    most recent 255 blocks of the chain since as far as execution is
208
    concerned, only those blocks are accessed. Practically, however, clients
209
    should store more blocks to handle reorgs.
210
211
    Parameters
212
    ----------
213
    chain :
214
        History and current state.
215
    block :
216
        Block to apply to `chain`.
217
218
    """
219
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
220
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
221
222
    validate_header(chain, block.header)
223
    if block.ommers != ():
224
        raise InvalidBlock
225
226
    block_env = vm.BlockEnvironment(
227
        chain_id=chain.chain_id,
228
        state=chain.state,
229
        block_gas_limit=block.header.gas_limit,
230
        block_hashes=get_last_256_block_hashes(chain),
231
        coinbase=block.header.coinbase,
232
        number=block.header.number,
233
        base_fee_per_gas=block.header.base_fee_per_gas,
234
        time=block.header.timestamp,
235
        prev_randao=block.header.prev_randao,
236
        excess_blob_gas=block.header.excess_blob_gas,
237
        parent_beacon_block_root=block.header.parent_beacon_block_root,
238
    )
239
240
    block_output = apply_body(
241
        block_env=block_env,
242
        transactions=block.transactions,
243
        withdrawals=block.withdrawals,
244
    )
245
    block_state_root = state_root(block_env.state)
246
    transactions_root = root(block_output.transactions_trie)
247
    receipt_root = root(block_output.receipts_trie)
248
    block_logs_bloom = logs_bloom(block_output.block_logs)
249
    withdrawals_root = root(block_output.withdrawals_trie)
250
    requests_hash = compute_requests_hash(block_output.requests)
251
252
    if block_output.block_gas_used != block.header.gas_used:
253
        raise InvalidBlock(
254
            f"{block_output.block_gas_used} != {block.header.gas_used}"
255
        )
256
    if transactions_root != block.header.transactions_root:
257
        raise InvalidBlock
258
    if block_state_root != block.header.state_root:
259
        raise InvalidBlock
260
    if receipt_root != block.header.receipt_root:
261
        raise InvalidBlock
262
    if block_logs_bloom != block.header.bloom:
263
        raise InvalidBlock
264
    if withdrawals_root != block.header.withdrawals_root:
265
        raise InvalidBlock
266
    if block_output.blob_gas_used != block.header.blob_gas_used:
267
        raise InvalidBlock
268
    if requests_hash != block.header.requests_hash:
269
        raise InvalidBlock
270
271
    chain.blocks.append(block)
272
    if len(chain.blocks) > 255:
273
        # Real clients have to store more blocks to deal with reorgs, but the
274
        # protocol only requires the last 255
275
        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:
284
    """
285
    Calculates the base fee per gas for the block.
286
287
    Parameters
288
    ----------
289
    block_gas_limit :
290
        Gas limit of the block for which the base fee is being calculated.
291
    parent_gas_limit :
292
        Gas limit of the parent block.
293
    parent_gas_used :
294
        Gas used in the parent block.
295
    parent_base_fee_per_gas :
296
        Base fee per gas of the parent block.
297
298
    Returns
299
    -------
300
    base_fee_per_gas : `Uint`
301
        Base fee per gas for the block.
302
303
    """
304
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
305
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
306
        raise InvalidBlock
307
308
    if parent_gas_used == parent_gas_target:
309
        expected_base_fee_per_gas = parent_base_fee_per_gas
310
    elif parent_gas_used > parent_gas_target:
311
        gas_used_delta = parent_gas_used - parent_gas_target
312
313
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
314
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
315
316
        base_fee_per_gas_delta = max(
317
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
318
            Uint(1),
319
        )
320
321
        expected_base_fee_per_gas = (
322
            parent_base_fee_per_gas + base_fee_per_gas_delta
323
        )
324
    else:
325
        gas_used_delta = parent_gas_target - parent_gas_used
326
327
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
328
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
329
330
        base_fee_per_gas_delta = (
331
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
332
        )
333
334
        expected_base_fee_per_gas = (
335
            parent_base_fee_per_gas - base_fee_per_gas_delta
336
        )
337
338
    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:
342
    """
343
    Verifies a block header.
344
345
    In order to consider a block's header valid, the logic for the
346
    quantities in the header should match the logic for the block itself.
347
    For example the header timestamp should be greater than the block's parent
348
    timestamp because the block was created *after* the parent block.
349
    Additionally, the block's number should be directly following the parent
350
    block's number since it is the next block in the sequence.
351
352
    Parameters
353
    ----------
354
    chain :
355
        History and current state.
356
    header :
357
        Header to check for correctness.
358
359
    """
360
    if header.number < Uint(1):
361
        raise InvalidBlock
362
363
    parent_header = chain.blocks[-1].header
364
365
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
366
    if header.excess_blob_gas != excess_blob_gas:
367
        raise InvalidBlock
368
369
    if header.gas_used > header.gas_limit:
370
        raise InvalidBlock
371
372
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
373
        header.gas_limit,
374
        parent_header.gas_limit,
375
        parent_header.gas_used,
376
        parent_header.base_fee_per_gas,
377
    )
378
    if expected_base_fee_per_gas != header.base_fee_per_gas:
379
        raise InvalidBlock
380
    if header.timestamp <= parent_header.timestamp:
381
        raise InvalidBlock
382
    if header.number != parent_header.number + Uint(1):
383
        raise InvalidBlock
384
    if len(header.extra_data) > 32:
385
        raise InvalidBlock
386
    if header.difficulty != 0:
387
        raise InvalidBlock
388
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
389
        raise InvalidBlock
390
    if header.ommers_hash != EMPTY_OMMER_HASH:
391
        raise InvalidBlock
392
393
    block_parent_hash = keccak256(rlp.encode(parent_header))
394
    if header.parent_hash != block_parent_hash:
395
        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.bpo2.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo2.vm.BlockOutput, ​​tx: Transaction) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
403
    """
404
    Check if the transaction is includable in the block.
405
406
    Parameters
407
    ----------
408
    block_env :
409
        The block scoped environment.
410
    block_output :
411
        The block output for the current block.
412
    tx :
413
        The transaction.
414
415
    Returns
416
    -------
417
    sender_address :
418
        The sender of the transaction.
419
    effective_gas_price :
420
        The price to charge for gas when the transaction is executed.
421
    blob_versioned_hashes :
422
        The blob versioned hashes of the transaction.
423
    tx_blob_gas_used:
424
        The blob gas used by the transaction.
425
426
    Raises
427
    ------
428
    InvalidBlock :
429
        If the transaction is not includable.
430
    GasUsedExceedsLimitError :
431
        If the gas used by the transaction exceeds the block's gas limit.
432
    NonceMismatchError :
433
        If the nonce of the transaction is not equal to the sender's nonce.
434
    InsufficientBalanceError :
435
        If the sender's balance is not enough to pay for the transaction.
436
    InvalidSenderError :
437
        If the transaction is from an address that does not exist anymore.
438
    PriorityFeeGreaterThanMaxFeeError :
439
        If the priority fee is greater than the maximum fee per gas.
440
    InsufficientMaxFeePerGasError :
441
        If the maximum fee per gas is insufficient for the transaction.
442
    InsufficientMaxFeePerBlobGasError :
443
        If the maximum fee per blob gas is insufficient for the transaction.
444
    BlobGasLimitExceededError :
445
        If the blob gas used by the transaction exceeds the block's blob gas
446
        limit.
447
    InvalidBlobVersionedHashError :
448
        If the transaction contains a blob versioned hash with an invalid
449
        version.
450
    NoBlobDataError :
451
        If the transaction is a type 3 but has no blobs.
452
    BlobCountExceededError :
453
        If the transaction is a type 3 and has more blobs than the limit.
454
    TransactionTypeContractCreationError:
455
        If the transaction type is not allowed to create contracts.
456
    EmptyAuthorizationListError :
457
        If the transaction is a SetCodeTransaction and the authorization list
458
        is empty.
459
460
    """
461
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
462
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
463
464
    if tx.gas > gas_available:
465
        raise GasUsedExceedsLimitError("gas used exceeds limit")
466
467
    tx_blob_gas_used = calculate_total_blob_gas(tx)
468
    if tx_blob_gas_used > blob_gas_available:
469
        raise BlobGasLimitExceededError("blob gas limit exceeded")
470
471
    sender_address = recover_sender(block_env.chain_id, tx)
472
    sender_account = get_account(block_env.state, sender_address)
473
474
    if isinstance(
475
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
476
    ):
477
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
478
            raise PriorityFeeGreaterThanMaxFeeError(
479
                "priority fee greater than max fee"
480
            )
481
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
482
            raise InsufficientMaxFeePerGasError(
483
                tx.max_fee_per_gas, block_env.base_fee_per_gas
484
            )
485
486
        priority_fee_per_gas = min(
487
            tx.max_priority_fee_per_gas,
488
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
489
        )
490
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
491
        max_gas_fee = tx.gas * tx.max_fee_per_gas
492
    else:
493
        if tx.gas_price < block_env.base_fee_per_gas:
494
            raise InvalidBlock
495
        effective_gas_price = tx.gas_price
496
        max_gas_fee = tx.gas * tx.gas_price
497
498
    if isinstance(tx, BlobTransaction):
499
        blob_count = len(tx.blob_versioned_hashes)
500
        if blob_count == 0:
501
            raise NoBlobDataError("no blob data in transaction")
502
        if blob_count > BLOB_COUNT_LIMIT:
503
            raise BlobCountExceededError(
504
                f"Tx has {blob_count} blobs. Max allowed: {BLOB_COUNT_LIMIT}"
505
            )
506
        for blob_versioned_hash in tx.blob_versioned_hashes:
507
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
508
                raise InvalidBlobVersionedHashError(
509
                    "invalid blob versioned hash"
510
                )
511
512
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
513
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
514
            raise InsufficientMaxFeePerBlobGasError(
515
                "insufficient max fee per blob gas"
516
            )
517
518
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
519
            tx.max_fee_per_blob_gas
520
        )
521
        blob_versioned_hashes = tx.blob_versioned_hashes
522
    else:
523
        blob_versioned_hashes = ()
524
525
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
526
        if not isinstance(tx.to, Address):
527
            raise TransactionTypeContractCreationError(tx)
528
529
    if isinstance(tx, SetCodeTransaction):
530
        if not any(tx.authorizations):
531
            raise EmptyAuthorizationListError("empty authorization list")
532
533
    if sender_account.nonce > Uint(tx.nonce):
534
        raise NonceMismatchError("nonce too low")
535
    elif sender_account.nonce < Uint(tx.nonce):
536
        raise NonceMismatchError("nonce too high")
537
538
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
539
        raise InsufficientBalanceError("insufficient sender balance")
540
    sender_code = get_code(block_env.state, sender_account.code_hash)
541
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
542
        sender_code
543
    ):
544
        raise InvalidSenderError("not EOA")
545
546
    return (
547
        sender_address,
548
        effective_gas_price,
549
        blob_versioned_hashes,
550
        tx_blob_gas_used,
551
    )

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:
560
    """
561
    Make the receipt for a transaction that was executed.
562
563
    Parameters
564
    ----------
565
    tx :
566
        The executed transaction.
567
    error :
568
        Error in the top level frame of the transaction, if any.
569
    cumulative_gas_used :
570
        The total gas used so far in the block after the transaction was
571
        executed.
572
    logs :
573
        The logs produced by the transaction.
574
575
    Returns
576
    -------
577
    receipt :
578
        The receipt for the transaction.
579
580
    """
581
    receipt = Receipt(
582
        succeeded=error is None,
583
        cumulative_gas_used=cumulative_gas_used,
584
        bloom=logs_bloom(logs),
585
        logs=logs,
586
    )
587
588
    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.bpo2.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
597
    """
598
    Process a system transaction with the given code.
599
600
    Prefer calling `process_checked_system_transaction` or
601
    `process_unchecked_system_transaction` depending on whether missing code or
602
    an execution error should cause the block to be rejected.
603
604
    Parameters
605
    ----------
606
    block_env :
607
        The block scoped environment.
608
    target_address :
609
        Address of the contract to call.
610
    system_contract_code :
611
        Code of the contract to call.
612
    data :
613
        Data to pass to the contract.
614
615
    Returns
616
    -------
617
    system_tx_output : `MessageCallOutput`
618
        Output of processing the system transaction.
619
620
    """
621
    tx_env = vm.TransactionEnvironment(
622
        origin=SYSTEM_ADDRESS,
623
        gas_price=block_env.base_fee_per_gas,
624
        gas=SYSTEM_TRANSACTION_GAS,
625
        access_list_addresses=set(),
626
        access_list_storage_keys=set(),
627
        transient_storage=TransientStorage(),
628
        blob_versioned_hashes=(),
629
        authorizations=(),
630
        index_in_block=None,
631
        tx_hash=None,
632
    )
633
634
    system_tx_message = Message(
635
        block_env=block_env,
636
        tx_env=tx_env,
637
        caller=SYSTEM_ADDRESS,
638
        target=target_address,
639
        gas=SYSTEM_TRANSACTION_GAS,
640
        value=U256(0),
641
        data=data,
642
        code=system_contract_code,
643
        depth=Uint(0),
644
        current_target=target_address,
645
        code_address=target_address,
646
        should_transfer_value=False,
647
        is_static=False,
648
        accessed_addresses=set(),
649
        accessed_storage_keys=set(),
650
        disable_precompiles=False,
651
        parent_evm=None,
652
    )
653
654
    system_tx_output = process_message_call(system_tx_message)
655
656
    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.bpo2.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
664
    """
665
    Process a system transaction and raise an error if the contract does not
666
    contain code or if the transaction fails.
667
668
    Parameters
669
    ----------
670
    block_env :
671
        The block scoped environment.
672
    target_address :
673
        Address of the contract to call.
674
    data :
675
        Data to pass to the contract.
676
677
    Returns
678
    -------
679
    system_tx_output : `MessageCallOutput`
680
        Output of processing the system transaction.
681
682
    """
683
    system_contract_code = get_code(
684
        block_env.state,
685
        get_account(block_env.state, target_address).code_hash,
686
    )
687
688
    if len(system_contract_code) == 0:
689
        raise InvalidBlock(
690
            f"System contract address {target_address.hex()} does not "
691
            "contain code"
692
        )
693
694
    system_tx_output = process_system_transaction(
695
        block_env,
696
        target_address,
697
        system_contract_code,
698
        data,
699
    )
700
701
    if system_tx_output.error:
702
        raise InvalidBlock(
703
            f"System contract ({target_address.hex()}) call failed: "
704
            f"{system_tx_output.error}"
705
        )
706
707
    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.bpo2.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
715
    """
716
    Process a system transaction without checking if the contract contains code
717
    or if the transaction fails.
718
719
    Parameters
720
    ----------
721
    block_env :
722
        The block scoped environment.
723
    target_address :
724
        Address of the contract to call.
725
    data :
726
        Data to pass to the contract.
727
728
    Returns
729
    -------
730
    system_tx_output : `MessageCallOutput`
731
        Output of processing the system transaction.
732
733
    """
734
    system_contract_code = get_code(
735
        block_env.state,
736
        get_account(block_env.state, target_address).code_hash,
737
    )
738
    return process_system_transaction(
739
        block_env,
740
        target_address,
741
        system_contract_code,
742
        data,
743
    )

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

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

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.bpo2.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo2.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
1018
    """
1019
    Increase the balance of the withdrawing account.
1020
    """
1021
1022
    def increase_recipient_balance(recipient: Account) -> None:
1023
        recipient.balance += wd.amount * U256(10**9)
1024
1025
    for i, wd in enumerate(withdrawals):
1026
        trie_set(
1027
            block_output.withdrawals_trie,
1028
            rlp.encode(Uint(i)),
1029
            rlp.encode(wd),
1030
        )
1031
1032
        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 GAS_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 GAS_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:
1036
    """
1037
    Validates the gas limit for a block.
1038
1039
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1040
    quotient of the parent block's gas limit and the
1041
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1042
    passed through as a parameter is greater than or equal to the *sum* of
1043
    the parent's gas and the adjustment delta then the limit for gas is too
1044
    high and fails this function's check. Similarly, if the limit is less
1045
    than or equal to the *difference* of the parent's gas and the adjustment
1046
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1047
    check fails because the gas limit doesn't allow for a sufficient or
1048
    reasonable amount of gas to be used on a block.
1049
1050
    Parameters
1051
    ----------
1052
    gas_limit :
1053
        Gas limit to validate.
1054
1055
    parent_gas_limit :
1056
        Gas limit of the parent block.
1057
1058
    Returns
1059
    -------
1060
    check : `bool`
1061
        True if gas limit constraints are satisfied, False otherwise.
1062
1063
    """
1064
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1065
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1066
        return False
1067
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1068
        return False
1069
    if gas_limit < GAS_LIMIT_MINIMUM:
1070
        return False
1071
1072
    return True