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

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

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