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

110
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

111
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

113
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

114
BEACON_ROOTS_ADDRESS = hex_to_address(
115
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
116
)

SYSTEM_TRANSACTION_GAS

117
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

118
MAX_BLOB_GAS_PER_BLOCK = GasCosts.BLOB_SCHEDULE_MAX * GasCosts.PER_BLOB

VERSIONED_HASH_VERSION_KZG

119
VERSIONED_HASH_VERSION_KZG = b"\x01"

GWEI_TO_WEI

120
GWEI_TO_WEI = U256(10**9)

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

122
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
123
    "0x00000961Ef480Eb55e80D19ad83579A64c007002"
124
)

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

125
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
126
    "0x0000BBdDc7CE488642fb579F8B00f3a590007251"
127
)

HISTORY_STORAGE_ADDRESS

128
HISTORY_STORAGE_ADDRESS = hex_to_address(
129
    "0x0000F90827F1C53a10cb7A02335B175320002935"
130
)

MAX_BLOCK_SIZE

131
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

132
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

133
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

134
BLOB_COUNT_LIMIT = 6

ChainContext

Chain context needed for block execution.

137
@slotted_freezable
138
@dataclass
class ChainContext:

chain_id

Identify the chain for transaction signature recovery.

144
    chain_id: U64

block_hashes

Recent ancestor hashes (up to 256) for the BLOCKHASH opcode.

147
    block_hashes: List[Hash32]

parent_header

Parent header used for header validation and system contracts.

150
    parent_header: Header | PreviousHeader

BlockChain

History and current state of the block chain.

154
@dataclass
class BlockChain:

blocks

160
    blocks: List[Block]

state

161
    state: State

chain_id

162
    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:
166
    """
167
    Transforms the state from the previous hard fork (`old`) into the block
168
    chain object for this hard fork and returns it.
169
170
    When forks need to implement an irregular state transition, this function
171
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
172
    an example.
173
174
    Parameters
175
    ----------
176
    old :
177
        Previous block chain object.
178
179
    Returns
180
    -------
181
    new : `BlockChain`
182
        Upgraded block chain object for this hard fork.
183
184
    """
185
    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]:
189
    """
190
    Obtain the list of hashes of the previous 256 blocks in order of
191
    increasing block number.
192
193
    This function will return less hashes for the first 256 blocks.
194
195
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
196
    therefore this function retrieves them.
197
198
    Parameters
199
    ----------
200
    chain :
201
        History and current state.
202
203
    Returns
204
    -------
205
    recent_block_hashes : `List[Hash32]`
206
        Hashes of the recent 256 blocks in order of increasing block number.
207
208
    """
209
    recent_blocks = chain.blocks[-255:]
210
    # TODO: This function has not been tested rigorously
211
    if len(recent_blocks) == 0:
212
        return []
213
214
    recent_block_hashes = []
215
216
    for block in recent_blocks:
217
        prev_block_hash = block.header.parent_hash
218
        recent_block_hashes.append(prev_block_hash)
219
220
    # We are computing the hash only for the most recent block and not for
221
    # the rest of the blocks as they have successors which have the hash of
222
    # the current block as parent hash.
223
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
224
    recent_block_hashes.append(most_recent_block_hash)
225
226
    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:
230
    """
231
    Attempts to apply a block to an existing block chain.
232
233
    All parts of the block's contents need to be verified before being added
234
    to the chain. Blocks are verified by ensuring that the contents of the
235
    block make logical sense with the contents of the parent block. The
236
    information in the block's header must also match the corresponding
237
    information in the block.
238
239
    To implement Ethereum, in theory clients are only required to store the
240
    most recent 255 blocks of the chain since as far as execution is
241
    concerned, only those blocks are accessed. Practically, however, clients
242
    should store more blocks to handle reorgs.
243
244
    Parameters
245
    ----------
246
    chain :
247
        History and current state.
248
    block :
249
        Block to apply to `chain`.
250
251
    """
252
    chain_context = ChainContext(
253
        chain_id=chain.chain_id,
254
        block_hashes=get_last_256_block_hashes(chain),
255
        parent_header=chain.blocks[-1].header,
256
    )
