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

91
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

92
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

94
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

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

SYSTEM_TRANSACTION_GAS

98
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

99
MAX_BLOB_GAS_PER_BLOCK = U64(1179648)

VERSIONED_HASH_VERSION_KZG

100
VERSIONED_HASH_VERSION_KZG = b"\x01"

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

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

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

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

HISTORY_STORAGE_ADDRESS

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

BlockChain

History and current state of the block chain.

113
@dataclass
class BlockChain:

blocks

119
    blocks: List[Block]

state

120
    state: State

chain_id

121
    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:
125
    """
126
    Transforms the state from the previous hard fork (`old`) into the block
127
    chain object for this hard fork and returns it.
128
129
    When forks need to implement an irregular state transition, this function
130
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
131
    an example.
132
133
    Parameters
134
    ----------
135
    old :
136
        Previous block chain object.
137
138
    Returns
139
    -------
140
    new : `BlockChain`
141
        Upgraded block chain object for this hard fork.
142
143
    """
144
    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]:
148
    """
149
    Obtain the list of hashes of the previous 256 blocks in order of
150
    increasing block number.
151
152
    This function will return less hashes for the first 256 blocks.
153
154
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
155
    therefore this function retrieves them.
156
157
    Parameters
158
    ----------
159
    chain :
160
        History and current state.
161
162
    Returns
163
    -------
164
    recent_block_hashes : `List[Hash32]`
165
        Hashes of the recent 256 blocks in order of increasing block number.
166
167
    """
168
    recent_blocks = chain.blocks[-255:]
169
    # TODO: This function has not been tested rigorously
170
    if len(recent_blocks) == 0:
171
        return []
172
173
    recent_block_hashes = []
174
175
    for block in recent_blocks:
176
        prev_block_hash = block.header.parent_hash
177
        recent_block_hashes.append(prev_block_hash)
178
179
    # We are computing the hash only for the most recent block and not for
180
    # the rest of the blocks as they have successors which have the hash of
181
    # the current block as parent hash.
182
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
183
    recent_block_hashes.append(most_recent_block_hash)
184
185
    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:
189
    """
190
    Attempts to apply a block to an existing block chain.
191
192
    All parts of the block's contents need to be verified before being added
193
    to the chain. Blocks are verified by ensuring that the contents of the
194
    block make logical sense with the contents of the parent block. The
195
    information in the block's header must also match the corresponding
196
    information in the block.
197
198
    To implement Ethereum, in theory clients are only required to store the
199
    most recent 255 blocks of the chain since as far as execution is
200
    concerned, only those blocks are accessed. Practically, however, clients
201
    should store more blocks to handle reorgs.
202
203
    Parameters
204
    ----------
205
    chain :
206
        History and current state.
207
    block :
208
        Block to apply to `chain`.
209
210
    """
211
    validate_header(chain, block.header)
212
    if block.ommers != ():
213
        raise InvalidBlock
214
215
    block_env = vm.BlockEnvironment(
216
        chain_id=chain.chain_id,
217
        state=chain.state,
218
        block_gas_limit=block.header.gas_limit,
219
        block_hashes=get_last_256_block_hashes(chain),
220
        coinbase=block.header.coinbase,
221
        number=block.header.number,
222
        base_fee_per_gas=block.header.base_fee_per_gas,
223
        time=block.header.timestamp,
224
        prev_randao=block.header.prev_randao,
225
        excess_blob_gas=block.header.excess_blob_gas,
226
        parent_beacon_block_root=block.header.parent_beacon_block_root,
227
    )
228
229
    block_output = apply_body(
230
        block_env=block_env,
231
        transactions=block.transactions,
232
        withdrawals=block.withdrawals,
233
    )
234
    block_state_root = state_root(block_env.state)
235
    transactions_root = root(block_output.transactions_trie)
236
    receipt_root = root(block_output.receipts_trie)
237
    block_logs_bloom = logs_bloom(block_output.block_logs)
238
    withdrawals_root = root(block_output.withdrawals_trie)
239
    requests_hash = compute_requests_hash(block_output.requests)
240
241
    if block_output.block_gas_used != block.header.gas_used:
242
        raise InvalidBlock(
243
            f"{block_output.block_gas_used} != {block.header.gas_used}"
244
        )
245
    if transactions_root != block.header.transactions_root:
246
        raise InvalidBlock
247
    if block_state_root != block.header.state_root:
