ethereum.cancun.forkethereum.prague.fork

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

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

89
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

90
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

91
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

92
GAS_LIMIT_MINIMUM = Uint(5000)

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

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

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

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

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

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.cancun.vm.BlockEnvironmentethereum.prague.vm.BlockEnvironment, ​​block_output: ethereum.cancun.vm.BlockOutputethereum.prague.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
978
    """
979
    Increase the balance of the withdrawing account.
980
    """
981
982
    def increase_recipient_balance(recipient: Account) -> None:
983
        recipient.balance += wd.amount * U256(10**9)
984
985
    for i, wd in enumerate(withdrawals):
986
        trie_set(
987
            block_output.withdrawals_trie,
988
            rlp.encode(Uint(i)),
989
            rlp.encode(wd),
990
        )
991
992
        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:
996
    """
997
    Validates the gas limit for a block.
998
999
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1000
    quotient of the parent block's gas limit and the
1001
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1002
    passed through as a parameter is greater than or equal to the *sum* of
1003
    the parent's gas and the adjustment delta then the limit for gas is too
1004
    high and fails this function's check. Similarly, if the limit is less
1005
    than or equal to the *difference* of the parent's gas and the adjustment
1006
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1007
    check fails because the gas limit doesn't allow for a sufficient or
1008
    reasonable amount of gas to be used on a block.
1009
1010
    Parameters
1011
    ----------
1012
    gas_limit :
1013
        Gas limit to validate.
1014
1015
    parent_gas_limit :
1016
        Gas limit of the parent block.
1017
1018
    Returns
1019
    -------
1020
    check : `bool`
1021
        True if gas limit constraints are satisfied, False otherwise.
1022
    """
1023
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1024
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1025
        return False
1026
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1027
        return False
1028
    if gas_limit < GAS_LIMIT_MINIMUM:
1029
        return False
1030
1031
    return True