257
258
    block_diff = execute_block(block, chain.state, chain_context)
259
260
    apply_changes_to_state(chain.state, block_diff)
261
    chain.blocks.append(block)
262
    if len(chain.blocks) > 255:
263
        # Real clients have to store more blocks to deal with reorgs, but the
264
        # protocol only requires the last 255
265
        chain.blocks = chain.blocks[-255:]

execute_block

Execute a block and validate the resulting roots against the header.

This method is idempotent.

Parameters

block : Block to validate and execute. pre_state : Pre-execution state provider. chain_context : Chain context that the block may need during execution.

Returns

block_diff : BlockDiff Account, storage, and code changes produced by block execution.

def execute_block(block: Block, ​​pre_state: State, ​​chain_context: ChainContext) -> BlockDiff:
273
    """
274
    Execute a block and validate the resulting roots against the header.
275
276
    This method is idempotent.
277
278
    Parameters
279
    ----------
280
    block :
281
        Block to validate and execute.
282
    pre_state :
283
        Pre-execution state provider.
284
    chain_context :
285
        Chain context that the block may need during execution.
286
287
    Returns
288
    -------
289
    block_diff : `BlockDiff`
290
        Account, storage, and code changes produced by block execution.
291
292
    """
293
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
294
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
295
296
    parent_header = chain_context.parent_header
297
    validate_header(parent_header, block.header)
298
299
    if block.ommers != ():
300
        raise InvalidBlock
301
302
    block_state = BlockState(pre_state=pre_state)
303
304
    block_env = vm.BlockEnvironment(
305
        chain_id=chain_context.chain_id,
306
        state=block_state,
307
        block_gas_limit=block.header.gas_limit,
308
        block_hashes=chain_context.block_hashes,
309
        coinbase=block.header.coinbase,
310
        number=block.header.number,
311
        base_fee_per_gas=block.header.base_fee_per_gas,
312
        time=block.header.timestamp,
313
        prev_randao=block.header.prev_randao,
314
        excess_blob_gas=block.header.excess_blob_gas,
315
        parent_beacon_block_root=block.header.parent_beacon_block_root,
316
        block_access_list_builder=BlockAccessListBuilder(),
317
    )
318
319
    block_output = apply_body(
320
        block_env=block_env,
321
        transactions=block.transactions,
322
        withdrawals=block.withdrawals,
323
    )
324
    block_diff = extract_block_diff(block_state)
325
    block_state_root, _ = pre_state.compute_state_root_and_trie_changes(
326
        block_diff.account_changes, block_diff.storage_changes
327
    )
328
    transactions_root = root(block_output.transactions_trie)
329
    receipt_root = root(block_output.receipts_trie)
330
    block_logs_bloom = logs_bloom(block_output.block_logs)
331
    withdrawals_root = root(block_output.withdrawals_trie)
332
    requests_hash = compute_requests_hash(block_output.requests)
333
    computed_block_access_list_hash = hash_block_access_list(
334
        block_output.block_access_list
335
    )
336
337
    if block_output.block_gas_used != block.header.gas_used:
338
        raise InvalidBlock(
339
            f"{block_output.block_gas_used} != {block.header.gas_used}"
340
        )
341
    if transactions_root != block.header.transactions_root:
342
        raise InvalidBlock
343
    if block_state_root != block.header.state_root:
344
        raise InvalidBlock
345
    if receipt_root != block.header.receipt_root:
346
        raise InvalidBlock
347
    if block_logs_bloom != block.header.bloom:
348
        raise InvalidBlock
349
    if withdrawals_root != block.header.withdrawals_root:
350
        raise InvalidBlock
351
    if block_output.blob_gas_used != block.header.blob_gas_used:
352
        raise InvalidBlock
353
    if requests_hash != block.header.requests_hash:
354
        raise InvalidBlock
355
    if computed_block_access_list_hash != block.header.block_access_list_hash:
356
        raise InvalidBlock("Invalid block access list hash")