248
        raise InvalidBlock
249
    if receipt_root != block.header.receipt_root:
250
        raise InvalidBlock
251
    if block_logs_bloom != block.header.bloom:
252
        raise InvalidBlock
253
    if withdrawals_root != block.header.withdrawals_root:
254
        raise InvalidBlock
255
    if block_output.blob_gas_used != block.header.blob_gas_used:
256
        raise InvalidBlock
257
    if requests_hash != block.header.requests_hash:
258
        raise InvalidBlock
259
260
    chain.blocks.append(block)
261
    if len(chain.blocks) > 255:
262
        # Real clients have to store more blocks to deal with reorgs, but the
263
        # protocol only requires the last 255
264
        chain.blocks = chain.blocks[-255:]

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:
273
    """
274
    Calculates the base fee per gas for the block.
275
276
    Parameters
277
    ----------
278
    block_gas_limit :
279
        Gas limit of the block for which the base fee is being calculated.
280
    parent_gas_limit :
281
        Gas limit of the parent block.
282
    parent_gas_used :
283
        Gas used in the parent block.
284
    parent_base_fee_per_gas :
285
        Base fee per gas of the parent block.
286
287
    Returns
288
    -------
289
    base_fee_per_gas : `Uint`
290
        Base fee per gas for the block.
291
292
    """
293
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
294
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
295
        raise InvalidBlock
296
297
    if parent_gas_used == parent_gas_target:
298
        expected_base_fee_per_gas = parent_base_fee_per_gas
299
    elif parent_gas_used > parent_gas_target:
300
        gas_used_delta = parent_gas_used - parent_gas_target
301
302
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
303
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
304
305
        base_fee_per_gas_delta = max(
306
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
307
            Uint(1),
308
        )
309
310
        expected_base_fee_per_gas = (
311
            parent_base_fee_per_gas + base_fee_per_gas_delta
312
        )
313
    else:
314
        gas_used_delta = parent_gas_target - parent_gas_used
315
316
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
317
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
318
319
        base_fee_per_gas_delta = (
320
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
321
        )
322
323
        expected_base_fee_per_gas = (
324
            parent_base_fee_per_gas - base_fee_per_gas_delta
325
        )
326
327
    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:
331
    """
332
    Verifies a block header.
333
334
    In order to consider a block's header valid, the logic for the
335
    quantities in the header should match the logic for the block itself.
336
    For example the header timestamp should be greater than the block's parent
337
    timestamp because the block was created *after* the parent block.
338
    Additionally, the block's number should be directly following the parent
339
    block's number since it is the next block in the sequence.
340
341
    Parameters
342
    ----------
343
    chain :
344
        History and current state.
345
    header :
346
        Header to check for correctness.
347
348
    """
349
    if header.number < Uint(1):
350
        raise InvalidBlock
351
352
    parent_header = chain.blocks[-1].header
353
354
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
355
    if header.excess_blob_gas != excess_blob_gas:
356
        raise InvalidBlock
357
358
    if header.gas_used > header.gas_limit:
359
        raise InvalidBlock
360
361
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
362
        header.gas_limit,
363
        parent_header.gas_limit,
364
        parent_header.gas_used,
365
        parent_header.base_fee_per_gas,
366
    )
367
    if expected_base_fee_per_gas != header.base_fee_per_gas:
368
        raise InvalidBlock
369
    if header.timestamp <= parent_header.timestamp:
370
        raise InvalidBlock
371
    if header.number != parent_header.number + Uint(1):
372
        raise InvalidBlock
373
    if len(header.extra_data) > 32:
374
        raise InvalidBlock
375
    if header.difficulty != 0:
376
        raise InvalidBlock
377
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
378
        raise InvalidBlock
379
    if header.ommers_hash != EMPTY_OMMER_HASH:
380
        raise InvalidBlock
381
382
    block_parent_hash = keccak256(rlp.encode(parent_header))
383
    if header.parent_hash != block_parent_hash:
384
        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. 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) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
