ethereum.forks.bpo5.forkethereum.forks.amsterdam.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

106
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

107
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

108
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

109
GAS_LIMIT_MINIMUM = Uint(5000)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

111
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

112
BEACON_ROOTS_ADDRESS = hex_to_address(
113
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
114
)

SYSTEM_TRANSACTION_GAS

115
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

116
MAX_BLOB_GAS_PER_BLOCK = BLOB_SCHEDULE_MAX * GAS_PER_BLOB

VERSIONED_HASH_VERSION_KZG

117
VERSIONED_HASH_VERSION_KZG = b"\x01"

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

119
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
120
    "0x00000961Ef480Eb55e80D19ad83579A64c007002"
121
)

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

122
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
123
    "0x0000BBdDc7CE488642fb579F8B00f3a590007251"
124
)

HISTORY_STORAGE_ADDRESS

125
HISTORY_STORAGE_ADDRESS = hex_to_address(
126
    "0x0000F90827F1C53a10cb7A02335B175320002935"
127
)

MAX_BLOCK_SIZE

128
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

129
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

130
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

131
BLOB_COUNT_LIMIT = 6

BlockChain

History and current state of the block chain.

134
@dataclass
class BlockChain:

blocks

140
    blocks: List[Block]

state

141
    state: State

chain_id

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

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:
576
    """
577
    Make the receipt for a transaction that was executed.
578
579
    Parameters
580
    ----------
581
    tx :
582
        The executed transaction.
583
    error :
584
        Error in the top level frame of the transaction, if any.
585
    cumulative_gas_used :
586
        The total gas used so far in the block after the transaction was
587
        executed.
588
    logs :
589
        The logs produced by the transaction.
590
591
    Returns
592
    -------
593
    receipt :
594
        The receipt for the transaction.
595
596
    """
597
    receipt = Receipt(
598
        succeeded=error is None,
599
        cumulative_gas_used=cumulative_gas_used,
600
        bloom=logs_bloom(logs),
601
        logs=logs,
602
    )
603
604
    return encode_receipt(tx, receipt)

process_system_transaction

Process a system transaction with the given code.

Prefer calling process_checked_system_transaction or process_unchecked_system_transaction depending on whether missing code or an execution error should cause the block to be rejected.

Parameters

block_env : The block scoped environment. target_address : Address of the contract to call. system_contract_code : Code of the contract to call. data : Data to pass to the contract.

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

