ethereum.forks.amsterdam.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

104
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

105
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

106
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

107
GAS_LIMIT_MINIMUM = Uint(5000)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

109
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

110
BEACON_ROOTS_ADDRESS = hex_to_address(
111
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
112
)

SYSTEM_TRANSACTION_GAS

113
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

114
MAX_BLOB_GAS_PER_BLOCK = BLOB_SCHEDULE_MAX * GAS_PER_BLOB

VERSIONED_HASH_VERSION_KZG

115
VERSIONED_HASH_VERSION_KZG = b"\x01"

GWEI_TO_WEI

116
GWEI_TO_WEI = U256(10**9)

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

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

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

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

HISTORY_STORAGE_ADDRESS

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

MAX_BLOCK_SIZE

127
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

128
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

129
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

130
BLOB_COUNT_LIMIT = 6

BlockChain

History and current state of the block chain.

133
@dataclass
class BlockChain:

blocks

139
    blocks: List[Block]

state

140
    state: State

chain_id

141
    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:
145
    """
146
    Transforms the state from the previous hard fork (`old`) into the block
147
    chain object for this hard fork and returns it.
148
149
    When forks need to implement an irregular state transition, this function
150
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
151
    an example.
152
153
    Parameters
154
    ----------
155
    old :
156
        Previous block chain object.
157
158
    Returns
159
    -------
160
    new : `BlockChain`
161
        Upgraded block chain object for this hard fork.
162
163
    """
164
    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]:
168
    """
169
    Obtain the list of hashes of the previous 256 blocks in order of
170
    increasing block number.
171
172
    This function will return less hashes for the first 256 blocks.
173
174
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
175
    therefore this function retrieves them.
176
177
    Parameters
178
    ----------
179
    chain :
180
        History and current state.
181
182
    Returns
183
    -------
184
    recent_block_hashes : `List[Hash32]`
185
        Hashes of the recent 256 blocks in order of increasing block number.
186
187
    """
188
    recent_blocks = chain.blocks[-255:]
189
    # TODO: This function has not been tested rigorously
190
    if len(recent_blocks) == 0:
191
        return []
192
193
    recent_block_hashes = []
194
195
    for block in recent_blocks:
196
        prev_block_hash = block.header.parent_hash
197
        recent_block_hashes.append(prev_block_hash)
198
199
    # We are computing the hash only for the most recent block and not for
200
    # the rest of the blocks as they have successors which have the hash of
201
    # the current block as parent hash.
202
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
203
    recent_block_hashes.append(most_recent_block_hash)
204
205
    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:
209
    """
210
    Attempts to apply a block to an existing block chain.
211
212
    All parts of the block's contents need to be verified before being added
213
    to the chain. Blocks are verified by ensuring that the contents of the
214
    block make logical sense with the contents of the parent block. The
215
    information in the block's header must also match the corresponding
216
    information in the block.
217
218
    To implement Ethereum, in theory clients are only required to store the
219
    most recent 255 blocks of the chain since as far as execution is
220
    concerned, only those blocks are accessed. Practically, however, clients
221
    should store more blocks to handle reorgs.
222
223
    Parameters
224
    ----------
225
    chain :
226
        History and current state.
227
    block :
228
        Block to apply to `chain`.
229
230
    """
231
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
232
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
233
234
    validate_header(chain, block.header)
235
    if block.ommers != ():
236
        raise InvalidBlock
237
238
    block_state = BlockState(pre_state=chain.state)
239
240
    block_env = vm.BlockEnvironment(
241
        chain_id=chain.chain_id,
242
        state=block_state,
243
        block_gas_limit=block.header.gas_limit,
244
        block_hashes=get_last_256_block_hashes(chain),
245
        coinbase=block.header.coinbase,
246
        number=block.header.number,
247
        base_fee_per_gas=block.header.base_fee_per_gas,
248
        time=block.header.timestamp,
249
        prev_randao=block.header.prev_randao,
250
        excess_blob_gas=block.header.excess_blob_gas,
251
        parent_beacon_block_root=block.header.parent_beacon_block_root,
252
        block_access_list_builder=BlockAccessListBuilder(),
253
    )
254
255
    block_output = apply_body(
256
        block_env=block_env,
257
        transactions=block.transactions,
258
        withdrawals=block.withdrawals,
259
    )
