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

88
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

89
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

90
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

91
GAS_LIMIT_MINIMUM = Uint(5000)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

93
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

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

SYSTEM_TRANSACTION_GAS

97
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

88
MAX_BLOB_GAS_PER_BLOCK = U64(786432)
98
MAX_BLOB_GAS_PER_BLOCK = U64(1179648)

VERSIONED_HASH_VERSION_KZG

99
VERSIONED_HASH_VERSION_KZG = b"\x01"

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

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

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

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

HISTORY_STORAGE_ADDRESS

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

BlockChain

History and current state of the block chain.

112
@dataclass
class BlockChain:

blocks

118
    blocks: List[Block]

state

119
    state: State

chain_id

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

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

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

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:
826
    """
827
    Execute a transaction against the provided environment.
828
829
    This function processes the actions needed to execute a transaction.
830
    It decrements the sender's account balance after calculating the gas fee
831
    and refunds them the proper amount after execution. Calling contracts,
832
    deploying code, and incrementing nonces are all examples of actions that
833
    happen within this function or from a call made within this function.
834
835
    Accounts that are marked for deletion are processed and destroyed after
836
    execution.
837
838
    Parameters
839
    ----------
840
    block_env :
841
        Environment for the Ethereum Virtual Machine.
842
    block_output :
843
        The block output for the current block.
844
    tx :
845
        Transaction to execute.
846
    index:
847
        Index of the transaction in the block.
848
849
    """
850
    trie_set(
851
        block_output.transactions_trie,
852
        rlp.encode(index),
853
        encode_transaction(tx),
854
    )
855
713
    intrinsic_gas = validate_transaction(tx)
856
    intrinsic_gas, calldata_floor_gas_cost = validate_transaction(tx)
857
858
    (
859
        sender,
860
        effective_gas_price,
861
        blob_versioned_hashes,
862
        tx_blob_gas_used,
863
    ) = check_transaction(
864
        block_env=block_env,
865
        block_output=block_output,
866
        tx=tx,
867
    )
868
869
    sender_account = get_account(block_env.state, sender)
870
871
    if isinstance(tx, BlobTransaction):
872
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
873
    else:
874
        blob_gas_fee = Uint(0)
875
876
    effective_gas_fee = tx.gas * effective_gas_price
877
878
    gas = tx.gas - intrinsic_gas
879
    increment_nonce(block_env.state, sender)
880
881
    sender_balance_after_gas_fee = (
882
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
883
    )
884
    set_account_balance(
885
        block_env.state, sender, U256(sender_balance_after_gas_fee)
886
    )
887
888
    access_list_addresses = set()
889
    access_list_storage_keys = set()
890
    access_list_addresses.add(block_env.coinbase)
891
    if isinstance(
749
        tx, (AccessListTransaction, FeeMarketTransaction, BlobTransaction)
892
        tx,
893
        (
894
            AccessListTransaction,
895
            FeeMarketTransaction,
896
            BlobTransaction,
897
            SetCodeTransaction,
898
        ),
899
    ):
900
        for access in tx.access_list:
901
            access_list_addresses.add(access.account)
902
            for slot in access.slots:
903
                access_list_storage_keys.add((access.account, slot))
904
905
    authorizations: Tuple[Authorization, ...] = ()
906
    if isinstance(tx, SetCodeTransaction):
907
        authorizations = tx.authorizations
908
909
    tx_env = vm.TransactionEnvironment(
910
        origin=sender,
911
        gas_price=effective_gas_price,
912
        gas=gas,
913
        access_list_addresses=access_list_addresses,
914
        access_list_storage_keys=access_list_storage_keys,
915
        transient_storage=TransientStorage(),
916
        blob_versioned_hashes=blob_versioned_hashes,
917
        authorizations=authorizations,
918
        index_in_block=index,
919
        tx_hash=get_transaction_hash(encode_transaction(tx)),
920
    )
921
922
    message = prepare_message(block_env, tx_env, tx)
923
924
    tx_output = process_message_call(message)
925
926
    # For EIP-7623 we first calculate the execution_gas_used, which includes
927
    # the execution gas refund.
928
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
929
    tx_gas_refund = min(
930
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
931
    )
932
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
933
934
    # Transactions with less execution_gas_used than the floor pay at the
935
    # floor cost.
936
    tx_gas_used_after_refund = max(
937
        tx_gas_used_after_refund, calldata_floor_gas_cost
938
    )
939
940
    tx_gas_left = tx.gas - tx_gas_used_after_refund
941
    gas_refund_amount = tx_gas_left * effective_gas_price
942
943
    # For non-1559 transactions effective_gas_price == tx.gas_price
944
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
945
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
946
947
    # refund gas
948
    sender_balance_after_refund = get_account(
949
        block_env.state, sender
950
    ).balance + U256(gas_refund_amount)
951
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
952
953
    # transfer miner fees
954
    coinbase_balance_after_mining_fee = get_account(
955
        block_env.state, block_env.coinbase
956
    ).balance + U256(transaction_fee)
957
    set_account_balance(
958
        block_env.state,
959
        block_env.coinbase,
960
        coinbase_balance_after_mining_fee,
961
    )
962
963
    for address in tx_output.accounts_to_delete:
964
        destroy_account(block_env.state, address)
965
966
    block_output.block_gas_used += tx_gas_used_after_refund
967
    block_output.blob_gas_used += tx_blob_gas_used
968
969
    receipt = make_receipt(
970
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
971
    )
972
973
    receipt_key = rlp.encode(Uint(index))
974
    block_output.receipt_keys += (receipt_key,)
975
976
    trie_set(
977
        block_output.receipts_trie,
978
        receipt_key,
979
        receipt,
980
    )
981
982
    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:
990
    """
991
    Increase the balance of the withdrawing account.
992
    """
993
994
    def increase_recipient_balance(recipient: Account) -> None:
995
        recipient.balance += wd.amount * U256(10**9)
996
997
    for i, wd in enumerate(withdrawals):
998
        trie_set(
999
            block_output.withdrawals_trie,
1000
            rlp.encode(Uint(i)),
1001
            rlp.encode(wd),
1002
        )
1003
1004
        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:
1008
    """
1009
    Validates the gas limit for a block.
1010
1011
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1012
    quotient of the parent block's gas limit and the
1013
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1014
    passed through as a parameter is greater than or equal to the *sum* of
1015
    the parent's gas and the adjustment delta then the limit for gas is too
1016
    high and fails this function's check. Similarly, if the limit is less
1017
    than or equal to the *difference* of the parent's gas and the adjustment
1018
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1019
    check fails because the gas limit doesn't allow for a sufficient or
1020
    reasonable amount of gas to be used on a block.
1021
1022
    Parameters
1023
    ----------
1024
    gas_limit :
1025
        Gas limit to validate.
1026
1027
    parent_gas_limit :
1028
        Gas limit of the parent block.
1029
1030
    Returns
1031
    -------
1032
    check : `bool`
1033
        True if gas limit constraints are satisfied, False otherwise.
1034
1035
    """
1036
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1037
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1038
        return False
1039
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1040
        return False
1041
    if gas_limit < GAS_LIMIT_MINIMUM:
1042
        return False
1043
1044
    return True