392
    """
393
    Check if the transaction is includable in the block.
394
395
    Parameters
396
    ----------
397
    block_env :
398
        The block scoped environment.
399
    block_output :
400
        The block output for the current block.
401
    tx :
402
        The transaction.
403
404
    Returns
405
    -------
406
    sender_address :
407
        The sender of the transaction.
408
    effective_gas_price :
409
        The price to charge for gas when the transaction is executed.
410
    blob_versioned_hashes :
411
        The blob versioned hashes of the transaction.
412
    tx_blob_gas_used:
413
        The blob gas used by the transaction.
414
415
    Raises
416
    ------
417
    InvalidBlock :
418
        If the transaction is not includable.
419
    GasUsedExceedsLimitError :
420
        If the gas used by the transaction exceeds the block's gas limit.
421
    NonceMismatchError :
422
        If the nonce of the transaction is not equal to the sender's nonce.
423
    InsufficientBalanceError :
424
        If the sender's balance is not enough to pay for the transaction.
425
    InvalidSenderError :
426
        If the transaction is from an address that does not exist anymore.
427
    PriorityFeeGreaterThanMaxFeeError :
428
        If the priority fee is greater than the maximum fee per gas.
429
    InsufficientMaxFeePerGasError :
430
        If the maximum fee per gas is insufficient for the transaction.
431
    InsufficientMaxFeePerBlobGasError :
432
        If the maximum fee per blob gas is insufficient for the transaction.
433
    BlobGasLimitExceededError :
434
        If the blob gas used by the transaction exceeds the block's blob gas
435
        limit.
436
    InvalidBlobVersionedHashError :
437
        If the transaction contains a blob versioned hash with an invalid
438
        version.
439
    NoBlobDataError :
440
        If the transaction is a type 3 but has no blobs.
441
    TransactionTypeContractCreationError:
442
        If the transaction type is not allowed to create contracts.
443
    EmptyAuthorizationListError :
444
        If the transaction is a SetCodeTransaction and the authorization list
445
        is empty.
446
447
    """
448
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
449
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
450
451
    if tx.gas > gas_available:
452
        raise GasUsedExceedsLimitError("gas used exceeds limit")
453
454
    tx_blob_gas_used = calculate_total_blob_gas(tx)
455
    if tx_blob_gas_used > blob_gas_available:
456
        raise BlobGasLimitExceededError("blob gas limit exceeded")
457
458
    sender_address = recover_sender(block_env.chain_id, tx)
459
    sender_account = get_account(block_env.state, sender_address)
460
461
    if isinstance(
462
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
463
    ):
464
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
465
            raise PriorityFeeGreaterThanMaxFeeError(
466
                "priority fee greater than max fee"
467
            )
468
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
469
            raise InsufficientMaxFeePerGasError(
470
                tx.max_fee_per_gas, block_env.base_fee_per_gas
471
            )
472
473
        priority_fee_per_gas = min(
474
            tx.max_priority_fee_per_gas,
475
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
476
        )
477
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
478
        max_gas_fee = tx.gas * tx.max_fee_per_gas
479
    else:
480
        if tx.gas_price < block_env.base_fee_per_gas:
481
            raise InvalidBlock
482
        effective_gas_price = tx.gas_price
483
        max_gas_fee = tx.gas * tx.gas_price
484
485
    if isinstance(tx, BlobTransaction):
486
        if len(tx.blob_versioned_hashes) == 0:
487
            raise NoBlobDataError("no blob data in transaction")
488
        for blob_versioned_hash in tx.blob_versioned_hashes:
489
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
490
                raise InvalidBlobVersionedHashError(
491
                    "invalid blob versioned hash"
492
                )
493
494
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
495
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
496
            raise InsufficientMaxFeePerBlobGasError(
497
                "insufficient max fee per blob gas"
498
            )
499
500
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
501
            tx.max_fee_per_blob_gas
502
        )
503
        blob_versioned_hashes = tx.blob_versioned_hashes
504
    else:
505
        blob_versioned_hashes = ()
506
507
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
508
        if not isinstance(tx.to, Address):
509
            raise TransactionTypeContractCreationError(tx)
510
511
    if isinstance(tx, SetCodeTransaction):
512
        if not any(tx.authorizations):
513
            raise EmptyAuthorizationListError("empty authorization list")
514
515
    if sender_account.nonce > Uint(tx.nonce):
516
        raise NonceMismatchError("nonce too low")
517
    elif sender_account.nonce < Uint(tx.nonce):
518
        raise NonceMismatchError("nonce too high")
519
520
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
521
        raise InsufficientBalanceError("insufficient sender balance")
522
    sender_code = get_code(block_env.state, sender_account.code_hash)
523
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
524
        sender_code
525
    ):
526
        raise InvalidSenderError("not EOA")