357
358
    return block_diff

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:
367
    """
368
    Calculates the base fee per gas for the block.
369
370
    Parameters
371
    ----------
372
    block_gas_limit :
373
        Gas limit of the block for which the base fee is being calculated.
374
    parent_gas_limit :
375
        Gas limit of the parent block.
376
    parent_gas_used :
377
        Gas used in the parent block.
378
    parent_base_fee_per_gas :
379
        Base fee per gas of the parent block.
380
381
    Returns
382
    -------
383
    base_fee_per_gas : `Uint`
384
        Base fee per gas for the block.
385
386
    """
387
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
388
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
389
        raise InvalidBlock
390
391
    if parent_gas_used == parent_gas_target:
392
        expected_base_fee_per_gas = parent_base_fee_per_gas
393
    elif parent_gas_used > parent_gas_target:
394
        gas_used_delta = parent_gas_used - parent_gas_target
395
396
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
397
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
398
399
        base_fee_per_gas_delta = max(
400
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
401
            Uint(1),
402
        )
403
404
        expected_base_fee_per_gas = (
405
            parent_base_fee_per_gas + base_fee_per_gas_delta
406
        )
407
    else:
408
        gas_used_delta = parent_gas_target - parent_gas_used
409
410
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
411
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
412
413
        base_fee_per_gas_delta = (
414
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
415
        )
416
417
        expected_base_fee_per_gas = (
418
            parent_base_fee_per_gas - base_fee_per_gas_delta
419
        )
420
421
    return Uint(expected_base_fee_per_gas)

validate_header

Verify a block header against its parent.

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

parent_header : Header of the parent block. header : Header to check for correctness.

def validate_header(parent_header: Header | PreviousHeader, ​​header: Header) -> None:
427
    """
428
    Verify a block header against its parent.
429
430
    In order to consider a block's header valid, the logic for the
431
    quantities in the header should match the logic for the block itself.
432
    For example the header timestamp should be greater than the block's parent
433
    timestamp because the block was created *after* the parent block.
434
    Additionally, the block's number should be directly following the parent
435
    block's number since it is the next block in the sequence.
436
437
    Parameters
438
    ----------
439
    parent_header :
440
        Header of the parent block.
441
    header :
442
        Header to check for correctness.
443
444
    """
445
    if header.number < Uint(1):
446
        raise InvalidBlock
447
448
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
449
    if header.excess_blob_gas != excess_blob_gas:
450
        raise InvalidBlock
451
452
    if header.gas_used > header.gas_limit:
453
        raise InvalidBlock
454
455
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
456
        header.gas_limit,
457
        parent_header.gas_limit,
458
        parent_header.gas_used,
459
        parent_header.base_fee_per_gas,
460
    )
461
    if expected_base_fee_per_gas != header.base_fee_per_gas:
462
        raise InvalidBlock
463
    if header.timestamp <= parent_header.timestamp:
464
        raise InvalidBlock
465
    if header.number != parent_header.number + Uint(1):
466
        raise InvalidBlock
467
    if len(header.extra_data) > 32:
468
        raise InvalidBlock
469
    if header.difficulty != 0:
470
        raise InvalidBlock
471
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
472
        raise InvalidBlock
473
    if header.ommers_hash != EMPTY_OMMER_HASH:
474
        raise InvalidBlock
475
476
    block_parent_hash = keccak256(rlp.encode(parent_header))
477
    if header.parent_hash != block_parent_hash:
478
        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]:
