ethereum.forks.osaka.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

91
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

92
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

93
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

94
GAS_LIMIT_MINIMUM = Uint(5000)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

96
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

97
BEACON_ROOTS_ADDRESS = hex_to_address(
98
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
99
)

SYSTEM_TRANSACTION_GAS

100
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

101
MAX_BLOB_GAS_PER_BLOCK = BLOB_SCHEDULE_MAX * GAS_PER_BLOB

VERSIONED_HASH_VERSION_KZG

102
VERSIONED_HASH_VERSION_KZG = b"\x01"

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

104
WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
105
    "0x00000961Ef480Eb55e80D19ad83579A64c007002"
106
)

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

107
CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS = hex_to_address(
108
    "0x0000BBdDc7CE488642fb579F8B00f3a590007251"
109
)

HISTORY_STORAGE_ADDRESS

110
HISTORY_STORAGE_ADDRESS = hex_to_address(
111
    "0x0000F90827F1C53a10cb7A02335B175320002935"
112
)

MAX_BLOCK_SIZE

113
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

114
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

115
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

116
BLOB_COUNT_LIMIT = 6

BlockChain

History and current state of the block chain.

119
@dataclass
class BlockChain:

blocks

125
    blocks: List[Block]

state

126
    state: State

chain_id

127
    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:
131
    """
132
    Transforms the state from the previous hard fork (`old`) into the block
133
    chain object for this hard fork and returns it.
134
135
    When forks need to implement an irregular state transition, this function
136
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
137
    an example.
138
139
    Parameters
140
    ----------
141
    old :
142
        Previous block chain object.
143
144
    Returns
145
    -------
146
    new : `BlockChain`
147
        Upgraded block chain object for this hard fork.
148
149
    """
150
    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]:
154
    """
155
    Obtain the list of hashes of the previous 256 blocks in order of
156
    increasing block number.
157
158
    This function will return less hashes for the first 256 blocks.
159
160
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
161
    therefore this function retrieves them.
162
163
    Parameters
164
    ----------
165
    chain :
166
        History and current state.
167
168
    Returns
169
    -------
170
    recent_block_hashes : `List[Hash32]`
171
        Hashes of the recent 256 blocks in order of increasing block number.
172
173
    """
174
    recent_blocks = chain.blocks[-255:]
175
    # TODO: This function has not been tested rigorously
176
    if len(recent_blocks) == 0:
177
        return []
178
179
    recent_block_hashes = []
180
181
    for block in recent_blocks:
182
        prev_block_hash = block.header.parent_hash
183
        recent_block_hashes.append(prev_block_hash)
184
185
    # We are computing the hash only for the most recent block and not for
186
    # the rest of the blocks as they have successors which have the hash of
187
    # the current block as parent hash.
188
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
189
    recent_block_hashes.append(most_recent_block_hash)
190
191
    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:
195
    """
196
    Attempts to apply a block to an existing block chain.
197
198
    All parts of the block's contents need to be verified before being added
199
    to the chain. Blocks are verified by ensuring that the contents of the
200
    block make logical sense with the contents of the parent block. The
201
    information in the block's header must also match the corresponding
202
    information in the block.
203
204
    To implement Ethereum, in theory clients are only required to store the
205
    most recent 255 blocks of the chain since as far as execution is
206
    concerned, only those blocks are accessed. Practically, however, clients
207
    should store more blocks to handle reorgs.
208
209
    Parameters
210
    ----------
211
    chain :
212
        History and current state.
213
    block :
214
        Block to apply to `chain`.
215
216
    """
217
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
218
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
219
220
    validate_header(chain, block.header)
221
    if block.ommers != ():
222
        raise InvalidBlock
223
224
    block_env = vm.BlockEnvironment(
225
        chain_id=chain.chain_id,
226
        state=chain.state,
227
        block_gas_limit=block.header.gas_limit,
228
        block_hashes=get_last_256_block_hashes(chain),
229
        coinbase=block.header.coinbase,
230
        number=block.header.number,
231
        base_fee_per_gas=block.header.base_fee_per_gas,
232
        time=block.header.timestamp,
233
        prev_randao=block.header.prev_randao,
234
        excess_blob_gas=block.header.excess_blob_gas,
235
        parent_beacon_block_root=block.header.parent_beacon_block_root,
236
    )
237
238
    block_output = apply_body(
239
        block_env=block_env,
240
        transactions=block.transactions,
241
        withdrawals=block.withdrawals,
242
    )