260
    account_changes, storage_changes = extract_block_diffs(block_state)
261
    block_state_root, _ = chain.state.compute_state_root_and_trie_changes(
262
        account_changes, storage_changes
263
    )
264
    apply_changes_to_state(chain.state, account_changes, storage_changes)
265
    transactions_root = root(block_output.transactions_trie)
266
    receipt_root = root(block_output.receipts_trie)
267
    block_logs_bloom = logs_bloom(block_output.block_logs)
268
    withdrawals_root = root(block_output.withdrawals_trie)
269
    requests_hash = compute_requests_hash(block_output.requests)
270
    computed_block_access_list_hash = compute_block_access_list_hash(
271
        block_output.block_access_list
272
    )
273
274
    if block_output.block_gas_used != block.header.gas_used:
275
        raise InvalidBlock(
276
            f"{block_output.block_gas_used} != {block.header.gas_used}"
277
        )
278
    if transactions_root != block.header.transactions_root:
279
        raise InvalidBlock
280
    if block_state_root != block.header.state_root:
281
        raise InvalidBlock
282
    if receipt_root != block.header.receipt_root:
283
        raise InvalidBlock
284
    if block_logs_bloom != block.header.bloom:
285
        raise InvalidBlock
286
    if withdrawals_root != block.header.withdrawals_root:
287
        raise InvalidBlock
288
    if block_output.blob_gas_used != block.header.blob_gas_used:
289
        raise InvalidBlock
290
    if requests_hash != block.header.requests_hash:
291
        raise InvalidBlock
292
    if computed_block_access_list_hash != block.header.block_access_list_hash:
293
        raise InvalidBlock("Invalid block access list hash")
294
295
    chain.blocks.append(block)
296
    if len(chain.blocks) > 255:
297
        # Real clients have to store more blocks to deal with reorgs, but the
298
        # protocol only requires the last 255
299
        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:
308
    """
309
    Calculates the base fee per gas for the block.
310
311
    Parameters
312
    ----------
313
    block_gas_limit :
314
        Gas limit of the block for which the base fee is being calculated.
315
    parent_gas_limit :
316
        Gas limit of the parent block.
317
    parent_gas_used :
318
        Gas used in the parent block.
319
    parent_base_fee_per_gas :
320
        Base fee per gas of the parent block.
321
322
    Returns
323
    -------
324
    base_fee_per_gas : `Uint`
325
        Base fee per gas for the block.
326
327
    """
328
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
329
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
330
        raise InvalidBlock
331
332
    if parent_gas_used == parent_gas_target:
333
        expected_base_fee_per_gas = parent_base_fee_per_gas
334
    elif parent_gas_used > parent_gas_target:
335
        gas_used_delta = parent_gas_used - parent_gas_target
336
337
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
338
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
339
340
        base_fee_per_gas_delta = max(
341
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
342
            Uint(1),
343
        )
344
345
        expected_base_fee_per_gas = (
346
            parent_base_fee_per_gas + base_fee_per_gas_delta
347
        )
348
    else:
349
        gas_used_delta = parent_gas_target - parent_gas_used
350
351
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
352
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
353
354
        base_fee_per_gas_delta = (
355
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
356
        )
357
358
        expected_base_fee_per_gas = (
359
            parent_base_fee_per_gas - base_fee_per_gas_delta
360
        )
361
362
    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:
366
    """
367
    Verifies a block header.
368
369
    In order to consider a block's header valid, the logic for the
370
    quantities in the header should match the logic for the block itself.
371
    For example the header timestamp should be greater than the block's parent
372
    timestamp because the block was created *after* the parent block.
373
    Additionally, the block's number should be directly following the parent
374
    block's number since it is the next block in the sequence.
375
376
    Parameters
377
    ----------
378
    chain :
379
        History and current state.
380
    header :
381
        Header to check for correctness.
382
383
    """
384
    if header.number < Uint(1):
385
        raise InvalidBlock
386
387
    parent_header = chain.blocks[-1].header
388
389
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
390
    if header.excess_blob_gas != excess_blob_gas:
391
        raise InvalidBlock
392
393
    if header.gas_used > header.gas_limit:
394
        raise InvalidBlock
395
396
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
397
        header.gas_limit,
398
        parent_header.gas_limit,
