ethereum.forks.cancun.forkethereum.forks.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

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

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

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

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

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.cancun.vm.BlockEnvironmentethereum.forks.prague.vm.BlockEnvironment, ​​block_output: ethereum.forks.cancun.vm.BlockOutputethereum.forks.prague.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
1001
    """
1002
    Increase the balance of the withdrawing account.
1003
    """
1004
1005
    def increase_recipient_balance(recipient: Account) -> None:
1006
        recipient.balance += wd.amount * U256(10**9)
1007
1008
    for i, wd in enumerate(withdrawals):
1009
        trie_set(
1010
            block_output.withdrawals_trie,
1011
            rlp.encode(Uint(i)),
1012
            rlp.encode(wd),
1013
        )
1014
1015
        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:
1019
    """
1020
    Validates the gas limit for a block.
1021
1022
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1023
    quotient of the parent block's gas limit and the
1024
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1025
    passed through as a parameter is greater than or equal to the *sum* of
1026
    the parent's gas and the adjustment delta then the limit for gas is too
1027
    high and fails this function's check. Similarly, if the limit is less
1028
    than or equal to the *difference* of the parent's gas and the adjustment
1029
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1030
    check fails because the gas limit doesn't allow for a sufficient or
1031
    reasonable amount of gas to be used on a block.
1032
1033
    Parameters
1034
    ----------
1035
    gas_limit :
1036
        Gas limit to validate.
1037
1038
    parent_gas_limit :
1039
        Gas limit of the parent block.
1040
1041
    Returns
1042
    -------
1043
    check : `bool`
1044
        True if gas limit constraints are satisfied, False otherwise.
1045
1046
    """
1047
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1048
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1049
        return False
1050
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1051
        return False
1052
    if gas_limit < GAS_LIMIT_MINIMUM:
1053
        return False
1054
1055
    return True