243
    block_state_root = state_root(block_env.state)
244
    transactions_root = root(block_output.transactions_trie)
245
    receipt_root = root(block_output.receipts_trie)
246
    block_logs_bloom = logs_bloom(block_output.block_logs)
247
    withdrawals_root = root(block_output.withdrawals_trie)
248
    requests_hash = compute_requests_hash(block_output.requests)
249
250
    if block_output.block_gas_used != block.header.gas_used:
251
        raise InvalidBlock(
252
            f"{block_output.block_gas_used} != {block.header.gas_used}"
253
        )
254
    if transactions_root != block.header.transactions_root:
255
        raise InvalidBlock
256
    if block_state_root != block.header.state_root:
257
        raise InvalidBlock
258
    if receipt_root != block.header.receipt_root:
259
        raise InvalidBlock
260
    if block_logs_bloom != block.header.bloom:
261
        raise InvalidBlock
262
    if withdrawals_root != block.header.withdrawals_root:
263
        raise InvalidBlock
264
    if block_output.blob_gas_used != block.header.blob_gas_used:
265
        raise InvalidBlock
266
    if requests_hash != block.header.requests_hash:
267
        raise InvalidBlock
268
269
    chain.blocks.append(block)
270
    if len(chain.blocks) > 255:
271
        # Real clients have to store more blocks to deal with reorgs, but the
272
        # protocol only requires the last 255
273
        chain.blocks = chain.blocks[-255:]

calculate_base_fee_per_gas

Calculates the base fee per gas for the block.

Parameters

block_gas_limit : Gas limit of the block for which the base fee is being calculated. parent_gas_limit : Gas limit of the parent block. parent_gas_used : Gas used in the parent block. parent_base_fee_per_gas : Base fee per gas of the parent block.

Returns

base_fee_per_gas : Uint Base fee per gas for the block.

def calculate_base_fee_per_gas(block_gas_limit: Uint, ​​parent_gas_limit: Uint, ​​parent_gas_used: Uint, ​​parent_base_fee_per_gas: Uint) -> Uint:
282
    """
283
    Calculates the base fee per gas for the block.
284
285
    Parameters
286
    ----------
287
    block_gas_limit :
288
        Gas limit of the block for which the base fee is being calculated.
289
    parent_gas_limit :
290
        Gas limit of the parent block.
291
    parent_gas_used :
292
        Gas used in the parent block.
293
    parent_base_fee_per_gas :
294
        Base fee per gas of the parent block.
295
296
    Returns
297
    -------
298
    base_fee_per_gas : `Uint`
299
        Base fee per gas for the block.
300
301
    """
302
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
303
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
304
        raise InvalidBlock
305
306
    if parent_gas_used == parent_gas_target:
307
        expected_base_fee_per_gas = parent_base_fee_per_gas
308
    elif parent_gas_used > parent_gas_target:
309
        gas_used_delta = parent_gas_used - parent_gas_target
310
311
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
312
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
313
314
        base_fee_per_gas_delta = max(
315
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
316
            Uint(1),
317
        )
318
319
        expected_base_fee_per_gas = (
320
            parent_base_fee_per_gas + base_fee_per_gas_delta
321
        )
322
    else:
323
        gas_used_delta = parent_gas_target - parent_gas_used
324
325
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
326
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
327
328
        base_fee_per_gas_delta = (
329
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
330
        )
331
332
        expected_base_fee_per_gas = (
333
            parent_base_fee_per_gas - base_fee_per_gas_delta
334
        )
335
336
    return Uint(expected_base_fee_per_gas)

validate_header

Verifies a block header.

In order to consider a block's header valid, the logic for the quantities in the header should match the logic for the block itself. For example the header timestamp should be greater than the block's parent timestamp because the block was created after the parent block. Additionally, the block's number should be directly following the parent block's number since it is the next block in the sequence.

Parameters

chain : History and current state. header : Header to check for correctness.

def validate_header(chain: BlockChain, ​​header: Header) -> None:
340
    """
341
    Verifies a block header.
342
343
    In order to consider a block's header valid, the logic for the
344
    quantities in the header should match the logic for the block itself.
345
    For example the header timestamp should be greater than the block's parent
346
    timestamp because the block was created *after* the parent block.
347
    Additionally, the block's number should be directly following the parent
348
    block's number since it is the next block in the sequence.
349
350
    Parameters
351
    ----------
352
    chain :
353
        History and current state.
354
    header :
355
        Header to check for correctness.
356
357
    """