399
        parent_header.gas_used,
400
        parent_header.base_fee_per_gas,
401
    )
402
    if expected_base_fee_per_gas != header.base_fee_per_gas:
403
        raise InvalidBlock
404
    if header.timestamp <= parent_header.timestamp:
405
        raise InvalidBlock
406
    if header.number != parent_header.number + Uint(1):
407
        raise InvalidBlock
408
    if len(header.extra_data) > 32:
409
        raise InvalidBlock
410
    if header.difficulty != 0:
411
        raise InvalidBlock
412
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
413
        raise InvalidBlock
414
    if header.ommers_hash != EMPTY_OMMER_HASH:
415
        raise InvalidBlock
416
417
    block_parent_hash = keccak256(rlp.encode(parent_header))
418
    if header.parent_hash != block_parent_hash:
419
        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. tx_state : The transaction state tracker.

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.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.amsterdam.vm.BlockOutput, ​​tx: Transaction, ​​tx_state: TransactionState) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
428
    """
429
    Check if the transaction is includable in the block.
430
431
    Parameters
432
    ----------
433
    block_env :
434
        The block scoped environment.
435
    block_output :
436
        The block output for the current block.
437
    tx :
438
        The transaction.
439
    tx_state :
440
        The transaction state tracker.
441
442
    Returns
443
    -------
444
    sender_address :
445
        The sender of the transaction.
446
    effective_gas_price :
447
        The price to charge for gas when the transaction is executed.
448
    blob_versioned_hashes :
449
        The blob versioned hashes of the transaction.
450
    tx_blob_gas_used:
451
        The blob gas used by the transaction.
452
453
    Raises
454
    ------
455
    InvalidBlock :
456
        If the transaction is not includable.
457
    GasUsedExceedsLimitError :
458
        If the gas used by the transaction exceeds the block's gas limit.
459
    NonceMismatchError :
460
        If the nonce of the transaction is not equal to the sender's nonce.
461
    InsufficientBalanceError :
462
        If the sender's balance is not enough to pay for the transaction.
463
    InvalidSenderError :
464
        If the transaction is from an address that does not exist anymore.
465
    PriorityFeeGreaterThanMaxFeeError :
466
        If the priority fee is greater than the maximum fee per gas.
467
    InsufficientMaxFeePerGasError :
468
        If the maximum fee per gas is insufficient for the transaction.
469
    InsufficientMaxFeePerBlobGasError :
470
        If the maximum fee per blob gas is insufficient for the transaction.
471
    BlobGasLimitExceededError :
472
        If the blob gas used by the transaction exceeds the block's blob gas
473
        limit.
474
    InvalidBlobVersionedHashError :
475
        If the transaction contains a blob versioned hash with an invalid
476
        version.
477
    NoBlobDataError :
478
        If the transaction is a type 3 but has no blobs.
479
    BlobCountExceededError :
480
        If the transaction is a type 3 and has more blobs than the limit.
481
    TransactionTypeContractCreationError:
482
        If the transaction type is not allowed to create contracts.
483
    EmptyAuthorizationListError :
484
        If the transaction is a SetCodeTransaction and the authorization list
485
        is empty.
486
487
    """
488
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
489
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
490
491
    if tx.gas > gas_available:
492
        raise GasUsedExceedsLimitError("gas used exceeds limit")
493
494
    tx_blob_gas_used = calculate_total_blob_gas(tx)
495
    if tx_blob_gas_used > blob_gas_available:
496
        raise BlobGasLimitExceededError("blob gas limit exceeded")
497
498
    sender_address = recover_sender(block_env.chain_id, tx)
499
    sender_account = get_account(tx_state, sender_address)
500
501
    if isinstance(
502
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
503
    ):
504
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
505
            raise PriorityFeeGreaterThanMaxFeeError(
506
                "priority fee greater than max fee"
507
            )
508
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
509
            raise InsufficientMaxFeePerGasError(
510
                tx.max_fee_per_gas, block_env.base_fee_per_gas
511
            )
512
513
        priority_fee_per_gas = min(
514
            tx.max_priority_fee_per_gas,
515
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
516
        )
517
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
518
        max_gas_fee = tx.gas * tx.max_fee_per_gas
519
    else:
520
        if tx.gas_price < block_env.base_fee_per_gas:
521
            raise InvalidBlock