487
    """
488
    Check if the transaction is includable in the block.
489
490
    Parameters
491
    ----------
492
    block_env :
493
        The block scoped environment.
494
    block_output :
495
        The block output for the current block.
496
    tx :
497
        The transaction.
498
    tx_state :
499
        The transaction state tracker.
500
501
    Returns
502
    -------
503
    sender_address :
504
        The sender of the transaction.
505
    effective_gas_price :
506
        The price to charge for gas when the transaction is executed.
507
    blob_versioned_hashes :
508
        The blob versioned hashes of the transaction.
509
    tx_blob_gas_used:
510
        The blob gas used by the transaction.
511
512
    Raises
513
    ------
514
    InvalidBlock :
515
        If the transaction is not includable.
516
    GasUsedExceedsLimitError :
517
        If the gas used by the transaction exceeds the block's gas limit.
518
    NonceMismatchError :
519
        If the nonce of the transaction is not equal to the sender's nonce.
520
    InsufficientBalanceError :
521
        If the sender's balance is not enough to pay for the transaction.
522
    InvalidSenderError :
523
        If the transaction is from an address that does not exist anymore.
524
    PriorityFeeGreaterThanMaxFeeError :
525
        If the priority fee is greater than the maximum fee per gas.
526
    InsufficientMaxFeePerGasError :
527
        If the maximum fee per gas is insufficient for the transaction.
528
    InsufficientMaxFeePerBlobGasError :
529
        If the maximum fee per blob gas is insufficient for the transaction.
530
    BlobGasLimitExceededError :
531
        If the blob gas used by the transaction exceeds the block's blob gas
532
        limit.
533
    InvalidBlobVersionedHashError :
534
        If the transaction contains a blob versioned hash with an invalid
535
        version.
536
    NoBlobDataError :
537
        If the transaction is a type 3 but has no blobs.
538
    BlobCountExceededError :
539
        If the transaction is a type 3 and has more blobs than the limit.
540
    TransactionTypeContractCreationError:
541
        If the transaction type is not allowed to create contracts.
542
    EmptyAuthorizationListError :
543
        If the transaction is a SetCodeTransaction and the authorization list
544
        is empty.
545
546
    """
547
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
548
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
549
550
    if tx.gas > gas_available:
551
        raise GasUsedExceedsLimitError("gas used exceeds limit")
552
553
    tx_blob_gas_used = calculate_total_blob_gas(tx)
554
    if tx_blob_gas_used > blob_gas_available:
555
        raise BlobGasLimitExceededError("blob gas limit exceeded")
556
557
    sender_address = recover_sender(block_env.chain_id, tx)
558
    sender_account = get_account(tx_state, sender_address)
559
560
    if isinstance(
561
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
562
    ):
563
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
564
            raise PriorityFeeGreaterThanMaxFeeError(
565
                "priority fee greater than max fee"
566
            )
567
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
568
            raise InsufficientMaxFeePerGasError(
569
                tx.max_fee_per_gas, block_env.base_fee_per_gas
570
            )
571
572
        priority_fee_per_gas = min(
573
            tx.max_priority_fee_per_gas,
574
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
575
        )
576
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
577
        max_gas_fee = tx.gas * tx.max_fee_per_gas
578
    else:
579
        if tx.gas_price < block_env.base_fee_per_gas:
580
            raise InvalidBlock
581
        effective_gas_price = tx.gas_price
582
        max_gas_fee = tx.gas * tx.gas_price
583
584
    if isinstance(tx, BlobTransaction):
585
        blob_count = len(tx.blob_versioned_hashes)
586
        if blob_count == 0:
587
            raise NoBlobDataError("no blob data in transaction")
588
        if blob_count > BLOB_COUNT_LIMIT:
589
            raise BlobCountExceededError(
590
                f"Tx has {blob_count} blobs. Max allowed: {BLOB_COUNT_LIMIT}"
591
            )
592
        for blob_versioned_hash in tx.blob_versioned_hashes:
593
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
594
                raise InvalidBlobVersionedHashError(
595
                    "invalid blob versioned hash"
596
                )
597
598
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
599
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
600
            raise InsufficientMaxFeePerBlobGasError(
601
                "insufficient max fee per blob gas"
602
            )
603
604
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
605
            tx.max_fee_per_blob_gas
606
        )
607
        blob_versioned_hashes = tx.blob_versioned_hashes
608
    else:
609
        blob_versioned_hashes = ()
610
611
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
612
        if not isinstance(tx.to, Address):
613
            raise TransactionTypeContractCreationError(tx)
614
615
    if isinstance(tx, SetCodeTransaction):
616
        if not any(tx.authorizations):
617
            raise EmptyAuthorizationListError("empty authorization list")
