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

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
    return old

get_last_256_block_hashes

Obtain the list of hashes of the previous 256 blocks in order of increasing block number.

This function will return less hashes for the first 256 blocks.

The BLOCKHASH opcode needs to access the latest hashes on the chain, therefore this function retrieves them.

Parameters

chain : History and current state.

Returns

recent_block_hashes : List[Hash32] Hashes of the recent 256 blocks in order of increasing block number.

def get_last_256_block_hashes(chain: BlockChain) -> List[Hash32]:
148
    """
149
    Obtain the list of hashes of the previous 256 blocks in order of
150
    increasing block number.
151
152
    This function will return less hashes for the first 256 blocks.
153
154
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
155
    therefore this function retrieves them.
156
157
    Parameters
158
    ----------
159
    chain :
160
        History and current state.
161
162
    Returns
163
    -------
164
    recent_block_hashes : `List[Hash32]`
165
        Hashes of the recent 256 blocks in order of increasing block number.
166
    """
167
    recent_blocks = chain.blocks[-255:]
168
    # TODO: This function has not been tested rigorously
169
    if len(recent_blocks) == 0:
170
        return []
171
172
    recent_block_hashes = []
173
174
    for block in recent_blocks:
175
        prev_block_hash = block.header.parent_hash
176
        recent_block_hashes.append(prev_block_hash)
177
178
    # We are computing the hash only for the most recent block and not for
179
    # the rest of the blocks as they have successors which have the hash of
180
    # the current block as parent hash.
181
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
182
    recent_block_hashes.append(most_recent_block_hash)
183
184
    return recent_block_hashes

state_transition

Attempts to apply a block to an existing block chain.

All parts of the block's contents need to be verified before being added to the chain. Blocks are verified by ensuring that the contents of the block make logical sense with the contents of the parent block. The information in the block's header must also match the corresponding information in the block.

To implement Ethereum, in theory clients are only required to store the most recent 255 blocks of the chain since as far as execution is concerned, only those blocks are accessed. Practically, however, clients should store more blocks to handle reorgs.

Parameters

chain : History and current state. block : Block to apply to chain.

def state_transition(chain: BlockChain, ​​block: Block) -> None:
188
    """
189
    Attempts to apply a block to an existing block chain.
190
191
    All parts of the block's contents need to be verified before being added
192
    to the chain. Blocks are verified by ensuring that the contents of the
193
    block make logical sense with the contents of the parent block. The
194
    information in the block's header must also match the corresponding
195
    information in the block.
196
197
    To implement Ethereum, in theory clients are only required to store the
198
    most recent 255 blocks of the chain since as far as execution is
199
    concerned, only those blocks are accessed. Practically, however, clients
200
    should store more blocks to handle reorgs.
201
202
    Parameters
203
    ----------
204
    chain :
205
        History and current state.
206
    block :
207
        Block to apply to `chain`.
208
    """
209
    validate_header(chain, block.header)
210
    if block.ommers != ():
211
        raise InvalidBlock
212
213
    block_env = vm.BlockEnvironment(
214
        chain_id=chain.chain_id,
215
        state=chain.state,
216
        block_gas_limit=block.header.gas_limit,
217
        block_hashes=get_last_256_block_hashes(chain),
218
        coinbase=block.header.coinbase,
219
        number=block.header.number,
220
        base_fee_per_gas=block.header.base_fee_per_gas,
221
        time=block.header.timestamp,
222
        prev_randao=block.header.prev_randao,
223
        excess_blob_gas=block.header.excess_blob_gas,
224
        parent_beacon_block_root=block.header.parent_beacon_block_root,
225
    )
226
227
    block_output = apply_body(
228
        block_env=block_env,
229
        transactions=block.transactions,
230
        withdrawals=block.withdrawals,
231
    )
232
    block_state_root = state_root(block_env.state)
233
    transactions_root = root(block_output.transactions_trie)
234
    receipt_root = root(block_output.receipts_trie)
235
    block_logs_bloom = logs_bloom(block_output.block_logs)
236
    withdrawals_root = root(block_output.withdrawals_trie)
237
    requests_hash = compute_requests_hash(block_output.requests)
238
239
    if block_output.block_gas_used != block.header.gas_used:
240
        raise InvalidBlock(
241
            f"{block_output.block_gas_used} != {block.header.gas_used}"
242
        )
243
    if transactions_root != block.header.transactions_root:
244
        raise InvalidBlock
245
    if block_state_root != block.header.state_root:
246
        raise InvalidBlock
247
    if receipt_root != block.header.receipt_root:
248
        raise InvalidBlock
249
    if block_logs_bloom != block.header.bloom:
250
        raise InvalidBlock
251
    if withdrawals_root != block.header.withdrawals_root:
252
        raise InvalidBlock
253
    if block_output.blob_gas_used != block.header.blob_gas_used:
254
        raise InvalidBlock
255
    if requests_hash != block.header.requests_hash:
256
        raise InvalidBlock
257
258
    chain.blocks.append(block)
259
    if len(chain.blocks) > 255:
260
        # Real clients have to store more blocks to deal with reorgs, but the
261
        # protocol only requires the last 255
262
        chain.blocks = chain.blocks[-255:]

calculate_base_fee_per_gas

Calculates the base fee per gas for the block.

Parameters

block_gas_limit : Gas limit of the block for which the base fee is being calculated. parent_gas_limit : Gas limit of the parent block. parent_gas_used : Gas used in the parent block. parent_base_fee_per_gas : Base fee per gas of the parent block.

Returns

base_fee_per_gas : Uint Base fee per gas for the block.

def calculate_base_fee_per_gas(block_gas_limit: Uint, ​​parent_gas_limit: Uint, ​​parent_gas_used: Uint, ​​parent_base_fee_per_gas: Uint) -> Uint:
271
    """
272
    Calculates the base fee per gas for the block.
273
274
    Parameters
275
    ----------
276
    block_gas_limit :
277
        Gas limit of the block for which the base fee is being calculated.
278
    parent_gas_limit :
279
        Gas limit of the parent block.
280
    parent_gas_used :
281
        Gas used in the parent block.
282
    parent_base_fee_per_gas :
283
        Base fee per gas of the parent block.
284
285
    Returns
286
    -------
287
    base_fee_per_gas : `Uint`
288
        Base fee per gas for the block.
289
    """
290
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
291
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
292
        raise InvalidBlock
293
294
    if parent_gas_used == parent_gas_target:
295
        expected_base_fee_per_gas = parent_base_fee_per_gas
296
    elif parent_gas_used > parent_gas_target:
297
        gas_used_delta = parent_gas_used - parent_gas_target
298
299
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
300
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
301
302
        base_fee_per_gas_delta = max(
303
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
304
            Uint(1),
305
        )
306
307
        expected_base_fee_per_gas = (
308
            parent_base_fee_per_gas + base_fee_per_gas_delta
309
        )
310
    else:
311
        gas_used_delta = parent_gas_target - parent_gas_used
312
313
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
314
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
315
316
        base_fee_per_gas_delta = (
317
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
318
        )
319
320
        expected_base_fee_per_gas = (
321
            parent_base_fee_per_gas - base_fee_per_gas_delta
322
        )
323
324
    return Uint(expected_base_fee_per_gas)

validate_header

Verifies a block header.

In order to consider a block's header valid, the logic for the quantities in the header should match the logic for the block itself. For example the header timestamp should be greater than the block's parent timestamp because the block was created after the parent block. Additionally, the block's number should be directly following the parent block's number since it is the next block in the sequence.

Parameters

chain : History and current state. header : Header to check for correctness.

def validate_header(chain: BlockChain, ​​header: Header) -> None:
328
    """
329
    Verifies a block header.
330
331
    In order to consider a block's header valid, the logic for the
332
    quantities in the header should match the logic for the block itself.
333
    For example the header timestamp should be greater than the block's parent
334
    timestamp because the block was created *after* the parent block.
335
    Additionally, the block's number should be directly following the parent
336
    block's number since it is the next block in the sequence.
337
338
    Parameters
339
    ----------
340
    chain :
341
        History and current state.
342
    header :
343
        Header to check for correctness.
344
    """
345
    if header.number < Uint(1):
346
        raise InvalidBlock
347
348
    parent_header = chain.blocks[-1].header
349
350
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
351
    if header.excess_blob_gas != excess_blob_gas:
352
        raise InvalidBlock
353
354
    if header.gas_used > header.gas_limit:
355
        raise InvalidBlock
356
357
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
358
        header.gas_limit,
359
        parent_header.gas_limit,
360
        parent_header.gas_used,
361
        parent_header.base_fee_per_gas,
362
    )
363
    if expected_base_fee_per_gas != header.base_fee_per_gas:
364
        raise InvalidBlock
365
    if header.timestamp <= parent_header.timestamp:
366
        raise InvalidBlock
367
    if header.number != parent_header.number + Uint(1):
368
        raise InvalidBlock
369
    if len(header.extra_data) > 32:
370
        raise InvalidBlock
371
    if header.difficulty != 0:
372
        raise InvalidBlock
373
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
374
        raise InvalidBlock
375
    if header.ommers_hash != EMPTY_OMMER_HASH:
376
        raise InvalidBlock
377
378
    block_parent_hash = keccak256(rlp.encode(parent_header))
379
    if header.parent_hash != block_parent_hash:
380
        raise InvalidBlock

check_transaction

Check if the transaction is includable in the block.

Parameters

block_env : The block scoped environment. block_output : The block output for the current block. tx : The transaction.

Returns

sender_address : The sender of the transaction. effective_gas_price : The price to charge for gas when the transaction is executed. blob_versioned_hashes : The blob versioned hashes of the transaction. tx_blob_gas_used: The blob gas used by the transaction.

Raises

InvalidBlock : If the transaction is not includable. GasUsedExceedsLimitError : If the gas used by the transaction exceeds the block's gas limit. NonceMismatchError : If the nonce of the transaction is not equal to the sender's nonce. InsufficientBalanceError : If the sender's balance is not enough to pay for the transaction. InvalidSenderError : If the transaction is from an address that does not exist anymore. PriorityFeeGreaterThanMaxFeeError : If the priority fee is greater than the maximum fee per gas. InsufficientMaxFeePerGasError : If the maximum fee per gas is insufficient for the transaction. InsufficientMaxFeePerBlobGasError : If the maximum fee per blob gas is insufficient for the transaction. BlobGasLimitExceededError : If the blob gas used by the transaction exceeds the block's blob gas limit. InvalidBlobVersionedHashError : If the transaction contains a blob versioned hash with an invalid version. NoBlobDataError : If the transaction is a type 3 but has no blobs. TransactionTypeContractCreationError: If the transaction type is not allowed to create contracts. EmptyAuthorizationListError : If the transaction is a SetCodeTransaction and the authorization list is empty.

def check_transaction(block_env: ethereum.cancun.vm.BlockEnvironmentethereum.prague.vm.BlockEnvironment, ​​block_output: ethereum.cancun.vm.BlockOutputethereum.prague.vm.BlockOutput, ​​tx: Transaction) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
388
    """
389
    Check if the transaction is includable in the block.
390
391
    Parameters
392
    ----------
393
    block_env :
394
        The block scoped environment.
395
    block_output :
396
        The block output for the current block.
397
    tx :
398
        The transaction.
399
400
    Returns
401
    -------
402
    sender_address :
403
        The sender of the transaction.
404
    effective_gas_price :
405
        The price to charge for gas when the transaction is executed.
406
    blob_versioned_hashes :
407
        The blob versioned hashes of the transaction.
408
    tx_blob_gas_used:
409
        The blob gas used by the transaction.
410
411
    Raises
412
    ------
413
    InvalidBlock :
414
        If the transaction is not includable.
415
    GasUsedExceedsLimitError :
416
        If the gas used by the transaction exceeds the block's gas limit.
417
    NonceMismatchError :
418
        If the nonce of the transaction is not equal to the sender's nonce.
419
    InsufficientBalanceError :
420
        If the sender's balance is not enough to pay for the transaction.
421
    InvalidSenderError :
422
        If the transaction is from an address that does not exist anymore.
423
    PriorityFeeGreaterThanMaxFeeError :
424
        If the priority fee is greater than the maximum fee per gas.
425
    InsufficientMaxFeePerGasError :
426
        If the maximum fee per gas is insufficient for the transaction.
427
    InsufficientMaxFeePerBlobGasError :
428
        If the maximum fee per blob gas is insufficient for the transaction.
429
    BlobGasLimitExceededError :
430
        If the blob gas used by the transaction exceeds the block's blob gas
431
        limit.
432
    InvalidBlobVersionedHashError :
433
        If the transaction contains a blob versioned hash with an invalid
434
        version.
435
    NoBlobDataError :
436
        If the transaction is a type 3 but has no blobs.
437
    TransactionTypeContractCreationError:
438
        If the transaction type is not allowed to create contracts.
439
    EmptyAuthorizationListError :
440
        If the transaction is a SetCodeTransaction and the authorization list
441
        is empty.
442
    """
443
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
444
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
445
446
    if tx.gas > gas_available:
447
        raise GasUsedExceedsLimitError("gas used exceeds limit")
