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

108
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

109
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

111
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

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

SYSTEM_TRANSACTION_GAS

115
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

116
MAX_BLOB_GAS_PER_BLOCK = GasCosts.BLOB_SCHEDULE_MAX * GasCosts.PER_BLOB

VERSIONED_HASH_VERSION_KZG

117
VERSIONED_HASH_VERSION_KZG = b"\x01"

GWEI_TO_WEI

118
GWEI_TO_WEI = U256(10**9)

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

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

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

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

HISTORY_STORAGE_ADDRESS

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

MAX_BLOCK_SIZE

129
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

130
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

131
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

132
BLOB_COUNT_LIMIT = 6

ChainContext

Chain context needed for block execution.

135
@slotted_freezable
136
@dataclass
class ChainContext:

chain_id

Identify the chain for transaction signature recovery.

142
    chain_id: U64

block_hashes

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

145
    block_hashes: List[Hash32]

parent_header

Parent header used for header validation and system contracts.

148
    parent_header: Header | PreviousHeader

BlockChain

History and current state of the block chain.

152
@dataclass
class BlockChain:

blocks

158
    blocks: List[Block]

state

159
    state: State

chain_id

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

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:
271
    """
272
    Execute a block and validate the resulting roots against the header.
273
274
    This method is idempotent.
275
276
    Parameters
277
    ----------
278
    block :
279
        Block to validate and execute.
280
    pre_state :
281
        Pre-execution state provider.
282
    chain_context :
283
        Chain context that the block may need during execution.
284
285
    Returns
286
    -------
287
    block_diff : `BlockDiff`
288
        Account, storage, and code changes produced by block execution.
289
290
    """
291
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
292
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
293
294
    parent_header = chain_context.parent_header
295
    validate_header(parent_header, block.header)
296
297
    if block.ommers != ():
298
        raise InvalidBlock
299
300
    block_state = BlockState(pre_state=pre_state)
301
302
    block_env = vm.BlockEnvironment(
303
        chain_id=chain_context.chain_id,
304
        state=block_state,
305
        block_gas_limit=block.header.gas_limit,
306
        block_hashes=chain_context.block_hashes,
307
        coinbase=block.header.coinbase,
308
        number=block.header.number,
309
        base_fee_per_gas=block.header.base_fee_per_gas,
310
        time=block.header.timestamp,
311
        prev_randao=block.header.prev_randao,
312
        excess_blob_gas=block.header.excess_blob_gas,
313
        parent_beacon_block_root=block.header.parent_beacon_block_root,
314
        block_access_list_builder=BlockAccessListBuilder(),
315
    )
316
317
    block_output = apply_body(
318
        block_env=block_env,
319
        transactions=block.transactions,
320
        withdrawals=block.withdrawals,
321
    )
322
    block_diff = extract_block_diff(block_state)
323
    block_state_root, _ = pre_state.compute_state_root_and_trie_changes(
324
        block_diff.account_changes, block_diff.storage_changes
325
    )
326
    transactions_root = root(block_output.transactions_trie)
327
    receipt_root = root(block_output.receipts_trie)
328
    block_logs_bloom = logs_bloom(block_output.block_logs)
329
    withdrawals_root = root(block_output.withdrawals_trie)
330
    requests_hash = compute_requests_hash(block_output.requests)
331
    computed_block_access_list_hash = hash_block_access_list(
332
        block_output.block_access_list
333
    )
334
335
    if block_output.block_gas_used != block.header.gas_used:
336
        raise InvalidBlock(
337
            f"{block_output.block_gas_used} != {block.header.gas_used}"
338
        )
339
    if transactions_root != block.header.transactions_root:
340
        raise InvalidBlock
341
    if block_state_root != block.header.state_root:
342
        raise InvalidBlock
343
    if receipt_root != block.header.receipt_root:
344
        raise InvalidBlock
345
    if block_logs_bloom != block.header.bloom:
346
        raise InvalidBlock
347
    if withdrawals_root != block.header.withdrawals_root:
348
        raise InvalidBlock
349
    if block_output.blob_gas_used != block.header.blob_gas_used:
