ethereum.prague.fork

Ethereum Specification ^^^^^^^^^^^^^^^^^^^^^^

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

90
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

91
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

92
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

93
GAS_LIMIT_MINIMUM = Uint(5000)

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 = U64(1179648)

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
)

BlockChain

History and current state of the block chain.

114
@dataclass
class BlockChain:

blocks

120
    blocks: List[Block]

state

121
    state: State

chain_id

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

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:
534
    """
535
    Make the receipt for a transaction that was executed.
536
537
    Parameters
538
    ----------
539
    tx :
540
        The executed transaction.
541
    error :
542
        Error in the top level frame of the transaction, if any.
543
    cumulative_gas_used :
544
        The total gas used so far in the block after the transaction was
545
        executed.
546
    logs :
547
        The logs produced by the transaction.
548
549
    Returns
550
    -------
551
    receipt :
552
        The receipt for the transaction.
553
    """
554
    receipt = Receipt(
555
        succeeded=error is None,
556
        cumulative_gas_used=cumulative_gas_used,
557
        bloom=logs_bloom(logs),
558
        logs=logs,
559
    )
560
561
    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.prague.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
570
    """
571
    Process a system transaction with the given code.
572
573
    Prefer calling `process_checked_system_transaction` or
574
    `process_unchecked_system_transaction` depending on whether missing code or
575
    an execution error should cause the block to be rejected.
576
577
    Parameters
578
    ----------
579
    block_env :
580
        The block scoped environment.
581
    target_address :
582
        Address of the contract to call.
583
    system_contract_code :
584
        Code of the contract to call.
585
    data :
586
        Data to pass to the contract.
587
588
    Returns
589
    -------
590
    system_tx_output : `MessageCallOutput`
591
        Output of processing the system transaction.
592
    """
593
    tx_env = vm.TransactionEnvironment(
594
        origin=SYSTEM_ADDRESS,
595
        gas_price=block_env.base_fee_per_gas,
596
        gas=SYSTEM_TRANSACTION_GAS,
597
        access_list_addresses=set(),
598
        access_list_storage_keys=set(),
599
        transient_storage=TransientStorage(),
600
        blob_versioned_hashes=(),
601
        authorizations=(),
602
        index_in_block=None,
603
        tx_hash=None,
604
    )
605
606
    system_tx_message = Message(
607
        block_env=block_env,
608
        tx_env=tx_env,
609
        caller=SYSTEM_ADDRESS,
610
        target=target_address,
611
        gas=SYSTEM_TRANSACTION_GAS,
612
        value=U256(0),
613
        data=data,
614
        code=system_contract_code,
615
        depth=Uint(0),
616
        current_target=target_address,
617
        code_address=target_address,
618
        should_transfer_value=False,
619
        is_static=False,
620
        accessed_addresses=set(),
621
        accessed_storage_keys=set(),
622
        disable_precompiles=False,
623
        parent_evm=None,
624
    )
625
626
    system_tx_output = process_message_call(system_tx_message)
627
628
    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.prague.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
636
    """
637
    Process a system transaction and raise an error if the contract does not
638
    contain code or if the transaction fails.
639
640
    Parameters
641
    ----------
642
    block_env :
643
        The block scoped environment.
644
    target_address :
645
        Address of the contract to call.
646
    data :
647
        Data to pass to the contract.
648
649
    Returns
650
    -------
651
    system_tx_output : `MessageCallOutput`
652
        Output of processing the system transaction.
653
    """
654
    system_contract_code = get_account(block_env.state, target_address).code
655
656
    if len(system_contract_code) == 0:
657
        raise InvalidBlock(
658
            f"System contract address {target_address.hex()} does not "
659
            "contain code"
660
        )
661
662
    system_tx_output = process_system_transaction(
663
        block_env,
664
        target_address,
665
        system_contract_code,
666
        data,
667
    )
668
669
    if system_tx_output.error:
670
        raise InvalidBlock(
671
            f"System contract ({target_address.hex()}) call failed: "
672
            f"{system_tx_output.error}"
673
        )
674
675
    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.prague.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
683
    """
684
    Process a system transaction without checking if the contract contains code
685
    or if the transaction fails.
686
687
    Parameters
688
    ----------
689
    block_env :
690
        The block scoped environment.
691
    target_address :
692
        Address of the contract to call.
693
    data :
694
        Data to pass to the contract.
695
696
    Returns
697
    -------
698
    system_tx_output : `MessageCallOutput`
699
        Output of processing the system transaction.
700
    """
701
    system_contract_code = get_account(block_env.state, target_address).code