448
449
    tx_blob_gas_used = calculate_total_blob_gas(tx)
450
    if tx_blob_gas_used > blob_gas_available:
451
        raise BlobGasLimitExceededError("blob gas limit exceeded")
452
453
    sender_address = recover_sender(block_env.chain_id, tx)
454
    sender_account = get_account(block_env.state, sender_address)
455
430
    if isinstance(tx, (FeeMarketTransaction, BlobTransaction)):
456
    if isinstance(
457
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
458
    ):
459
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
460
            raise PriorityFeeGreaterThanMaxFeeError(
461
                "priority fee greater than max fee"
462
            )
463
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
464
            raise InsufficientMaxFeePerGasError(
465
                tx.max_fee_per_gas, block_env.base_fee_per_gas
466
            )
467
468
        priority_fee_per_gas = min(
469
            tx.max_priority_fee_per_gas,
470
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
471
        )
472
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
473
        max_gas_fee = tx.gas * tx.max_fee_per_gas
474
    else:
475
        if tx.gas_price < block_env.base_fee_per_gas:
476
            raise InvalidBlock
477
        effective_gas_price = tx.gas_price
478
        max_gas_fee = tx.gas * tx.gas_price
479
480
    if isinstance(tx, BlobTransaction):
453
        if not isinstance(tx.to, Address):
454
            raise TransactionTypeContractCreationError(tx)
455
        if len(tx.blob_versioned_hashes) == 0:
481
        if len(tx.blob_versioned_hashes) == 0:
482
            raise NoBlobDataError("no blob data in transaction")
483
        for blob_versioned_hash in tx.blob_versioned_hashes:
484
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
485
                raise InvalidBlobVersionedHashError(
486
                    "invalid blob versioned hash"
487
                )
488
489
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
490
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
491
            raise InsufficientMaxFeePerBlobGasError(
492
                "insufficient max fee per blob gas"
493
            )
494
495
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
496
            tx.max_fee_per_blob_gas
497
        )
498
        blob_versioned_hashes = tx.blob_versioned_hashes
499
    else:
500
        blob_versioned_hashes = ()
501
502
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
503
        if not isinstance(tx.to, Address):
504
            raise TransactionTypeContractCreationError(tx)
505
506
    if isinstance(tx, SetCodeTransaction):
507
        if not any(tx.authorizations):
508
            raise EmptyAuthorizationListError("empty authorization list")
509
510
    if sender_account.nonce > Uint(tx.nonce):
511
        raise NonceMismatchError("nonce too low")
512
    elif sender_account.nonce < Uint(tx.nonce):
513
        raise NonceMismatchError("nonce too high")
514
515
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
516
        raise InsufficientBalanceError("insufficient sender balance")
481
    if sender_account.code:
517
    if sender_account.code and not is_valid_delegation(sender_account.code):
518
        raise InvalidSenderError("not EOA")
519
520
    return (
521
        sender_address,
522
        effective_gas_price,
523
        blob_versioned_hashes,
524
        tx_blob_gas_used,
525
    )

make_receipt

Make the receipt for a transaction that was executed.

Parameters

tx : The executed transaction. error : Error in the top level frame of the transaction, if any. cumulative_gas_used : The total gas used so far in the block after the transaction was executed. logs : The logs produced by the transaction.

Returns

receipt : The receipt for the transaction.

def make_receipt(tx: Transaction, ​​error: Optional[EthereumException], ​​cumulative_gas_used: Uint, ​​logs: Tuple[Log, ...]) -> Bytes | Receipt:
534
    """
535
    Make the receipt for a transaction that was executed.
536
537
    Parameters
538
    ----------
539
    tx :
540
        The executed transaction.
541
    error :
542
        Error in the top level frame of the transaction, if any.
543
    cumulative_gas_used :
544
        The total gas used so far in the block after the transaction was
545
        executed.
546
    logs :
547
        The logs produced by the transaction.
548
549
    Returns
550
    -------
551
    receipt :
552
        The receipt for the transaction.
553
    """
554
    receipt = Receipt(
555
        succeeded=error is None,
556
        cumulative_gas_used=cumulative_gas_used,
557
        bloom=logs_bloom(logs),
558
        logs=logs,
559
    )
560
561
    return encode_receipt(tx, receipt)

process_system_transaction

Process a system transaction with the given code.