def process_system_transaction(block_env: ethereum.forks.bpo5.vm.BlockEnvironmentethereum.forks.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
613
    """
614
    Process a system transaction with the given code.
615
616
    Prefer calling `process_checked_system_transaction` or
617
    `process_unchecked_system_transaction` depending on whether missing code or
618
    an execution error should cause the block to be rejected.
619
620
    Parameters
621
    ----------
622
    block_env :
623
        The block scoped environment.
624
    target_address :
625
        Address of the contract to call.
626
    system_contract_code :
627
        Code of the contract to call.
628
    data :
629
        Data to pass to the contract.
630
631
    Returns
632
    -------
633
    system_tx_output : `MessageCallOutput`
634
        Output of processing the system transaction.
635
636
    """
637
    # EIP-7928: Create a child frame for system transaction
638
    # This allows proper pre-state capture for net-zero filtering
639
    system_tx_state_changes = create_child_frame(block_env.state_changes)
640
641
    tx_env = vm.TransactionEnvironment(
642
        origin=SYSTEM_ADDRESS,
643
        gas_price=block_env.base_fee_per_gas,
644
        gas=SYSTEM_TRANSACTION_GAS,
645
        access_list_addresses=set(),
646
        access_list_storage_keys=set(),
647
        transient_storage=TransientStorage(),
648
        blob_versioned_hashes=(),
649
        authorizations=(),
650
        index_in_block=None,
651
        tx_hash=None,
652
        state_changes=system_tx_state_changes,
653
    )
654
655
    # Create call frame as child of tx frame
656
    call_frame = create_child_frame(tx_env.state_changes)
657
658
    system_tx_message = Message(
659
        block_env=block_env,
660
        tx_env=tx_env,
661
        caller=SYSTEM_ADDRESS,
662
        target=target_address,
663
        gas=SYSTEM_TRANSACTION_GAS,
664
        value=U256(0),
665
        data=data,
666
        code=system_contract_code,
667
        depth=Uint(0),
668
        current_target=target_address,
669
        code_address=target_address,
670
        should_transfer_value=False,
671
        is_static=False,
672
        accessed_addresses=set(),
673
        accessed_storage_keys=set(),
674
        disable_precompiles=False,
675
        parent_evm=None,
676
        is_create=False,
677
        state_changes=call_frame,
678
    )
679
680
    system_tx_output = process_message_call(system_tx_message)
681
682
    # Commit system transaction changes to block frame
683
    # System transactions always succeed (or block is invalid)
684
    commit_transaction_frame(tx_env.state_changes)
685
686
    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.bpo5.vm.BlockEnvironmentethereum.forks.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
694
    """
695
    Process a system transaction and raise an error if the contract does not
696
    contain code or if the transaction fails.
697
698
    Parameters
699
    ----------
700
    block_env :
701
        The block scoped environment.
702
    target_address :
703
        Address of the contract to call.
704
    data :
705
        Data to pass to the contract.
706
707
    Returns
708
    -------
709
    system_tx_output : `MessageCallOutput`
710
        Output of processing the system transaction.
711
712
    """
713
    system_contract_code = get_account(block_env.state, target_address).code
714
715
    if len(system_contract_code) == 0:
716
        raise InvalidBlock(
717
            f"System contract address {target_address.hex()} does not "
718
            "contain code"
719
        )
720
721
    system_tx_output = process_system_transaction(
722
        block_env,
723
        target_address,
724
        system_contract_code,
725
        data,
726
    )
727
728
    if system_tx_output.error:
729
        raise InvalidBlock(
730
            f"System contract ({target_address.hex()}) call failed: "
731
            f"{system_tx_output.error}"
732
        )
733
734
    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.bpo5.vm.BlockEnvironmentethereum.forks.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
742
    """
743
    Process a system transaction without checking if the contract contains code
744
    or if the transaction fails.
745
746
    Parameters
747
    ----------
748
    block_env :
749
        The block scoped environment.
750
    target_address :
751
        Address of the contract to call.
752
    data :
753
        Data to pass to the contract.
754
755
    Returns
756
    -------
757
    system_tx_output : `MessageCallOutput`
758
        Output of processing the system transaction.
759
760
    """
761
    system_contract_code = get_account(block_env.state, target_address).code
762
    return process_system_transaction(
763
        block_env,
764
        target_address,
765
        system_contract_code,
766
        data,
767
    )

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.bpo5.vm.BlockEnvironmentethereum.forks.amsterdam.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.forks.bpo5.vm.BlockOutputethereum.forks.amsterdam.vm.BlockOutput:
775
    """
776
    Executes a block.
777
778
    Many of the contents of a block are stored in data structures called
779
    tries. There is a transactions trie which is similar to a ledger of the
780
    transactions stored in the current block. There is also a receipts trie
781
    which stores the results of executing a transaction, like the post state
782
    and gas used. This function creates and executes the block that is to be
783
    added to the chain.
784
785
    Parameters
786
    ----------
787
    block_env :
788
        The block scoped environment.
789
    transactions :
790
        Transactions included in the block.
791
    withdrawals :
792
        Withdrawals to be processed in the current block.
793
794
    Returns
795
    -------
796
    block_output :
797
        The block output for the current block.
798
799
    """
800
    block_output = vm.BlockOutput()
801
802
    # EIP-7928: System contracts use block_access_index 0
803
    # The block frame already starts at index 0, so system transactions
804
    # naturally use that index through the block frame
805
806
    process_unchecked_system_transaction(
807
        block_env=block_env,
808
        target_address=BEACON_ROOTS_ADDRESS,
809
        data=block_env.parent_beacon_block_root,
810
    )
811
812
    process_unchecked_system_transaction(
813
        block_env=block_env,
814
        target_address=HISTORY_STORAGE_ADDRESS,
815
        data=block_env.block_hashes[-1],  # The parent hash
816
    )
817
818
    for i, tx in enumerate(map(decode_transaction, transactions)):
819
        process_transaction(block_env, block_output, tx, Uint(i))
820
821
    # EIP-7928: Increment block frame to post-execution index
822
    # After N transactions, block frame is at index N
823
    # Post-execution operations (withdrawals, etc.) use index N+1
824
    increment_block_access_index(block_env.state_changes)
825
826
    process_withdrawals(block_env, block_output, withdrawals)
827
828
    process_general_purpose_requests(
829
        block_env=block_env,
830
        block_output=block_output,
831
    )
832
    # Build block access list from block_env.state_changes
833
    block_output.block_access_list = build_block_access_list(
834
        block_env.state_changes
835
    )
836
837
    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.bpo5.vm.BlockEnvironmentethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo5.vm.BlockOutputethereum.forks.amsterdam.vm.BlockOutput) -> None:
844
    """
845
    Process all the requests in the block.
846
847
    Parameters
848
    ----------
849
    block_env :
850
        The execution environment for the Block.
851
    block_output :
852
        The block output for the current block.
853
854
    """
855
    # Requests are to be in ascending order of request type
856
    deposit_requests = parse_deposit_requests(block_output)
857
    requests_from_execution = block_output.requests
858
    if len(deposit_requests) > 0:
859
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
860
861
    system_withdrawal_tx_output = process_checked_system_transaction(
862
        block_env=block_env,
863
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
864
        data=b"",
865
    )
866
867
    if len(system_withdrawal_tx_output.return_data) > 0:
868
        requests_from_execution.append(
869
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
870
        )
871
872
    system_consolidation_tx_output = process_checked_system_transaction(
873
        block_env=block_env,
874
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
875
        data=b"",
876
    )
877
878
    if len(system_consolidation_tx_output.return_data) > 0:
879
        requests_from_execution.append(
880
            CONSOLIDATION_REQUEST_TYPE
881
            + system_consolidation_tx_output.return_data
882
        )

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.bpo5.vm.BlockEnvironmentethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo5.vm.BlockOutputethereum.forks.amsterdam.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
891
    """
892
    Execute a transaction against the provided environment.
893
894
    This function processes the actions needed to execute a transaction.
895
    It decrements the sender's account balance after calculating the gas fee
896
    and refunds them the proper amount after execution. Calling contracts,
897
    deploying code, and incrementing nonces are all examples of actions that
898
    happen within this function or from a call made within this function.
899
900
    Accounts that are marked for deletion are processed and destroyed after
901
    execution.
902
903
    Parameters
904
    ----------
905
    block_env :
906
        Environment for the Ethereum Virtual Machine.
907
    block_output :
908
        The block output for the current block.
909
    tx :
910
        Transaction to execute.
911
    index:
912
        Index of the transaction in the block.
913
914
    """
915
    # EIP-7928: Create a transaction-level StateChanges frame
916
    # The frame will read the current block_access_index from the block frame
917
    increment_block_access_index(block_env.state_changes)
918
    tx_state_changes = create_child_frame(block_env.state_changes)
919
920
    # Capture coinbase pre-balance for net-zero filtering
921
    coinbase_pre_balance = get_account(
922
        block_env.state, block_env.coinbase
923
    ).balance
924
    track_address(tx_state_changes, block_env.coinbase)
925
    capture_pre_balance(
926
        tx_state_changes, block_env.coinbase, coinbase_pre_balance
927
    )
928
929
    trie_set(
930
        block_output.transactions_trie,
931
        rlp.encode(index),
932
        encode_transaction(tx),
933
    )
934
935
    intrinsic_gas, calldata_floor_gas_cost = validate_transaction(tx)
936
937
    (
938
        sender,
939
        effective_gas_price,
940
        blob_versioned_hashes,
941
        tx_blob_gas_used,
942
    ) = check_transaction(
943
        block_env=block_env,
944
        block_output=block_output,
945
        tx=tx,
946
    )
947
948
    sender_account = get_account(block_env.state, sender)
949
950
    if isinstance(tx, BlobTransaction):