527
528
    return (
529
        sender_address,
530
        effective_gas_price,
531
        blob_versioned_hashes,
532
        tx_blob_gas_used,
533
    )

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:
542
    """
543
    Make the receipt for a transaction that was executed.
544
545
    Parameters
546
    ----------
547
    tx :
548
        The executed transaction.
549
    error :
550
        Error in the top level frame of the transaction, if any.
551
    cumulative_gas_used :
552
        The total gas used so far in the block after the transaction was
553
        executed.
554
    logs :
555
        The logs produced by the transaction.
556
557
    Returns
558
    -------
559
    receipt :
560
        The receipt for the transaction.
561
562
    """
563
    receipt = Receipt(
564
        succeeded=error is None,
565
        cumulative_gas_used=cumulative_gas_used,
566
        bloom=logs_bloom(logs),
567
        logs=logs,
568
    )
569
570
    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.prague.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
579
    """
580
    Process a system transaction with the given code.
581
582
    Prefer calling `process_checked_system_transaction` or
583
    `process_unchecked_system_transaction` depending on whether missing code or
584
    an execution error should cause the block to be rejected.
585
586
    Parameters
587
    ----------
588
    block_env :
589
        The block scoped environment.
590
    target_address :
591
        Address of the contract to call.
592
    system_contract_code :
593
        Code of the contract to call.
594
    data :
595
        Data to pass to the contract.
596
597
    Returns
598
    -------
599
    system_tx_output : `MessageCallOutput`
600
        Output of processing the system transaction.
601
602
    """
603
    tx_env = vm.TransactionEnvironment(
604
        origin=SYSTEM_ADDRESS,
605
        gas_price=block_env.base_fee_per_gas,
606
        gas=SYSTEM_TRANSACTION_GAS,
607
        access_list_addresses=set(),
608
        access_list_storage_keys=set(),
609
        transient_storage=TransientStorage(),
610
        blob_versioned_hashes=(),
611
        authorizations=(),
612
        index_in_block=None,
613
        tx_hash=None,
614
    )
615
616
    system_tx_message = Message(
617
        block_env=block_env,
618
        tx_env=tx_env,
619
        caller=SYSTEM_ADDRESS,
620
        target=target_address,
621
        gas=SYSTEM_TRANSACTION_GAS,
622
        value=U256(0),
623
        data=data,
624
        code=system_contract_code,
625
        depth=Uint(0),
626
        current_target=target_address,
627
        code_address=target_address,
628
        should_transfer_value=False,
629
        is_static=False,
630
        accessed_addresses=set(),
631
        accessed_storage_keys=set(),
632
        disable_precompiles=False,
633
        parent_evm=None,
634
    )
635
636
    system_tx_output = process_message_call(system_tx_message)
637
638
    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.prague.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
646
    """
647
    Process a system transaction and raise an error if the contract does not
648
    contain code or if the transaction fails.
649
650
    Parameters
651
    ----------
652
    block_env :
653
        The block scoped environment.
654
    target_address :
655
        Address of the contract to call.
656
    data :
657
        Data to pass to the contract.
658
659
    Returns
660
    -------
661
    system_tx_output : `MessageCallOutput`
662
        Output of processing the system transaction.
663
664
    """
665
    system_contract_code = get_code(
666
        block_env.state,
667
        get_account(block_env.state, target_address).code_hash,
668
    )
669
670
    if len(system_contract_code) == 0:
671
        raise InvalidBlock(
672
            f"System contract address {target_address.hex()} does not "
673
            "contain code"
674
        )
675
676
    system_tx_output = process_system_transaction(
677
        block_env,
678
        target_address,
679
        system_contract_code,
680
        data,
681
    )
682
683
    if system_tx_output.error:
684
        raise InvalidBlock(
685
            f"System contract ({target_address.hex()}) call failed: "
686
            f"{system_tx_output.error}"
687
        )
688
689
    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:
697
    """
698
    Process a system transaction without checking if the contract contains code
699
    or if the transaction fails.
700
701
    Parameters
702
    ----------
703
    block_env :
704
        The block scoped environment.
705
    target_address :
706
        Address of the contract to call.
707
    data :
708
        Data to pass to the contract.
709
710
    Returns
711
    -------
712
    system_tx_output : `MessageCallOutput`
713
        Output of processing the system transaction.
714
715
    """
716
    system_contract_code = get_code(
717
        block_env.state,
718
        get_account(block_env.state, target_address).code_hash,
719
    )