350
        raise InvalidBlock
351
    if requests_hash != block.header.requests_hash:
352
        raise InvalidBlock
353
    if computed_block_access_list_hash != block.header.block_access_list_hash:
354
        raise InvalidBlock("Invalid block access list hash")
355
356
    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:
365
    """
366
    Calculates the base fee per gas for the block.
367
368
    Parameters
369
    ----------
370
    block_gas_limit :
371
        Gas limit of the block for which the base fee is being calculated.
372
    parent_gas_limit :
373
        Gas limit of the parent block.
374
    parent_gas_used :
375
        Gas used in the parent block.
376
    parent_base_fee_per_gas :
377
        Base fee per gas of the parent block.
378
379
    Returns
380
    -------
381
    base_fee_per_gas : `Uint`
382
        Base fee per gas for the block.
383
384
    """
385
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
386
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
387
        raise InvalidBlock
388
389
    if parent_gas_used == parent_gas_target:
390
        expected_base_fee_per_gas = parent_base_fee_per_gas
391
    elif parent_gas_used > parent_gas_target:
392
        gas_used_delta = parent_gas_used - parent_gas_target
393
394
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
395
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
396
397
        base_fee_per_gas_delta = max(
398
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
399
            Uint(1),
400
        )
401
402
        expected_base_fee_per_gas = (
403
            parent_base_fee_per_gas + base_fee_per_gas_delta
404
        )
405
    else:
406
        gas_used_delta = parent_gas_target - parent_gas_used
407
408
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
409
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
410
411
        base_fee_per_gas_delta = (
412
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
413
        )
414
415
        expected_base_fee_per_gas = (
416
            parent_base_fee_per_gas - base_fee_per_gas_delta
417
        )
418
419
    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:
425
    """
426
    Verify a block header against its parent.
427
428
    In order to consider a block's header valid, the logic for the
429
    quantities in the header should match the logic for the block itself.
430
    For example the header timestamp should be greater than the block's parent
431
    timestamp because the block was created *after* the parent block.
432
    Additionally, the block's number should be directly following the parent
433
    block's number since it is the next block in the sequence.
434
435
    Parameters
436
    ----------
437
    parent_header :
438
        Header of the parent block.
439
    header :
440
        Header to check for correctness.
441
442
    """
443
    if header.number < Uint(1):
444
        raise InvalidBlock
445
446
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
447
    if header.excess_blob_gas != excess_blob_gas:
448
        raise InvalidBlock
449
450
    if header.gas_used > header.gas_limit:
451
        raise InvalidBlock
452
453
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
454
        header.gas_limit,
455
        parent_header.gas_limit,
456
        parent_header.gas_used,
457
        parent_header.base_fee_per_gas,
458
    )
459
    if expected_base_fee_per_gas != header.base_fee_per_gas:
460
        raise InvalidBlock
461
    if header.timestamp <= parent_header.timestamp:
462
        raise InvalidBlock
463
    if header.number != parent_header.number + Uint(1):
464
        raise InvalidBlock
465
    if len(header.extra_data) > 32:
466
        raise InvalidBlock
467
    if header.difficulty != 0:
468
        raise InvalidBlock
469
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
470
        raise InvalidBlock
471
    if header.ommers_hash != EMPTY_OMMER_HASH:
472
        raise InvalidBlock
473
474
    block_parent_hash = keccak256(rlp.encode(parent_header))
475
    if header.parent_hash != block_parent_hash:
476
        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]:
485
    """
486
    Check if the transaction is includable in the block.
487
488
    Parameters
489
    ----------
490
    block_env :
491
        The block scoped environment.
492
    block_output :
493
        The block output for the current block.
494
    tx :
495
        The transaction.
496
    tx_state :
497
        The transaction state tracker.
498
499
    Returns
500
    -------
501
    sender_address :
502
        The sender of the transaction.
503
    effective_gas_price :
504
        The price to charge for gas when the transaction is executed.
505
    blob_versioned_hashes :
506
        The blob versioned hashes of the transaction.
507
    tx_blob_gas_used:
508
        The blob gas used by the transaction.
509
510
    Raises
511
    ------
512
    InvalidBlock :
513
        If the transaction is not includable.
514
    GasUsedExceedsLimitError :
515
        If the gas used by the transaction exceeds the block's gas limit.
516
    NonceMismatchError :
517
        If the nonce of the transaction is not equal to the sender's nonce.
518
    InsufficientBalanceError :
519
        If the sender's balance is not enough to pay for the transaction.
520
    InvalidSenderError :
521
        If the transaction is from an address that does not exist anymore.
522
    PriorityFeeGreaterThanMaxFeeError :
523
        If the priority fee is greater than the maximum fee per gas.
524
    InsufficientMaxFeePerGasError :
525
        If the maximum fee per gas is insufficient for the transaction.
526
    InsufficientMaxFeePerBlobGasError :
527
        If the maximum fee per blob gas is insufficient for the transaction.
528
    BlobGasLimitExceededError :
529
        If the blob gas used by the transaction exceeds the block's blob gas
530
        limit.
531
    InvalidBlobVersionedHashError :
532
        If the transaction contains a blob versioned hash with an invalid
533
        version.
534
    NoBlobDataError :
535
        If the transaction is a type 3 but has no blobs.
536
    BlobCountExceededError :
537
        If the transaction is a type 3 and has more blobs than the limit.
538
    TransactionTypeContractCreationError:
539
        If the transaction type is not allowed to create contracts.
540
    EmptyAuthorizationListError :
541
        If the transaction is a SetCodeTransaction and the authorization list
542
        is empty.
543
544
    """
545
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
546
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
547
548
    if tx.gas > gas_available:
549
        raise GasUsedExceedsLimitError("gas used exceeds limit")
550
551
    tx_blob_gas_used = calculate_total_blob_gas(tx)
552
    if tx_blob_gas_used > blob_gas_available:
553
        raise BlobGasLimitExceededError("blob gas limit exceeded")
554
555
    sender_address = recover_sender(block_env.chain_id, tx)
556
    sender_account = get_account(tx_state, sender_address)
557
558
    if isinstance(
559
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
560
    ):
561
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
562
            raise PriorityFeeGreaterThanMaxFeeError(
563
                "priority fee greater than max fee"
564
            )
565
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
566
            raise InsufficientMaxFeePerGasError(
567
                tx.max_fee_per_gas, block_env.base_fee_per_gas
568
            )
569
570
        priority_fee_per_gas = min(
571
            tx.max_priority_fee_per_gas,
572
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
573
        )
574
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
575
        max_gas_fee = tx.gas * tx.max_fee_per_gas
576
    else:
577
        if tx.gas_price < block_env.base_fee_per_gas:
578
            raise InvalidBlock
579
        effective_gas_price = tx.gas_price
580
        max_gas_fee = tx.gas * tx.gas_price
581
582
    if isinstance(tx, BlobTransaction):
583
        blob_count = len(tx.blob_versioned_hashes)
584
        if blob_count == 0:
585
            raise NoBlobDataError("no blob data in transaction")
586
        if blob_count > BLOB_COUNT_LIMIT:
587
            raise BlobCountExceededError(
588
                f"Tx has {blob_count} blobs. Max allowed: {BLOB_COUNT_LIMIT}"
589
            )
590
        for blob_versioned_hash in tx.blob_versioned_hashes:
591
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
592
                raise InvalidBlobVersionedHashError(
593
                    "invalid blob versioned hash"
594
                )
595
596
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
597
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
598
            raise InsufficientMaxFeePerBlobGasError(
599
                "insufficient max fee per blob gas"
600
            )
601
602
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
603
            tx.max_fee_per_blob_gas
604
        )
605
        blob_versioned_hashes = tx.blob_versioned_hashes
606
    else:
607
        blob_versioned_hashes = ()
608
609
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
610
        if not isinstance(tx.to, Address):
611
            raise TransactionTypeContractCreationError(tx)
612
613
    if isinstance(tx, SetCodeTransaction):
614
        if not any(tx.authorizations):
615
            raise EmptyAuthorizationListError("empty authorization list")