951
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
952
    else:
953
        blob_gas_fee = Uint(0)
954
955
    effective_gas_fee = tx.gas * effective_gas_price
956
957
    gas = tx.gas - intrinsic_gas
958
959
    # Track sender nonce increment
960
    increment_nonce(block_env.state, sender)
961
    sender_nonce_after = get_account(block_env.state, sender).nonce
962
    track_nonce_change(tx_state_changes, sender, U64(sender_nonce_after))
963
964
    # Track sender balance deduction for gas fee
965
    sender_balance_before = get_account(block_env.state, sender).balance
966
    track_address(tx_state_changes, sender)
967
    capture_pre_balance(tx_state_changes, sender, sender_balance_before)
968
969
    sender_balance_after_gas_fee = (
970
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
971
    )
972
    set_account_balance(
973
        block_env.state, sender, U256(sender_balance_after_gas_fee)
974
    )
975
    track_balance_change(
976
        tx_state_changes,
977
        sender,
978
        U256(sender_balance_after_gas_fee),
979
    )
980
981
    access_list_addresses = set()
982
    access_list_storage_keys = set()
983
    access_list_addresses.add(block_env.coinbase)
984
    if isinstance(
985
        tx,
986
        (
987
            AccessListTransaction,
988
            FeeMarketTransaction,
989
            BlobTransaction,
990
            SetCodeTransaction,
991
        ),
992
    ):
993
        for access in tx.access_list:
994
            access_list_addresses.add(access.account)
995
            for slot in access.slots:
996
                access_list_storage_keys.add((access.account, slot))
997
998
    authorizations: Tuple[Authorization, ...] = ()
999
    if isinstance(tx, SetCodeTransaction):
1000
        authorizations = tx.authorizations
1001
1002
    tx_env = vm.TransactionEnvironment(
1003
        origin=sender,
1004
        gas_price=effective_gas_price,
1005
        gas=gas,
1006
        access_list_addresses=access_list_addresses,
1007
        access_list_storage_keys=access_list_storage_keys,
1008
        transient_storage=TransientStorage(),
1009
        blob_versioned_hashes=blob_versioned_hashes,
1010
        authorizations=authorizations,
1011
        index_in_block=index,
1012
        tx_hash=get_transaction_hash(encode_transaction(tx)),
1013
        state_changes=tx_state_changes,
1014
    )
1015
939
    message = prepare_message(block_env, tx_env, tx)
1016
    message = prepare_message(
1017
        block_env,
1018
        tx_env,
1019
        tx,
1020
    )
1021
1022
    tx_output = process_message_call(message)
1023
1024
    # For EIP-7623 we first calculate the execution_gas_used, which includes
1025
    # the execution gas refund.
1026
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
1027
    tx_gas_refund = min(
1028
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
1029
    )
1030
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
1031
1032
    # Transactions with less execution_gas_used than the floor pay at the
1033
    # floor cost.
1034
    tx_gas_used_after_refund = max(
1035
        tx_gas_used_after_refund, calldata_floor_gas_cost
1036
    )
1037
1038
    tx_gas_left = tx.gas - tx_gas_used_after_refund
1039
    gas_refund_amount = tx_gas_left * effective_gas_price
1040
1041
    # For non-1559 transactions effective_gas_price == tx.gas_price
1042
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
1043
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
1044
1045
    # refund gas
1046
    sender_balance_after_refund = get_account(
1047
        block_env.state, sender
1048
    ).balance + U256(gas_refund_amount)
1049
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
1050
    track_balance_change(
1051
        tx_env.state_changes,
1052
        sender,
1053
        sender_balance_after_refund,
1054
    )
1055
970
    # transfer miner fees
1056
    coinbase_balance_after_mining_fee = get_account(
1057
        block_env.state, block_env.coinbase
1058
    ).balance + U256(transaction_fee)
1059
1060
    set_account_balance(
975
        block_env.state,
1061
        block_env.state, block_env.coinbase, coinbase_balance_after_mining_fee
1062
    )
1063
    track_balance_change(
1064
        tx_env.state_changes,
1065
        block_env.coinbase,
1066
        coinbase_balance_after_mining_fee,
1067
    )