720
    return process_system_transaction(
721
        block_env,
722
        target_address,
723
        system_contract_code,
724
        data,
725
    )

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:
733
    """
734
    Executes a block.
735
736
    Many of the contents of a block are stored in data structures called
737
    tries. There is a transactions trie which is similar to a ledger of the
738
    transactions stored in the current block. There is also a receipts trie
739
    which stores the results of executing a transaction, like the post state
740
    and gas used. This function creates and executes the block that is to be
741
    added to the chain.
742
743
    Parameters
744
    ----------
745
    block_env :
746
        The block scoped environment.
747
    transactions :
748
        Transactions included in the block.
749
    withdrawals :
750
        Withdrawals to be processed in the current block.
751
752
    Returns
753
    -------
754
    block_output :
755
        The block output for the current block.
756
757
    """
758
    block_output = vm.BlockOutput()
759
760
    process_unchecked_system_transaction(
761
        block_env=block_env,
762
        target_address=BEACON_ROOTS_ADDRESS,
763
        data=block_env.parent_beacon_block_root,
764
    )
765
766
    process_unchecked_system_transaction(
767
        block_env=block_env,
768
        target_address=HISTORY_STORAGE_ADDRESS,
769
        data=block_env.block_hashes[-1],  # The parent hash
770
    )
771
772
    for i, tx in enumerate(map(decode_transaction, transactions)):
773
        process_transaction(block_env, block_output, tx, Uint(i))
774
775
    process_withdrawals(block_env, block_output, withdrawals)
776
777
    process_general_purpose_requests(
778
        block_env=block_env,
779
        block_output=block_output,
780
    )
781
782
    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:
789
    """
790
    Process all the requests in the block.
791
792
    Parameters
793
    ----------
794
    block_env :
795
        The execution environment for the Block.
796
    block_output :
797
        The block output for the current block.
798
799
    """
800
    # Requests are to be in ascending order of request type
801
    deposit_requests = parse_deposit_requests(block_output)
802
    requests_from_execution = block_output.requests
803
    if len(deposit_requests) > 0:
804
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
805
806
    system_withdrawal_tx_output = process_checked_system_transaction(
807
        block_env=block_env,
808
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
809
        data=b"",
810
    )
811
812
    if len(system_withdrawal_tx_output.return_data) > 0:
813
        requests_from_execution.append(
814
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
815
        )
816
817
    system_consolidation_tx_output = process_checked_system_transaction(
818
        block_env=block_env,
819
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
820
        data=b"",
821
    )
822
823
    if len(system_consolidation_tx_output.return_data) > 0:
824
        requests_from_execution.append(
825
            CONSOLIDATION_REQUEST_TYPE
826
            + system_consolidation_tx_output.return_data
827
        )

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

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:
992
    """
993
    Increase the balance of the withdrawing account.
994
    """
995
996
    def increase_recipient_balance(recipient: Account) -> None:
997
        recipient.balance += wd.amount * U256(10**9)
998
999
    for i, wd in enumerate(withdrawals):
1000
        trie_set(
1001
            block_output.withdrawals_trie,
1002
            rlp.encode(Uint(i)),
1003
            rlp.encode(wd),
1004
        )
1005
1006
        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:
1010
    """
1011
    Validates the gas limit for a block.
1012
1013
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1014
    quotient of the parent block's gas limit and the
1015
    ``LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is passed
1016
    through as a parameter is greater than or equal to the *sum* of the
1017
    parent's gas and the adjustment delta then the limit for gas is too high
1018
    and fails this function's check. Similarly, if the limit is less than or
1019
    equal to the *difference* of the parent's gas and the adjustment delta *or*
1020
    the predefined ``LIMIT_MINIMUM`` then this function's check fails because
1021
    the gas limit doesn't allow for a sufficient or reasonable amount of gas to
1022
    be used on a block.
1023
1024
    Parameters
1025
    ----------
1026
    gas_limit :
1027
        Gas limit to validate.
1028
1029
    parent_gas_limit :
1030
        Gas limit of the parent block.
1031
1032
    Returns
1033
    -------
1034
    check : `bool`
1035
        True if gas limit constraints are satisfied, False otherwise.
1036
1037
    """
1038
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
1039
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1040
        return False
1041
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1042
        return False
1043
    if gas_limit < GasCosts.LIMIT_MINIMUM:
1044
        return False
1045
1046
    return True