358
    if header.number < Uint(1):
359
        raise InvalidBlock
360
361
    parent_header = chain.blocks[-1].header
362
363
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
364
    if header.excess_blob_gas != excess_blob_gas:
365
        raise InvalidBlock
366
367
    if header.gas_used > header.gas_limit:
368
        raise InvalidBlock
369
370
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
371
        header.gas_limit,
372
        parent_header.gas_limit,
373
        parent_header.gas_used,
374
        parent_header.base_fee_per_gas,
375
    )
376
    if expected_base_fee_per_gas != header.base_fee_per_gas:
377
        raise InvalidBlock
378
    if header.timestamp <= parent_header.timestamp:
379
        raise InvalidBlock
380
    if header.number != parent_header.number + Uint(1):
381
        raise InvalidBlock
382
    if len(header.extra_data) > 32:
383
        raise InvalidBlock
384
    if header.difficulty != 0:
385
        raise InvalidBlock
386
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
387
        raise InvalidBlock
388
    if header.ommers_hash != EMPTY_OMMER_HASH:
389
        raise InvalidBlock
390
391
    block_parent_hash = keccak256(rlp.encode(parent_header))
392
    if header.parent_hash != block_parent_hash:
393
        raise InvalidBlock

check_transaction

Check if the transaction is includable in the block.

Parameters

block_env : The block scoped environment. block_output : The block output for the current block. tx : The transaction.

Returns

sender_address : The sender of the transaction. effective_gas_price : The price to charge for gas when the transaction is executed. blob_versioned_hashes : The blob versioned hashes of the transaction. tx_blob_gas_used: The blob gas used by the transaction.

Raises

InvalidBlock : If the transaction is not includable. GasUsedExceedsLimitError : If the gas used by the transaction exceeds the block's gas limit. NonceMismatchError : If the nonce of the transaction is not equal to the sender's nonce. InsufficientBalanceError : If the sender's balance is not enough to pay for the transaction. InvalidSenderError : If the transaction is from an address that does not exist anymore. PriorityFeeGreaterThanMaxFeeError : If the priority fee is greater than the maximum fee per gas. InsufficientMaxFeePerGasError : If the maximum fee per gas is insufficient for the transaction. InsufficientMaxFeePerBlobGasError : If the maximum fee per blob gas is insufficient for the transaction. BlobGasLimitExceededError : If the blob gas used by the transaction exceeds the block's blob gas limit. InvalidBlobVersionedHashError : If the transaction contains a blob versioned hash with an invalid version. NoBlobDataError : If the transaction is a type 3 but has no blobs. BlobCountExceededError : If the transaction is a type 3 and has more blobs than the limit. TransactionTypeContractCreationError: If the transaction type is not allowed to create contracts. EmptyAuthorizationListError : If the transaction is a SetCodeTransaction and the authorization list is empty.

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

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:
555
    """
556
    Make the receipt for a transaction that was executed.
557
558
    Parameters
559
    ----------
560
    tx :
561
        The executed transaction.
562
    error :
563
        Error in the top level frame of the transaction, if any.
564
    cumulative_gas_used :
565
        The total gas used so far in the block after the transaction was
566
        executed.
567
    logs :
568
        The logs produced by the transaction.
569
570
    Returns
571
    -------
572
    receipt :
573
        The receipt for the transaction.
574
575
    """
576
    receipt = Receipt(
577
        succeeded=error is None,
578
        cumulative_gas_used=cumulative_gas_used,
579
        bloom=logs_bloom(logs),
580
        logs=logs,
581
    )
582
583
    return encode_receipt(tx, receipt)

process_system_transaction

Process a system transaction with the given code.

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

Parameters

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

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