618
619
    if sender_account.nonce > Uint(tx.nonce):
620
        raise NonceMismatchError("nonce too low")
621
    elif sender_account.nonce < Uint(tx.nonce):
622
        raise NonceMismatchError("nonce too high")
623
624
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
625
        raise InsufficientBalanceError("insufficient sender balance")
626
    sender_code = get_code(tx_state, sender_account.code_hash)
627
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
628
        sender_code
629
    ):
630
        raise InvalidSenderError("not EOA")
631
632
    return (
633
        sender_address,
634
        effective_gas_price,
635
        blob_versioned_hashes,
636
        tx_blob_gas_used,
637
    )

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. This is the gas used after refunds. 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:
646
    """
647
    Make the receipt for a transaction that was executed.
648
649
    Parameters
650
    ----------
651
    tx :
652
        The executed transaction.
653
    error :
654
        Error in the top level frame of the transaction, if any.
655
    cumulative_gas_used :
656
        The total gas used so far in the block after the transaction was
657
        executed. This is the gas used after refunds.
658
    logs :
659
        The logs produced by the transaction.
660
661
    Returns
662
    -------
663
    receipt :
664
        The receipt for the transaction.
665
666
    """
667
    receipt = Receipt(
668
        succeeded=error is None,
669
        cumulative_gas_used=cumulative_gas_used,
670
        bloom=logs_bloom(logs),
671
        logs=logs,
672
    )
673
674
    return encode_receipt(tx, receipt)

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:
682
    """
683
    Process a system transaction and raise an error if the contract does not
684
    contain code or if the transaction fails.
685
686
    Parameters
687
    ----------
688
    block_env :
689
        The block scoped environment.
690
    target_address :
691
        Address of the contract to call.
692
    data :
693
        Data to pass to the contract.
694
695
    Returns
696
    -------
697
    system_tx_output : `MessageCallOutput`
698
        Output of processing the system transaction.
699
700
    """
701
    # Pre-check that the system contract has code. We use a throwaway
702
    # TransactionState here that is *never* propagated back to BlockState
703
    # (no incorporate_tx_into_block call); the same get_account / get_code
704
    # lookups are performed and properly tracked by
705
    # process_unchecked_system_transaction below, which this function
706
    # always calls. Reading via a TransactionState (rather than directly
707
    # against pre_state) lets us see system contracts deployed earlier in
708
    # the same block — see EIP-7002 and EIP-7251 for this edge case.
709
    untracked_state = TransactionState(parent=block_env.state)
710
    system_contract_code = get_code(
711
        untracked_state,
712
        get_account(untracked_state, target_address).code_hash,
713
    )
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_unchecked_system_transaction(
722
        block_env,
723
        target_address,
724
        data,
725
    )
726
727
    if system_tx_output.error:
728
        raise InvalidBlock(
729
            f"System contract ({target_address.hex()}) call failed: "
730
            f"{system_tx_output.error}"
731
        )
732
733
    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:
741
    """
742
    Process a system transaction without checking if the contract contains
743
    code or if the transaction fails.
744
745
    Parameters
746
    ----------
747
    block_env :
748
        The block scoped environment.
749
    target_address :
750
        Address of the contract to call.
751
    data :
752
        Data to pass to the contract.
753
754
    Returns
755
    -------
756
    system_tx_output : `MessageCallOutput`
757
        Output of processing the system transaction.
758
759
    """
760
    system_tx_state = TransactionState(parent=block_env.state)
761
    system_contract_code = get_code(
762
        system_tx_state,
763
        get_account(system_tx_state, target_address).code_hash,
764
    )
765
766
    tx_env = vm.TransactionEnvironment(
767
        origin=SYSTEM_ADDRESS,
768
        gas_price=block_env.base_fee_per_gas,
769
        gas=SYSTEM_TRANSACTION_GAS,
770
        access_list_addresses=set(),
771
        access_list_storage_keys=set(),
772
        state=system_tx_state,
773
        blob_versioned_hashes=(),
774
        authorizations=(),
775
        index_in_block=None,
776
        tx_hash=None,
777
    )