616
617
    if sender_account.nonce > Uint(tx.nonce):
618
        raise NonceMismatchError("nonce too low")
619
    elif sender_account.nonce < Uint(tx.nonce):
620
        raise NonceMismatchError("nonce too high")
621
622
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
623
        raise InsufficientBalanceError("insufficient sender balance")
624
    sender_code = get_code(tx_state, sender_account.code_hash)
625
    if sender_account.code_hash != EMPTY_CODE_HASH and not is_valid_delegation(
626
        sender_code
627
    ):
628
        raise InvalidSenderError("not EOA")
629
630
    return (
631
        sender_address,
632
        effective_gas_price,
633
        blob_versioned_hashes,
634
        tx_blob_gas_used,
635
    )

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:
644
    """
645
    Make the receipt for a transaction that was executed.
646
647
    Parameters
648
    ----------
649
    tx :
650
        The executed transaction.
651
    error :
652
        Error in the top level frame of the transaction, if any.
653
    cumulative_gas_used :
654
        The total gas used so far in the block after the transaction was
655
        executed.
656
    logs :
657
        The logs produced by the transaction.
658
659
    Returns
660
    -------
661
    receipt :
662
        The receipt for the transaction.
663
664
    """
665
    receipt = Receipt(
666
        succeeded=error is None,
667
        cumulative_gas_used=cumulative_gas_used,
668
        bloom=logs_bloom(logs),
669
        logs=logs,
670
    )
671
672
    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:
680
    """
681
    Process a system transaction and raise an error if the contract does not
682
    contain code or if the transaction fails.
683
684
    Parameters
685
    ----------
686
    block_env :
687
        The block scoped environment.
688
    target_address :
689
        Address of the contract to call.
690
    data :
691
        Data to pass to the contract.
692
693
    Returns
694
    -------
695
    system_tx_output : `MessageCallOutput`
696
        Output of processing the system transaction.
697
698
    """
699
    # Read through BlockState (not pre-state) so that a system contract
700
    # deployed by an earlier transaction in the same block is visible.
701
    # See EIP-7002 and EIP-7251 for this edge case.
702
    #
703
    # This read is not recorded in the state tracker.
704
    # However, this is fine because `process_unchecked_system_transaction`
705
    # does its own get_account on the TransactionState that we do incorporate
706
    # into BlockState.
707
    untracked_state = TransactionState(parent=block_env.state)
708
    system_contract_code = get_code(
709
        untracked_state,
710
        get_account(untracked_state, target_address).code_hash,
711
    )
712
713
    if len(system_contract_code) == 0:
714
        raise InvalidBlock(
715
            f"System contract address {target_address.hex()} does not "
716
            "contain code"
717
        )
718
719
    system_tx_output = process_unchecked_system_transaction(
720
        block_env,
721
        target_address,
722
        data,
723
    )
724
725
    if system_tx_output.error:
726
        raise InvalidBlock(
727
            f"System contract ({target_address.hex()}) call failed: "
728
            f"{system_tx_output.error}"
729
        )
730
731
    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:
739
    """
740
    Process a system transaction without checking if the contract contains
741
    code or if the transaction fails.
742
743
    Parameters
744
    ----------
745
    block_env :
746
        The block scoped environment.
747
    target_address :
748
        Address of the contract to call.
749
    data :
750
        Data to pass to the contract.
751
752
    Returns
753
    -------
754
    system_tx_output : `MessageCallOutput`
755
        Output of processing the system transaction.
756
757
    """
758
    system_tx_state = TransactionState(parent=block_env.state)
759
    system_contract_code = get_code(
760
        system_tx_state,
761
        get_account(system_tx_state, target_address).code_hash,
762
    )
763
764
    tx_env = vm.TransactionEnvironment(
765
        origin=SYSTEM_ADDRESS,
766
        gas_price=block_env.base_fee_per_gas,
767
        gas=SYSTEM_TRANSACTION_GAS,
768
        access_list_addresses=set(),
769
        access_list_storage_keys=set(),
770
        state=system_tx_state,
771
        blob_versioned_hashes=(),
772
        authorizations=(),
773
        index_in_block=None,
774
        tx_hash=None,
775
    )