1068
980
    for address in tx_output.accounts_to_delete:
981
        destroy_account(block_env.state, address)
1069
    if coinbase_balance_after_mining_fee == 0 and account_exists_and_is_empty(
1070
        block_env.state, block_env.coinbase
1071
    ):
1072
        destroy_account(block_env.state, block_env.coinbase)
1073
1074
    block_output.block_gas_used += tx_gas_used_after_refund
1075
    block_output.blob_gas_used += tx_blob_gas_used
1076
1077
    receipt = make_receipt(
1078
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
1079
    )
1080
1081
    receipt_key = rlp.encode(Uint(index))
1082
    block_output.receipt_keys += (receipt_key,)
1083
1084
    trie_set(
1085
        block_output.receipts_trie,
1086
        receipt_key,
1087
        receipt,
1088
    )
1089
999
    block_output.block_logs += tx_output.logs
1090
    block_output.block_logs += tx_output.logs
1091
1092
    for address in tx_output.accounts_to_delete:
1093
        destroy_account(block_env.state, address)
1094
        track_selfdestruct(tx_env.state_changes, address)
1095
1096
    # EIP-7928: Commit transaction frame (includes net-zero filtering).
1097
    # Must happen AFTER destroy_account so filtering sees correct state.
1098
    commit_transaction_frame(tx_env.state_changes)

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.bpo5.vm.BlockEnvironmentethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.bpo5.vm.BlockOutputethereum.forks.amsterdam.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
1106
    """
1107
    Increase the balance of the withdrawing account.
1108
    """
1109
    # Capture pre-state for withdrawal balance filtering
1110
    withdrawal_addresses = {wd.address for wd in withdrawals}
1111
    for address in withdrawal_addresses:
1112
        pre_balance = get_account(block_env.state, address).balance
1113
        track_address(block_env.state_changes, address)
1114
        capture_pre_balance(block_env.state_changes, address, pre_balance)
1115
1116
    def increase_recipient_balance(recipient: Account) -> None:
1117
        recipient.balance += wd.amount * U256(10**9)
1118
1119
    for i, wd in enumerate(withdrawals):
1120
        trie_set(
1121
            block_output.withdrawals_trie,
1122
            rlp.encode(Uint(i)),
1123
            rlp.encode(wd),
1124
        )
1125
1021
        modify_state(block_env.state, wd.address, increase_recipient_balance)
1126
        modify_state(block_env.state, wd.address, increase_recipient_balance)
1127
1128
        new_balance = get_account(block_env.state, wd.address).balance
1129
        track_balance_change(
1130
            block_env.state_changes,
1131
            wd.address,
1132
            new_balance,
1133
        )
1134
1135
        if account_exists_and_is_empty(block_env.state, wd.address):
1136
            destroy_account(block_env.state, wd.address)
1137
1138
    # EIP-7928: Filter net-zero balance changes for withdrawals
1139
    filter_net_zero_frame_changes(block_env.state_changes)

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:
1143
    """
1144
    Validates the gas limit for a block.
1145
1146
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1147
    quotient of the parent block's gas limit and the
1148
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1149
    passed through as a parameter is greater than or equal to the *sum* of
1150
    the parent's gas and the adjustment delta then the limit for gas is too
1151
    high and fails this function's check. Similarly, if the limit is less
1152
    than or equal to the *difference* of the parent's gas and the adjustment
1153
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1154
    check fails because the gas limit doesn't allow for a sufficient or
1155
    reasonable amount of gas to be used on a block.
1156
1157
    Parameters
1158
    ----------
1159
    gas_limit :
1160
        Gas limit to validate.
1161
1162
    parent_gas_limit :
1163
        Gas limit of the parent block.
1164
1165
    Returns
1166
    -------
1167
    check : `bool`
1168
        True if gas limit constraints are satisfied, False otherwise.
1169
1170
    """
1171
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1172
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1173
        return False
1174
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1175
        return False
1176
    if gas_limit < GAS_LIMIT_MINIMUM:
1177
        return False
1178
1179
    return True