def process_system_transaction(block_env: ethereum.forks.osaka.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
592
    """
593
    Process a system transaction with the given code.
594
595
    Prefer calling `process_checked_system_transaction` or
596
    `process_unchecked_system_transaction` depending on whether missing code or
597
    an execution error should cause the block to be rejected.
598
599
    Parameters
600
    ----------
601
    block_env :
602
        The block scoped environment.
603
    target_address :
604
        Address of the contract to call.
605
    system_contract_code :
606
        Code of the contract to call.
607
    data :
608
        Data to pass to the contract.
609
610
    Returns
611
    -------
612
    system_tx_output : `MessageCallOutput`
613
        Output of processing the system transaction.
614
615
    """
616
    tx_env = vm.TransactionEnvironment(
617
        origin=SYSTEM_ADDRESS,
618
        gas_price=block_env.base_fee_per_gas,
619
        gas=SYSTEM_TRANSACTION_GAS,
620
        access_list_addresses=set(),
621
        access_list_storage_keys=set(),
622
        transient_storage=TransientStorage(),
623
        blob_versioned_hashes=(),
624
        authorizations=(),
625
        index_in_block=None,
626
        tx_hash=None,
627
    )
628
629
    system_tx_message = Message(
630
        block_env=block_env,
631
        tx_env=tx_env,
632
        caller=SYSTEM_ADDRESS,
633
        target=target_address,
634
        gas=SYSTEM_TRANSACTION_GAS,
635
        value=U256(0),
636
        data=data,
637
        code=system_contract_code,
638
        depth=Uint(0),
639
        current_target=target_address,
640
        code_address=target_address,
641
        should_transfer_value=False,
642
        is_static=False,
643
        accessed_addresses=set(),
644
        accessed_storage_keys=set(),
645
        disable_precompiles=False,
646
        parent_evm=None,
647
    )
648
649
    system_tx_output = process_message_call(system_tx_message)
650
651
    return system_tx_output

process_checked_system_transaction

Process a system transaction and raise an error if the contract does not contain code or if the transaction fails.

Parameters

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

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

def process_checked_system_transaction(block_env: ethereum.forks.osaka.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
659
    """
660
    Process a system transaction and raise an error if the contract does not
661
    contain code or if the transaction fails.
662
663
    Parameters
664
    ----------
665
    block_env :
666
        The block scoped environment.
667
    target_address :
668
        Address of the contract to call.
669
    data :
670
        Data to pass to the contract.
671
672
    Returns
673
    -------
674
    system_tx_output : `MessageCallOutput`
675
        Output of processing the system transaction.
676
677
    """
678
    system_contract_code = get_account(block_env.state, target_address).code
679
680
    if len(system_contract_code) == 0:
681
        raise InvalidBlock(
682
            f"System contract address {target_address.hex()} does not "
683
            "contain code"
684
        )
685
686
    system_tx_output = process_system_transaction(
687
        block_env,
688
        target_address,
689
        system_contract_code,
690
        data,
691
    )
692
693
    if system_tx_output.error:
694
        raise InvalidBlock(
695
            f"System contract ({target_address.hex()}) call failed: "
696
            f"{system_tx_output.error}"
697
        )
698
699
    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.osaka.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
707
    """
708
    Process a system transaction without checking if the contract contains code
709
    or if the transaction fails.
710
711
    Parameters
712
    ----------
713
    block_env :
714
        The block scoped environment.
715
    target_address :
716
        Address of the contract to call.
717
    data :
718
        Data to pass to the contract.
719
720
    Returns
721
    -------
722
    system_tx_output : `MessageCallOutput`
723
        Output of processing the system transaction.
724
725
    """
726
    system_contract_code = get_account(block_env.state, target_address).code
727
    return process_system_transaction(
728
        block_env,
729
        target_address,
730
        system_contract_code,
731
        data,
732
    )

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.osaka.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.forks.osaka.vm.BlockOutput:
740
    """
741
    Executes a block.
742
743
    Many of the contents of a block are stored in data structures called
744
    tries. There is a transactions trie which is similar to a ledger of the
745
    transactions stored in the current block. There is also a receipts trie
746
    which stores the results of executing a transaction, like the post state
747
    and gas used. This function creates and executes the block that is to be
748
    added to the chain.
749
750
    Parameters
751
    ----------
752
    block_env :
753
        The block scoped environment.
754
    transactions :
755
        Transactions included in the block.
756
    withdrawals :
757
        Withdrawals to be processed in the current block.
758
759
    Returns
760
    -------
761
    block_output :
762
        The block output for the current block.
763
764
    """
765
    block_output = vm.BlockOutput()
766
767
    process_unchecked_system_transaction(
768
        block_env=block_env,
769
        target_address=BEACON_ROOTS_ADDRESS,
770
        data=block_env.parent_beacon_block_root,
771
    )
772
773
    process_unchecked_system_transaction(
774
        block_env=block_env,
775
        target_address=HISTORY_STORAGE_ADDRESS,
776
        data=block_env.block_hashes[-1],  # The parent hash
777
    )
778
779
    for i, tx in enumerate(map(decode_transaction, transactions)):
780
        process_transaction(block_env, block_output, tx, Uint(i))
781
782
    process_withdrawals(block_env, block_output, withdrawals)
783
784
    process_general_purpose_requests(
785
        block_env=block_env,
786
        block_output=block_output,
787
    )
788
789
    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.osaka.vm.BlockEnvironment, ​​block_output: ethereum.forks.osaka.vm.BlockOutput) -> None:
796
    """
797
    Process all the requests in the block.
798
799
    Parameters
800
    ----------
801
    block_env :
802
        The execution environment for the Block.
803
    block_output :
804
        The block output for the current block.
805
806
    """
807
    # Requests are to be in ascending order of request type
808
    deposit_requests = parse_deposit_requests(block_output)
809
    requests_from_execution = block_output.requests
810
    if len(deposit_requests) > 0:
811
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
812
813
    system_withdrawal_tx_output = process_checked_system_transaction(
814
        block_env=block_env,
815
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
816
        data=b"",
817
    )
818
819
    if len(system_withdrawal_tx_output.return_data) > 0:
820
        requests_from_execution.append(
821
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
822
        )
823
824
    system_consolidation_tx_output = process_checked_system_transaction(
825
        block_env=block_env,
826
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
827
        data=b"",
828
    )
829
830
    if len(system_consolidation_tx_output.return_data) > 0:
831
        requests_from_execution.append(
832
            CONSOLIDATION_REQUEST_TYPE
833
            + system_consolidation_tx_output.return_data
834
        )

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.osaka.vm.BlockEnvironment, ​​block_output: ethereum.forks.osaka.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
843
    """
844
    Execute a transaction against the provided environment.
845
846
    This function processes the actions needed to execute a transaction.
847
    It decrements the sender's account balance after calculating the gas fee
848
    and refunds them the proper amount after execution. Calling contracts,
849
    deploying code, and incrementing nonces are all examples of actions that
850
    happen within this function or from a call made within this function.
851
852
    Accounts that are marked for deletion are processed and destroyed after
853
    execution.
854
855
    Parameters
856
    ----------
857
    block_env :
858
        Environment for the Ethereum Virtual Machine.
859
    block_output :
860
        The block output for the current block.
861
    tx :
862
        Transaction to execute.
863
    index:
864
        Index of the transaction in the block.
865
866
    """
867
    trie_set(
868
        block_output.transactions_trie,
869
        rlp.encode(index),
870
        encode_transaction(tx),
871
    )
872
873
    intrinsic_gas, calldata_floor_gas_cost = validate_transaction(tx)
874
875
    (
876
        sender,
877
        effective_gas_price,
878
        blob_versioned_hashes,
879
        tx_blob_gas_used,
880
    ) = check_transaction(
881
        block_env=block_env,
882
        block_output=block_output,
883
        tx=tx,
884
    )
885
886
    sender_account = get_account(block_env.state, sender)
887
888
    if isinstance(tx, BlobTransaction):
889
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
890
    else:
891
        blob_gas_fee = Uint(0)
892
893
    effective_gas_fee = tx.gas * effective_gas_price
894
895
    gas = tx.gas - intrinsic_gas
896
    increment_nonce(block_env.state, sender)
897
898
    sender_balance_after_gas_fee = (
899
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
900
    )
901
    set_account_balance(
902
        block_env.state, sender, U256(sender_balance_after_gas_fee)
903
    )
904
905
    access_list_addresses = set()
906
    access_list_storage_keys = set()
907
    access_list_addresses.add(block_env.coinbase)
908
    if isinstance(
909
        tx,
910
        (
911
            AccessListTransaction,
912
            FeeMarketTransaction,
913
            BlobTransaction,
914
            SetCodeTransaction,
915
        ),
916
    ):
917
        for access in tx.access_list:
918
            access_list_addresses.add(access.account)
919
            for slot in access.slots:
920
                access_list_storage_keys.add((access.account, slot))
921
922
    authorizations: Tuple[Authorization, ...] = ()
923
    if isinstance(tx, SetCodeTransaction):
924
        authorizations = tx.authorizations
925
926
    tx_env = vm.TransactionEnvironment(
927
        origin=sender,
928
        gas_price=effective_gas_price,
929
        gas=gas,
930
        access_list_addresses=access_list_addresses,
931
        access_list_storage_keys=access_list_storage_keys,
932
        transient_storage=TransientStorage(),
933
        blob_versioned_hashes=blob_versioned_hashes,
934
        authorizations=authorizations,
935
        index_in_block=index,
936
        tx_hash=get_transaction_hash(encode_transaction(tx)),
937
    )
938
939
    message = prepare_message(block_env, tx_env, tx)
940
941
    tx_output = process_message_call(message)
942
943
    # For EIP-7623 we first calculate the execution_gas_used, which includes
944
    # the execution gas refund.
945
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
946
    tx_gas_refund = min(
947
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
948
    )
949
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
950
951
    # Transactions with less execution_gas_used than the floor pay at the
952
    # floor cost.
953
    tx_gas_used_after_refund = max(
954
        tx_gas_used_after_refund, calldata_floor_gas_cost
955
    )
956
957
    tx_gas_left = tx.gas - tx_gas_used_after_refund
958
    gas_refund_amount = tx_gas_left * effective_gas_price
959
960
    # For non-1559 transactions effective_gas_price == tx.gas_price
961
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
962
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
963
964
    # refund gas
965
    sender_balance_after_refund = get_account(
966
        block_env.state, sender
967
    ).balance + U256(gas_refund_amount)
968
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
969
970
    # transfer miner fees
971
    coinbase_balance_after_mining_fee = get_account(
972
        block_env.state, block_env.coinbase
973
    ).balance + U256(transaction_fee)
974
    set_account_balance(
975
        block_env.state,
976
        block_env.coinbase,
977
        coinbase_balance_after_mining_fee,
978
    )
979
980
    for address in tx_output.accounts_to_delete:
981
        destroy_account(block_env.state, address)
982
983
    block_output.block_gas_used += tx_gas_used_after_refund
984
    block_output.blob_gas_used += tx_blob_gas_used
985
986
    receipt = make_receipt(
987
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
988
    )
989
990
    receipt_key = rlp.encode(Uint(index))
991
    block_output.receipt_keys += (receipt_key,)
992
993
    trie_set(
994
        block_output.receipts_trie,
995
        receipt_key,
996
        receipt,
997
    )
998
999
    block_output.block_logs += tx_output.logs

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.osaka.vm.BlockEnvironment, ​​block_output: ethereum.forks.osaka.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
1007
    """
1008
    Increase the balance of the withdrawing account.
1009
    """
1010
1011
    def increase_recipient_balance(recipient: Account) -> None:
1012
        recipient.balance += wd.amount * U256(10**9)
1013
1014
    for i, wd in enumerate(withdrawals):
1015
        trie_set(
1016
            block_output.withdrawals_trie,
1017
            rlp.encode(Uint(i)),
1018
            rlp.encode(wd),
1019
        )
1020
1021
        modify_state(block_env.state, wd.address, increase_recipient_balance)

check_gas_limit

Validates the gas limit for a block.

The bounds of the gas limit, max_adjustment_delta, is set as the quotient of the parent block's gas limit and the GAS_LIMIT_ADJUSTMENT_FACTOR. Therefore, if the gas limit that is passed through as a parameter is greater than or equal to the sum of the parent's gas and the adjustment delta then the limit for gas is too high and fails this function's check. Similarly, if the limit is less than or equal to the difference of the parent's gas and the adjustment delta or the predefined GAS_LIMIT_MINIMUM then this function's check fails because the gas limit doesn't allow for a sufficient or reasonable amount of gas to be used on a block.

Parameters

gas_limit : Gas limit to validate.

parent_gas_limit : Gas limit of the parent block.

Returns

check : bool True if gas limit constraints are satisfied, False otherwise.

def check_gas_limit(gas_limit: Uint, ​​parent_gas_limit: Uint) -> bool:
1025
    """
1026
    Validates the gas limit for a block.
1027
1028
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1029
    quotient of the parent block's gas limit and the
1030
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1031
    passed through as a parameter is greater than or equal to the *sum* of
1032
    the parent's gas and the adjustment delta then the limit for gas is too
1033
    high and fails this function's check. Similarly, if the limit is less
1034
    than or equal to the *difference* of the parent's gas and the adjustment
1035
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1036
    check fails because the gas limit doesn't allow for a sufficient or
1037
    reasonable amount of gas to be used on a block.
1038
1039
    Parameters
1040
    ----------
1041
    gas_limit :
1042
        Gas limit to validate.
1043
1044
    parent_gas_limit :
1045
        Gas limit of the parent block.
1046
1047
    Returns
1048
    -------
1049
    check : `bool`
1050
        True if gas limit constraints are satisfied, False otherwise.
1051
1052
    """
1053
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1054
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1055
        return False
1056
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1057
        return False
1058
    if gas_limit < GAS_LIMIT_MINIMUM:
1059
        return False
1060
1061
    return True