702
    return process_system_transaction(
703
        block_env,
704
        target_address,
705
        system_contract_code,
706
        data,
707
    )

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.prague.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.prague.vm.BlockOutput:
715
    """
716
    Executes a block.
717
718
    Many of the contents of a block are stored in data structures called
719
    tries. There is a transactions trie which is similar to a ledger of the
720
    transactions stored in the current block. There is also a receipts trie
721
    which stores the results of executing a transaction, like the post state
722
    and gas used. This function creates and executes the block that is to be
723
    added to the chain.
724
725
    Parameters
726
    ----------
727
    block_env :
728
        The block scoped environment.
729
    transactions :
730
        Transactions included in the block.
731
    withdrawals :
732
        Withdrawals to be processed in the current block.
733
734
    Returns
735
    -------
736
    block_output :
737
        The block output for the current block.
738
    """
739
    block_output = vm.BlockOutput()
740
741
    process_unchecked_system_transaction(
742
        block_env=block_env,
743
        target_address=BEACON_ROOTS_ADDRESS,
744
        data=block_env.parent_beacon_block_root,
745
    )
746
747
    process_unchecked_system_transaction(
748
        block_env=block_env,
749
        target_address=HISTORY_STORAGE_ADDRESS,
750
        data=block_env.block_hashes[-1],  # The parent hash
751
    )
752
753
    for i, tx in enumerate(map(decode_transaction, transactions)):
754
        process_transaction(block_env, block_output, tx, Uint(i))
755
756
    process_withdrawals(block_env, block_output, withdrawals)
757
758
    process_general_purpose_requests(
759
        block_env=block_env,
760
        block_output=block_output,
761
    )
762
763
    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.prague.vm.BlockEnvironment, ​​block_output: ethereum.prague.vm.BlockOutput) -> None:
770
    """
771
    Process all the requests in the block.
772
773
    Parameters
774
    ----------
775
    block_env :
776
        The execution environment for the Block.
777
    block_output :
778
        The block output for the current block.
779
    """
780
    # Requests are to be in ascending order of request type
781
    deposit_requests = parse_deposit_requests(block_output)
782
    requests_from_execution = block_output.requests
783
    if len(deposit_requests) > 0:
784
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
785
786
    system_withdrawal_tx_output = process_checked_system_transaction(
787
        block_env=block_env,
788
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
789
        data=b"",
790
    )
791
792
    if len(system_withdrawal_tx_output.return_data) > 0:
793
        requests_from_execution.append(
794
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
795
        )
796
797
    system_consolidation_tx_output = process_checked_system_transaction(
798
        block_env=block_env,
799
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
800
        data=b"",
801
    )
802
803
    if len(system_consolidation_tx_output.return_data) > 0:
804
        requests_from_execution.append(
805
            CONSOLIDATION_REQUEST_TYPE
806
            + system_consolidation_tx_output.return_data
807
        )

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

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.prague.vm.BlockEnvironment, ​​block_output: ethereum.prague.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
982
    """
983
    Increase the balance of the withdrawing account.
984
    """
985
986
    def increase_recipient_balance(recipient: Account) -> None:
987
        recipient.balance += wd.amount * U256(10**9)
988
989
    for i, wd in enumerate(withdrawals):
990
        trie_set(
991
            block_output.withdrawals_trie,
992
            rlp.encode(Uint(i)),
993
            rlp.encode(wd),
994
        )
995
996
        modify_state(block_env.state, wd.address, increase_recipient_balance)
997
998
        if account_exists_and_is_empty(block_env.state, wd.address):
999
            destroy_account(block_env.state, wd.address)

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:
1003
    """
1004
    Validates the gas limit for a block.
1005
1006
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1007
    quotient of the parent block's gas limit and the
1008
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1009
    passed through as a parameter is greater than or equal to the *sum* of
1010
    the parent's gas and the adjustment delta then the limit for gas is too
1011
    high and fails this function's check. Similarly, if the limit is less
1012
    than or equal to the *difference* of the parent's gas and the adjustment
1013
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1014
    check fails because the gas limit doesn't allow for a sufficient or
1015
    reasonable amount of gas to be used on a block.
1016
1017
    Parameters
1018
    ----------
1019
    gas_limit :
1020
        Gas limit to validate.
1021
1022
    parent_gas_limit :
1023
        Gas limit of the parent block.
1024
1025
    Returns
1026
    -------
1027
    check : `bool`
1028
        True if gas limit constraints are satisfied, False otherwise.
1029
    """
1030
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1031
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1032
        return False
1033
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1034
        return False
1035
    if gas_limit < GAS_LIMIT_MINIMUM:
1036
        return False
1037
1038
    return True