522
        effective_gas_price = tx.gas_price
523
        max_gas_fee = tx.gas * tx.gas_price
524
525
    if isinstance(tx, BlobTransaction):
526
        blob_count = len(tx.blob_versioned_hashes)
527
        if blob_count == 0:
528
            raise NoBlobDataError("no blob data in transaction")
529
        if blob_count > BLOB_COUNT_LIMIT:
530
            raise BlobCountExceededError(
531
                f"Tx has {blob_count} blobs. Max allowed: {BLOB_COUNT_LIMIT}"
532
            )
533
        for blob_versioned_hash in tx.blob_versioned_hashes:
534
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
535
                raise InvalidBlobVersionedHashError(
536
                    "invalid blob versioned hash"
537
                )
538
539
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
540
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
541
            raise InsufficientMaxFeePerBlobGasError(
542
                "insufficient max fee per blob gas"
543
            )
544
545
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
546
            tx.max_fee_per_blob_gas
547
        )
548
        blob_versioned_hashes = tx.blob_versioned_hashes
549
    else:
550
        blob_versioned_hashes = ()
551
552
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
553
        if not isinstance(tx.to, Address):
554
            raise TransactionTypeContractCreationError(tx)
555
556
    if isinstance(tx, SetCodeTransaction):
557
        if not any(tx.authorizations):
558
            raise EmptyAuthorizationListError("empty authorization list")
559
560
    if sender_account.nonce > Uint(tx.nonce):
561
        raise NonceMismatchError("nonce too low")
562
    elif sender_account.nonce < Uint(tx.nonce):
563
        raise NonceMismatchError("nonce too high")
564
565
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
566
        raise InsufficientBalanceError("insufficient sender balance")
567
    if sender_account.code and not is_valid_delegation(sender_account.code):
568
        raise InvalidSenderError("not EOA")
569
570
    return (
571
        sender_address,
572
        effective_gas_price,
573
        blob_versioned_hashes,
574
        tx_blob_gas_used,
575
    )

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:
584
    """
585
    Make the receipt for a transaction that was executed.
586
587
    Parameters
588
    ----------
589
    tx :
590
        The executed transaction.
591
    error :
592
        Error in the top level frame of the transaction, if any.
593
    cumulative_gas_used :
594
        The total gas used so far in the block after the transaction was
595
        executed.
596
    logs :
597
        The logs produced by the transaction.
598
599
    Returns
600
    -------
601
    receipt :
602
        The receipt for the transaction.
603
604
    """
605
    receipt = Receipt(
606
        succeeded=error is None,
607
        cumulative_gas_used=cumulative_gas_used,
608
        bloom=logs_bloom(logs),
609
        logs=logs,
610
    )
611
612
    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.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
621
    """
622
    Process a system transaction with the given code.
623
624
    Prefer calling `process_checked_system_transaction` or
625
    `process_unchecked_system_transaction` depending on whether missing code or
626
    an execution error should cause the block to be rejected.
627
628
    Parameters
629
    ----------
630
    block_env :
631
        The block scoped environment.
632
    target_address :
633
        Address of the contract to call.
634
    system_contract_code :
635
        Code of the contract to call.
636
    data :
637
        Data to pass to the contract.
638
639
    Returns
640
    -------
641
    system_tx_output : `MessageCallOutput`
642
        Output of processing the system transaction.
643
644
    """
645
    system_tx_state = TransactionState(parent=block_env.state)
646
647
    tx_env = vm.TransactionEnvironment(
648
        origin=SYSTEM_ADDRESS,
649
        gas_price=block_env.base_fee_per_gas,
650
        gas=SYSTEM_TRANSACTION_GAS,
651
        access_list_addresses=set(),
652
        access_list_storage_keys=set(),
653
        state=system_tx_state,
654
        blob_versioned_hashes=(),
655
        authorizations=(),
656
        index_in_block=None,
657
        tx_hash=None,
658
    )
659
660
    system_tx_message = Message(
661
        block_env=block_env,
662
        tx_env=tx_env,
663
        caller=SYSTEM_ADDRESS,
664
        target=target_address,
665
        gas=SYSTEM_TRANSACTION_GAS,
666
        value=U256(0),
667
        data=data,
668
        code=system_contract_code,
669
        depth=Uint(0),
670
        current_target=target_address,
671
        code_address=target_address,
672
        should_transfer_value=False,
673
        is_static=False,
674
        accessed_addresses=set(),
675
        accessed_storage_keys=set(),
676
        disable_precompiles=False,
677
        parent_evm=None,
678
        is_create=False,
679
    )