778
779
    system_tx_message = Message(
780
        block_env=block_env,
781
        tx_env=tx_env,
782
        caller=SYSTEM_ADDRESS,
783
        target=target_address,
784
        gas=SYSTEM_TRANSACTION_GAS,
785
        value=U256(0),
786
        data=data,
787
        code=system_contract_code,
788
        depth=Uint(0),
789
        current_target=target_address,
790
        code_address=target_address,
791
        should_transfer_value=False,
792
        is_static=False,
793
        accessed_addresses=set(),
794
        accessed_storage_keys=set(),
795
        disable_precompiles=False,
796
        parent_evm=None,
797
    )
798
799
    system_tx_output = process_message_call(system_tx_message)
800
801
    incorporate_tx_into_block(
802
        system_tx_state, block_env.block_access_list_builder
803
    )
804
805
    return system_tx_output

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:
813
    """
814
    Executes a block.
815
816
    Many of the contents of a block are stored in data structures called
817
    tries. There is a transactions trie which is similar to a ledger of the
818
    transactions stored in the current block. There is also a receipts trie
819
    which stores the results of executing a transaction, like the post state
820
    and gas used. This function creates and executes the block that is to be
821
    added to the chain.
822
823
    Parameters
824
    ----------
825
    block_env :
826
        The block scoped environment.
827
    transactions :
828
        Transactions included in the block.
829
    withdrawals :
830
        Withdrawals to be processed in the current block.
831
832
    Returns
833
    -------
834
    block_output :
835
        The block output for the current block.
836
837
    """
838
    block_output = vm.BlockOutput()
839
840
    process_unchecked_system_transaction(
841
        block_env=block_env,
842
        target_address=BEACON_ROOTS_ADDRESS,
843
        data=block_env.parent_beacon_block_root,
844
    )
845
846
    process_unchecked_system_transaction(
847
        block_env=block_env,
848
        target_address=HISTORY_STORAGE_ADDRESS,
849
        data=block_env.block_hashes[-1],  # The parent hash
850
    )
851
852
    for i, tx in enumerate(map(decode_transaction, transactions)):
853
        process_transaction(block_env, block_output, tx, Uint(i))
854
855
    # EIP-7928: Post-execution operations use index N+1
856
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
857
        ulen(transactions) + Uint(1)
858
    )
859
860
    process_withdrawals(block_env, block_output, withdrawals)
861
862
    process_general_purpose_requests(
863
        block_env=block_env,
864
        block_output=block_output,
865
    )
866
867
    block_output.block_access_list = build_block_access_list(
868
        block_env.block_access_list_builder, block_env.state
869
    )
870
871
    # Validate block access list gas limit constraint (EIP-7928)
872
    validate_block_access_list_gas_limit(
873
        block_access_list=block_output.block_access_list,
874
        block_gas_limit=block_env.block_gas_limit,
875
    )
876
877
    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:
884
    """
885
    Process all the requests in the block.
886
887
    Parameters
888
    ----------
889
    block_env :
890
        The execution environment for the Block.
891
    block_output :
892
        The block output for the current block.
893
894
    """
895
    # Requests are to be in ascending order of request type
896
    deposit_requests = parse_deposit_requests(block_output)
897
    requests_from_execution = block_output.requests
898
    if len(deposit_requests) > 0:
899
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
900
901
    system_withdrawal_tx_output = process_checked_system_transaction(
902
        block_env=block_env,
903
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
904
        data=b"",
905
    )
906
907
    if len(system_withdrawal_tx_output.return_data) > 0:
908
        requests_from_execution.append(
909
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
910
        )
911
912
    system_consolidation_tx_output = process_checked_system_transaction(
913
        block_env=block_env,
914
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
915
        data=b"",
916
    )
917
918
    if len(system_consolidation_tx_output.return_data) > 0:
919
        requests_from_execution.append(
920
            CONSOLIDATION_REQUEST_TYPE
921
            + system_consolidation_tx_output.return_data
922
        )

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:
931
    """
932
    Execute a transaction against the provided environment.
933
934
    This function processes the actions needed to execute a transaction.
935
    It decrements the sender's account balance after calculating the gas fee
936
    and refunds them the proper amount after execution. Calling contracts,
937
    deploying code, and incrementing nonces are all examples of actions that
938
    happen within this function or from a call made within this function.
939
940
    Accounts that are marked for deletion are processed and destroyed after
941
    execution.
942
943
    Parameters
944
    ----------
945
    block_env :
946
        Environment for the Ethereum Virtual Machine.
947
    block_output :
948
        The block output for the current block.
949
    tx :
950
        Transaction to execute.
951
    index:
952
        Index of the transaction in the block.
953
954
    """
955
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
956
        index + Uint(1)
957
    )
958
    tx_state = TransactionState(parent=block_env.state)
959
960
    trie_set(
961
        block_output.transactions_trie,
962
        rlp.encode(index),
963
        encode_transaction(tx),
964
    )
965
966
    intrinsic_gas, calldata_floor_gas_cost = validate_transaction(tx)
967
968
    (
969
        sender,
970
        effective_gas_price,
971
        blob_versioned_hashes,
972
        tx_blob_gas_used,
973
    ) = check_transaction(
974
        block_env=block_env,
975
        block_output=block_output,
976
        tx=tx,
977
        tx_state=tx_state,
978
    )
979
980
    sender_account = get_account(tx_state, sender)
981
982
    if isinstance(tx, BlobTransaction):
983
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
984
    else:
985
        blob_gas_fee = Uint(0)
986
987
    effective_gas_fee = tx.gas * effective_gas_price
988
989
    gas = tx.gas - intrinsic_gas
990
991
    increment_nonce(tx_state, sender)
992
993
    sender_balance_after_gas_fee = (
994
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
995
    )
996
    set_account_balance(tx_state, sender, U256(sender_balance_after_gas_fee))
997
998
    access_list_addresses = set()
999
    access_list_storage_keys = set()
1000
    access_list_addresses.add(block_env.coinbase)
1001
    if has_access_list(tx):
1002
        for access in tx.access_list:
1003
            access_list_addresses.add(access.account)
1004
            for slot in access.slots:
1005
                access_list_storage_keys.add((access.account, slot))
1006
1007
    authorizations: Tuple[Authorization, ...] = ()
1008
    if isinstance(tx, SetCodeTransaction):
1009
        authorizations = tx.authorizations
1010
1011
    tx_env = vm.TransactionEnvironment(
1012
        origin=sender,
1013
        gas_price=effective_gas_price,
1014
        gas=gas,
1015
        access_list_addresses=access_list_addresses,
1016
        access_list_storage_keys=access_list_storage_keys,
1017
        state=tx_state,
1018
        blob_versioned_hashes=blob_versioned_hashes,
1019
        authorizations=authorizations,
1020
        index_in_block=index,
1021
        tx_hash=get_transaction_hash(encode_transaction(tx)),
1022
    )
1023
1024
    message = prepare_message(
1025
        block_env,
1026
        tx_env,
1027
        tx,
1028
    )
1029
1030
    tx_output = process_message_call(message)
1031
1032
    # For EIP-7623 we first calculate the execution_gas_used, which includes
1033
    # the execution gas refund.
1034
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
1035
    tx_gas_refund = min(
1036
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
1037
    )
1038
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
1039
1040
    # Transactions with less execution_gas_used than the floor pay at the
1041
    # floor cost.
1042
    tx_gas_used = max(tx_gas_used_after_refund, calldata_floor_gas_cost)
1043
    block_gas_used_in_tx = max(
1044
        tx_gas_used_before_refund, calldata_floor_gas_cost
1045
    )
1046
1047
    tx_gas_left = tx.gas - tx_gas_used
1048
    gas_refund_amount = tx_gas_left * effective_gas_price
1049
1050
    # For non-1559 transactions effective_gas_price == tx.gas_price
1051
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
1052
    transaction_fee = tx_gas_used * priority_fee_per_gas
1053
1054
    # refund gas
1055
    sender_balance_after_refund = get_account(tx_state, sender).balance + U256(
1056
        gas_refund_amount
1057
    )