Prefer calling process_checked_system_transaction or process_unchecked_system_transaction 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:
570
    """
571
    Process a system transaction with the given code.
572
537
    Prefer calling `process_unchecked_system_transaction` unless the contract
538
    code has already been read from the state.
573
    Prefer calling `process_checked_system_transaction` or
574
    `process_unchecked_system_transaction` depending on whether missing code or
575
    an execution error should cause the block to be rejected.
576
577
    Parameters
578
    ----------
579
    block_env :
580
        The block scoped environment.
581
    target_address :
582
        Address of the contract to call.
583
    system_contract_code :
584
        Code of the contract to call.
585
    data :
586
        Data to pass to the contract.
587
588
    Returns
589
    -------
590
    system_tx_output : `MessageCallOutput`
591
        Output of processing the system transaction.
592
    """
593
    tx_env = vm.TransactionEnvironment(
594
        origin=SYSTEM_ADDRESS,
595
        gas_price=block_env.base_fee_per_gas,
596
        gas=SYSTEM_TRANSACTION_GAS,
597
        access_list_addresses=set(),
598
        access_list_storage_keys=set(),
599
        transient_storage=TransientStorage(),
600
        blob_versioned_hashes=(),
601
        authorizations=(),
602
        index_in_block=None,
603
        tx_hash=None,
604
        traces=[],
605
    )
606
607
    system_tx_message = Message(
608
        block_env=block_env,
609
        tx_env=tx_env,
610
        caller=SYSTEM_ADDRESS,
611
        target=target_address,
612
        gas=SYSTEM_TRANSACTION_GAS,
613
        value=U256(0),
614
        data=data,
615
        code=system_contract_code,
616
        depth=Uint(0),
617
        current_target=target_address,
618
        code_address=target_address,
619
        should_transfer_value=False,
620
        is_static=False,
621
        accessed_addresses=set(),
622
        accessed_storage_keys=set(),
623
        disable_precompiles=False,
624
        parent_evm=None,
625
    )
626
627
    system_tx_output = process_message_call(system_tx_message)
628
629
    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:
637
    """
638
    Process a system transaction and raise an error if the contract does not
639
    contain code or if the transaction fails.
640
641
    Parameters
642
    ----------
643
    block_env :
644
        The block scoped environment.
645
    target_address :
646
        Address of the contract to call.
647
    data :
648
        Data to pass to the contract.
649
650
    Returns
651
    -------
652
    system_tx_output : `MessageCallOutput`
653
        Output of processing the system transaction.
654
    """
655
    system_contract_code = get_account(block_env.state, target_address).code
656
657
    if len(system_contract_code) == 0:
658
        raise InvalidBlock(
659
            f"System contract address {target_address.hex()} does not "
660
            "contain code"
661
        )
662
663
    system_tx_output = process_system_transaction(
664
        block_env,
665
        target_address,
666
        system_contract_code,
667
        data,
668
    )
669
670
    if system_tx_output.error:
671
        raise InvalidBlock(
672
            f"System contract ({target_address.hex()}) call failed: "
673
            f"{system_tx_output.error}"
674
        )
675
676
    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:
684
    """
685
    Process a system transaction without checking if the contract contains code
686
    or if the transaction fails.
687
688
    Parameters
689
    ----------
690
    block_env :
691
        The block scoped environment.
692
    target_address :
693
        Address of the contract to call.
694
    data :
695
        Data to pass to the contract.
696
697
    Returns
698
    -------
699
    system_tx_output : `MessageCallOutput`
700
        Output of processing the system transaction.
701
    """
702
    system_contract_code = get_account(block_env.state, target_address).code
703
    return process_system_transaction(
704
        block_env,
705
        target_address,
706
        system_contract_code,
707
        data,
708
    )

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

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

check_gas_limit

Validates the gas limit for a block.

The bounds of the gas limit, max_adjustment_delta, is set as the quotient of the parent block's gas limit and the GAS_LIMIT_ADJUSTMENT_FACTOR. Therefore, if the gas limit that is passed through as a parameter is greater than or equal to the sum of the parent's gas and the adjustment delta then the limit for gas is too high and fails this function's check. Similarly, if the limit is less than or equal to the difference of the parent's gas and the adjustment delta or the predefined GAS_LIMIT_MINIMUM then this function's check fails because the gas limit doesn't allow for a sufficient or reasonable amount of gas to be used on a block.

Parameters

gas_limit : Gas limit to validate.

parent_gas_limit : Gas limit of the parent block.

Returns

check : bool True if gas limit constraints are satisfied, False otherwise.

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