680
681
    system_tx_output = process_message_call(system_tx_message)
682
683
    incorporate_tx_into_block(
684
        system_tx_state, block_env.block_access_list_builder
685
    )
686
687
    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.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
695
    """
696
    Process a system transaction and raise an error if the contract does not
697
    contain code or if the transaction fails.
698
699
    Parameters
700
    ----------
701
    block_env :
702
        The block scoped environment.
703
    target_address :
704
        Address of the contract to call.
705
    data :
706
        Data to pass to the contract.
707
708
    Returns
709
    -------
710
    system_tx_output : `MessageCallOutput`
711
        Output of processing the system transaction.
712
713
    """
714
    system_tx_state = TransactionState(parent=block_env.state)
715
    system_contract_code = get_account(system_tx_state, target_address).code
716
717
    if len(system_contract_code) == 0:
718
        raise InvalidBlock(
719
            f"System contract address {target_address.hex()} does not "
720
            "contain code"
721
        )
722
723
    system_tx_output = process_system_transaction(
724
        block_env,
725
        target_address,
726
        system_contract_code,
727
        data,
728
    )
729
730
    if system_tx_output.error:
731
        raise InvalidBlock(
732
            f"System contract ({target_address.hex()}) call failed: "
733
            f"{system_tx_output.error}"
734
        )
735
736
    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.amsterdam.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
744
    """
745
    Process a system transaction without checking if the contract contains code
746
    or if the transaction fails.
747
748
    Parameters
749
    ----------
750
    block_env :
751
        The block scoped environment.
752
    target_address :
753
        Address of the contract to call.
754
    data :
755
        Data to pass to the contract.
756
757
    Returns
758
    -------
759
    system_tx_output : `MessageCallOutput`
760
        Output of processing the system transaction.
761
762
    """
763
    system_tx_state = TransactionState(parent=block_env.state)
764
    system_contract_code = get_account(system_tx_state, target_address).code
765
    return process_system_transaction(
766
        block_env,
767
        target_address,
768
        system_contract_code,
769
        data,
770
    )

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

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

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.amsterdam.vm.BlockEnvironment, ​​block_output: ethereum.forks.amsterdam.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
1071
    """
1072
    Increase the balance of the withdrawing account.
1073
    """
1074
    wd_state = TransactionState(parent=block_env.state)
1075
1076
    for i, wd in enumerate(withdrawals):
1077
        trie_set(
1078
            block_output.withdrawals_trie,
1079
            rlp.encode(Uint(i)),
1080
            rlp.encode(wd),
1081
        )
1082
1083
        track_address(wd_state, wd.address)
1084
        current_balance = get_account(wd_state, wd.address).balance
1085
        new_balance = current_balance + wd.amount * GWEI_TO_WEI
1086
        set_account_balance(wd_state, wd.address, new_balance)
1087
1088
    incorporate_tx_into_block(wd_state, block_env.block_access_list_builder)

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:
1092
    """
1093
    Validates the gas limit for a block.
1094
1095
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1096
    quotient of the parent block's gas limit and the
1097
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1098
    passed through as a parameter is greater than or equal to the *sum* of
1099
    the parent's gas and the adjustment delta then the limit for gas is too
1100
    high and fails this function's check. Similarly, if the limit is less
1101
    than or equal to the *difference* of the parent's gas and the adjustment
1102
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1103
    check fails because the gas limit doesn't allow for a sufficient or
1104
    reasonable amount of gas to be used on a block.
1105
1106
    Parameters
1107
    ----------
1108
    gas_limit :
1109
        Gas limit to validate.
1110
1111
    parent_gas_limit :
1112
        Gas limit of the parent block.
1113
1114
    Returns
1115
    -------
1116
    check : `bool`
1117
        True if gas limit constraints are satisfied, False otherwise.
1118
1119
    """
1120
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1121
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1122
        return False
1123
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1124
        return False
1125
    if gas_limit < GAS_LIMIT_MINIMUM:
1126
        return False
1127
1128
    return True