1058
    set_account_balance(tx_state, sender, sender_balance_after_refund)
1059
1060
    # transfer miner fees
1061
    coinbase_balance_after_mining_fee = get_account(
1062
        tx_state, block_env.coinbase
1063
    ).balance + U256(transaction_fee)
1064
1065
    set_account_balance(
1066
        tx_state, block_env.coinbase, coinbase_balance_after_mining_fee
1067
    )
1068
1069
    # EIP-7708: Emit burn logs for balances held by accounts marked for
1070
    # deletion AFTER miner fee transfer.
1071
    finalization_logs: List[Log] = []
1072
    for address in sorted(tx_output.accounts_to_delete):
1073
        balance = get_account(tx_state, address).balance
1074
        if balance > U256(0):
1075
            padded_address = left_pad_zero_bytes(address, 32)
1076
            finalization_logs.append(
1077
                Log(
1078
                    address=vm.SYSTEM_ADDRESS,
1079
                    topics=(
1080
                        vm.BURN_TOPIC,
1081
                        Hash32(padded_address),
1082
                    ),
1083
                    data=balance.to_be_bytes32(),
1084
                )
1085
            )
1086
1087
    all_logs = tx_output.logs + tuple(finalization_logs)
1088
1089
    if coinbase_balance_after_mining_fee == 0 and account_exists_and_is_empty(
1090
        tx_state, block_env.coinbase
1091
    ):
1092
        destroy_account(tx_state, block_env.coinbase)
1093
1094
    block_output.cumulative_gas_used += tx_gas_used
1095
    block_output.block_gas_used += block_gas_used_in_tx
1096
    block_output.blob_gas_used += tx_blob_gas_used
1097
1098
    receipt = make_receipt(
1099
        tx,
1100
        tx_output.error,
1101
        block_output.cumulative_gas_used,
1102
        all_logs,
1103
    )
1104
1105
    receipt_key = rlp.encode(Uint(index))
1106
    block_output.receipt_keys += (receipt_key,)
1107
1108
    trie_set(
1109
        block_output.receipts_trie,
1110
        receipt_key,
1111
        receipt,
1112
    )
1113
1114
    block_output.block_logs += all_logs
1115
1116
    for address in tx_output.accounts_to_delete:
1117
        destroy_account(tx_state, address)
1118
1119
    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:
1127
    """
1128
    Increase the balance of the withdrawing account.
1129
    """
1130
    wd_state = TransactionState(parent=block_env.state)
1131
1132
    for i, wd in enumerate(withdrawals):
1133
        trie_set(
1134
            block_output.withdrawals_trie,
1135
            rlp.encode(Uint(i)),
1136
            rlp.encode(wd),
1137
        )
1138
1139
        create_ether(wd_state, wd.address, wd.amount * GWEI_TO_WEI)
1140
1141
    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 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 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:
1145
    """
1146
    Validates the gas limit for a block.
1147
1148
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1149
    quotient of the parent block's gas limit and the
1150
    ``LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is passed
1151
    through as a parameter is greater than or equal to the *sum* of the
1152
    parent's gas and the adjustment delta then the limit for gas is too high
1153
    and fails this function's check. Similarly, if the limit is less than or
1154
    equal to the *difference* of the parent's gas and the adjustment delta *or*
1155
    the predefined ``LIMIT_MINIMUM`` then this function's check fails because
1156
    the gas limit doesn't allow for a sufficient or reasonable amount of gas to
1157
    be used on a block.
1158
1159
    Parameters
1160
    ----------
1161
    gas_limit :
1162
        Gas limit to validate.
1163
1164
    parent_gas_limit :
1165
        Gas limit of the parent block.
1166
1167
    Returns
1168
    -------
1169
    check : `bool`
1170
        True if gas limit constraints are satisfied, False otherwise.
1171
1172
    """
1173
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
1174
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1175
        return False
1176
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1177
        return False
1178
    if gas_limit < GasCosts.LIMIT_MINIMUM:
1179
        return False
1180
1181
    return True