776
777
    system_tx_message = Message(
778
        block_env=block_env,
779
        tx_env=tx_env,
780
        caller=SYSTEM_ADDRESS,
781
        target=target_address,
782
        gas=SYSTEM_TRANSACTION_GAS,
783
        value=U256(0),
784
        data=data,
785
        code=system_contract_code,
786
        depth=Uint(0),
787
        current_target=target_address,
788
        code_address=target_address,
789
        should_transfer_value=False,
790
        is_static=False,
791
        accessed_addresses=set(),
792
        accessed_storage_keys=set(),
793
        disable_precompiles=False,
794
        parent_evm=None,
795
    )
796
797
    system_tx_output = process_message_call(system_tx_message)
798
799
    incorporate_tx_into_block(
800
        system_tx_state, block_env.block_access_list_builder
801
    )
802
803
    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:
811
    """
812
    Executes a block.
813
814
    Many of the contents of a block are stored in data structures called
815
    tries. There is a transactions trie which is similar to a ledger of the
816
    transactions stored in the current block. There is also a receipts trie
817
    which stores the results of executing a transaction, like the post state
818
    and gas used. This function creates and executes the block that is to be
819
    added to the chain.
820
821
    Parameters
822
    ----------
823
    block_env :
824
        The block scoped environment.
825
    transactions :
826
        Transactions included in the block.
827
    withdrawals :
828
        Withdrawals to be processed in the current block.
829
830
    Returns
831
    -------
832
    block_output :
833
        The block output for the current block.
834
835
    """
836
    block_output = vm.BlockOutput()
837
838
    process_unchecked_system_transaction(
839
        block_env=block_env,
840
        target_address=BEACON_ROOTS_ADDRESS,
841
        data=block_env.parent_beacon_block_root,
842
    )
843
844
    process_unchecked_system_transaction(
845
        block_env=block_env,
846
        target_address=HISTORY_STORAGE_ADDRESS,
847
        data=block_env.block_hashes[-1],  # The parent hash
848
    )
849
850
    for i, tx in enumerate(map(decode_transaction, transactions)):
851
        process_transaction(block_env, block_output, tx, Uint(i))
852
853
    # EIP-7928: Post-execution operations use index N+1
854
    block_env.block_access_list_builder.block_access_index = BlockAccessIndex(
855
        ulen(transactions) + Uint(1)
856
    )
857
858
    process_withdrawals(block_env, block_output, withdrawals)
859
860
    process_general_purpose_requests(
861
        block_env=block_env,
862
        block_output=block_output,
863
    )
864
865
    block_output.block_access_list = build_block_access_list(
866
        block_env.block_access_list_builder, block_env.state
867
    )
868
869
    # Validate block access list gas limit constraint (EIP-7928)
870
    validate_block_access_list_gas_limit(
871
        block_access_list=block_output.block_access_list,
872
        block_gas_limit=block_env.block_gas_limit,
873
    )
874
875
    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:
882
    """
883
    Process all the requests in the block.
884
885
    Parameters
886
    ----------
887
    block_env :
888
        The execution environment for the Block.
889
    block_output :
890
        The block output for the current block.
891
892
    """
893
    # Requests are to be in ascending order of request type
894
    deposit_requests = parse_deposit_requests(block_output)
895
    requests_from_execution = block_output.requests
896
    if len(deposit_requests) > 0:
897
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
898
899
    system_withdrawal_tx_output = process_checked_system_transaction(
900
        block_env=block_env,
901
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
902
        data=b"",
903
    )
904
905
    if len(system_withdrawal_tx_output.return_data) > 0:
906
        requests_from_execution.append(
907
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
908
        )
909
910
    system_consolidation_tx_output = process_checked_system_transaction(
911
        block_env=block_env,
912
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
913
        data=b"",
914
    )
915
916
    if len(system_consolidation_tx_output.return_data) > 0:
917
        requests_from_execution.append(
918
            CONSOLIDATION_REQUEST_TYPE
919
            + system_consolidation_tx_output.return_data
920
        )

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