ethereum.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 = U64(1179648)

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
    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]:
153
    """
154
    Obtain the list of hashes of the previous 256 blocks in order of
155
    increasing block number.
156
157
    This function will return less hashes for the first 256 blocks.
158
159
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
160
    therefore this function retrieves them.
161
162
    Parameters
163
    ----------
164
    chain :
165
        History and current state.
166
167
    Returns
168
    -------
169
    recent_block_hashes : `List[Hash32]`
170
        Hashes of the recent 256 blocks in order of increasing block number.
171
    """
172
    recent_blocks = chain.blocks[-255:]
173
    # TODO: This function has not been tested rigorously
174
    if len(recent_blocks) == 0:
175
        return []
176
177
    recent_block_hashes = []
178
179
    for block in recent_blocks:
180
        prev_block_hash = block.header.parent_hash
181
        recent_block_hashes.append(prev_block_hash)
182
183
    # We are computing the hash only for the most recent block and not for
184
    # the rest of the blocks as they have successors which have the hash of
185
    # the current block as parent hash.
186
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
187
    recent_block_hashes.append(most_recent_block_hash)
188
189
    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:
193
    """
194
    Attempts to apply a block to an existing block chain.
195
196
    All parts of the block's contents need to be verified before being added
197
    to the chain. Blocks are verified by ensuring that the contents of the
198
    block make logical sense with the contents of the parent block. The
199
    information in the block's header must also match the corresponding
200
    information in the block.
201
202
    To implement Ethereum, in theory clients are only required to store the
203
    most recent 255 blocks of the chain since as far as execution is
204
    concerned, only those blocks are accessed. Practically, however, clients
205
    should store more blocks to handle reorgs.
206
207
    Parameters
208
    ----------
209
    chain :
210
        History and current state.
211
    block :
212
        Block to apply to `chain`.
213
    """
214
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
215
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
216
217
    validate_header(chain, block.header)
218
    if block.ommers != ():
219
        raise InvalidBlock
220
221
    block_env = vm.BlockEnvironment(
222
        chain_id=chain.chain_id,
223
        state=chain.state,
224
        block_gas_limit=block.header.gas_limit,
225
        block_hashes=get_last_256_block_hashes(chain),
226
        coinbase=block.header.coinbase,
227
        number=block.header.number,
228
        base_fee_per_gas=block.header.base_fee_per_gas,
229
        time=block.header.timestamp,
230
        prev_randao=block.header.prev_randao,
231
        excess_blob_gas=block.header.excess_blob_gas,
232
        parent_beacon_block_root=block.header.parent_beacon_block_root,
233
    )
234
235
    block_output = apply_body(
236
        block_env=block_env,
237
        transactions=block.transactions,
238
        withdrawals=block.withdrawals,
239
    )
240
    block_state_root = state_root(block_env.state)
241
    transactions_root = root(block_output.transactions_trie)
242
    receipt_root = root(block_output.receipts_trie)
243
    block_logs_bloom = logs_bloom(block_output.block_logs)
244
    withdrawals_root = root(block_output.withdrawals_trie)
245
    requests_hash = compute_requests_hash(block_output.requests)
246
247
    if block_output.block_gas_used != block.header.gas_used:
248
        raise InvalidBlock(
249
            f"{block_output.block_gas_used} != {block.header.gas_used}"
250
        )
251
    if transactions_root != block.header.transactions_root:
252
        raise InvalidBlock
253
    if block_state_root != block.header.state_root:
254
        raise InvalidBlock
255
    if receipt_root != block.header.receipt_root:
256
        raise InvalidBlock
257
    if block_logs_bloom != block.header.bloom:
258
        raise InvalidBlock
259
    if withdrawals_root != block.header.withdrawals_root:
260
        raise InvalidBlock
261
    if block_output.blob_gas_used != block.header.blob_gas_used:
262
        raise InvalidBlock
263
    if requests_hash != block.header.requests_hash:
264
        raise InvalidBlock
265
266
    chain.blocks.append(block)
267
    if len(chain.blocks) > 255:
268
        # Real clients have to store more blocks to deal with reorgs, but the
269
        # protocol only requires the last 255
270
        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:
279
    """
280
    Calculates the base fee per gas for the block.
281
282
    Parameters
283
    ----------
284
    block_gas_limit :
285
        Gas limit of the block for which the base fee is being calculated.
286
    parent_gas_limit :
287
        Gas limit of the parent block.
288
    parent_gas_used :
289
        Gas used in the parent block.
290
    parent_base_fee_per_gas :
291
        Base fee per gas of the parent block.
292
293
    Returns
294
    -------
295
    base_fee_per_gas : `Uint`
296
        Base fee per gas for the block.
297
    """
298
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
299
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
300
        raise InvalidBlock
301
302
    if parent_gas_used == parent_gas_target:
303
        expected_base_fee_per_gas = parent_base_fee_per_gas
304
    elif parent_gas_used > parent_gas_target:
305
        gas_used_delta = parent_gas_used - parent_gas_target
306
307
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
308
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
309
310
        base_fee_per_gas_delta = max(
311
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
312
            Uint(1),
313
        )
314
315
        expected_base_fee_per_gas = (
316
            parent_base_fee_per_gas + base_fee_per_gas_delta
317
        )
318
    else:
319
        gas_used_delta = parent_gas_target - parent_gas_used
320
321
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
322
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
323
324
        base_fee_per_gas_delta = (
325
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
326
        )
327
328
        expected_base_fee_per_gas = (
329
            parent_base_fee_per_gas - base_fee_per_gas_delta
330
        )
331
332
    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:
336
    """
337
    Verifies a block header.
338
339
    In order to consider a block's header valid, the logic for the
340
    quantities in the header should match the logic for the block itself.
341
    For example the header timestamp should be greater than the block's parent
342
    timestamp because the block was created *after* the parent block.
343
    Additionally, the block's number should be directly following the parent
344
    block's number since it is the next block in the sequence.
345
346
    Parameters
347
    ----------
348
    chain :
349
        History and current state.
350
    header :
351
        Header to check for correctness.
352
    """
353
    if header.number < Uint(1):
354
        raise InvalidBlock
355
356
    parent_header = chain.blocks[-1].header
357
358
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
359
    if header.excess_blob_gas != excess_blob_gas:
360
        raise InvalidBlock
361
362
    if header.gas_used > header.gas_limit:
363
        raise InvalidBlock
364
365
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
366
        header.gas_limit,
367
        parent_header.gas_limit,
368
        parent_header.gas_used,
369
        parent_header.base_fee_per_gas,
370
    )
371
    if expected_base_fee_per_gas != header.base_fee_per_gas:
372
        raise InvalidBlock
373
    if header.timestamp <= parent_header.timestamp:
374
        raise InvalidBlock
375
    if header.number != parent_header.number + Uint(1):
376
        raise InvalidBlock
377
    if len(header.extra_data) > 32:
378
        raise InvalidBlock
379
    if header.difficulty != 0:
380
        raise InvalidBlock
381
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
382
        raise InvalidBlock
383
    if header.ommers_hash != EMPTY_OMMER_HASH:
384
        raise InvalidBlock
385
386
    block_parent_hash = keccak256(rlp.encode(parent_header))
387
    if header.parent_hash != block_parent_hash:
388
        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 nore 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.osaka.vm.BlockEnvironment, ​​block_output: ethereum.osaka.vm.BlockOutput, ​​tx: Transaction) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
396
    """
397
    Check if the transaction is includable in the block.
398
399
    Parameters
400
    ----------
401
    block_env :
402
        The block scoped environment.
403
    block_output :
404
        The block output for the current block.
405
    tx :
406
        The transaction.
407
408
    Returns
409
    -------
410
    sender_address :
411
        The sender of the transaction.
412
    effective_gas_price :
413
        The price to charge for gas when the transaction is executed.
414
    blob_versioned_hashes :
415
        The blob versioned hashes of the transaction.
416
    tx_blob_gas_used:
417
        The blob gas used by the transaction.
418
419
    Raises
420
    ------
421
    InvalidBlock :
422
        If the transaction is not includable.
423
    GasUsedExceedsLimitError :
424
        If the gas used by the transaction exceeds the block's gas limit.
425
    NonceMismatchError :
426
        If the nonce of the transaction is not equal to the sender's nonce.
427
    InsufficientBalanceError :
428
        If the sender's balance is not enough to pay for the transaction.
429
    InvalidSenderError :
430
        If the transaction is from an address that does not exist anymore.
431
    PriorityFeeGreaterThanMaxFeeError :
432
        If the priority fee is greater than the maximum fee per gas.
433
    InsufficientMaxFeePerGasError :
434
        If the maximum fee per gas is insufficient for the transaction.
435
    InsufficientMaxFeePerBlobGasError :
436
        If the maximum fee per blob gas is insufficient for the transaction.
437
    BlobGasLimitExceededError :
438
        If the blob gas used by the transaction exceeds the block's blob gas
439
        limit.
440
    InvalidBlobVersionedHashError :
441
        If the transaction contains a blob versioned hash with an invalid
442
        version.
443
    NoBlobDataError :
444
        If the transaction is a type 3 but has no blobs.
445
    BlobCountExceededError :
446
        If the transaction is a type 3 and has nore blobs than the limit.
447
    TransactionTypeContractCreationError:
448
        If the transaction type is not allowed to create contracts.
449
    EmptyAuthorizationListError :
450
        If the transaction is a SetCodeTransaction and the authorization list
451
        is empty.
452
    """
453
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
454
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
455
456
    if tx.gas > gas_available:
457
        raise GasUsedExceedsLimitError("gas used exceeds limit")
458
459
    tx_blob_gas_used = calculate_total_blob_gas(tx)
460
    if tx_blob_gas_used > blob_gas_available:
461
        raise BlobGasLimitExceededError("blob gas limit exceeded")
462
463
    sender_address = recover_sender(block_env.chain_id, tx)
464
    sender_account = get_account(block_env.state, sender_address)
465
466
    if isinstance(
467
        tx, (FeeMarketTransaction, BlobTransaction, SetCodeTransaction)
468
    ):
469
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
470
            raise PriorityFeeGreaterThanMaxFeeError(
471
                "priority fee greater than max fee"
472
            )
473
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
474
            raise InsufficientMaxFeePerGasError(
475
                tx.max_fee_per_gas, block_env.base_fee_per_gas
476
            )
477
478
        priority_fee_per_gas = min(
479
            tx.max_priority_fee_per_gas,
480
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
481
        )
482
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
483
        max_gas_fee = tx.gas * tx.max_fee_per_gas
484
    else:
485
        if tx.gas_price < block_env.base_fee_per_gas:
486
            raise InvalidBlock
487
        effective_gas_price = tx.gas_price
488
        max_gas_fee = tx.gas * tx.gas_price
489
490
    if isinstance(tx, BlobTransaction):
491
        blob_count = len(tx.blob_versioned_hashes)
492
        if blob_count == 0:
493
            raise NoBlobDataError("no blob data in transaction")
494
        if blob_count > BLOB_COUNT_LIMIT:
495
            raise BlobCountExceededError(
496
                f"Tx has {blob_count} blobs. Max allowed: {BLOB_COUNT_LIMIT}"
497
            )
498
        for blob_versioned_hash in tx.blob_versioned_hashes:
499
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
500
                raise InvalidBlobVersionedHashError(
501
                    "invalid blob versioned hash"
502
                )
503
504
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
505
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
506
            raise InsufficientMaxFeePerBlobGasError(
507
                "insufficient max fee per blob gas"
508
            )
509
510
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
511
            tx.max_fee_per_blob_gas
512
        )
513
        blob_versioned_hashes = tx.blob_versioned_hashes
514
    else:
515
        blob_versioned_hashes = ()
516
517
    if isinstance(tx, (BlobTransaction, SetCodeTransaction)):
518
        if not isinstance(tx.to, Address):
519
            raise TransactionTypeContractCreationError(tx)
520
521
    if isinstance(tx, SetCodeTransaction):
522
        if not any(tx.authorizations):
523
            raise EmptyAuthorizationListError("empty authorization list")
524
525
    if sender_account.nonce > Uint(tx.nonce):
526
        raise NonceMismatchError("nonce too low")
527
    elif sender_account.nonce < Uint(tx.nonce):
528
        raise NonceMismatchError("nonce too high")
529
530
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
531
        raise InsufficientBalanceError("insufficient sender balance")
532
    if sender_account.code and not is_valid_delegation(sender_account.code):
533
        raise InvalidSenderError("not EOA")
534
535
    return (
536
        sender_address,
537
        effective_gas_price,
538
        blob_versioned_hashes,
539
        tx_blob_gas_used,
540
    )

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:
549
    """
550
    Make the receipt for a transaction that was executed.
551
552
    Parameters
553
    ----------
554
    tx :
555
        The executed transaction.
556
    error :
557
        Error in the top level frame of the transaction, if any.
558
    cumulative_gas_used :
559
        The total gas used so far in the block after the transaction was
560
        executed.
561
    logs :
562
        The logs produced by the transaction.
563
564
    Returns
565
    -------
566
    receipt :
567
        The receipt for the transaction.
568
    """
569
    receipt = Receipt(
570
        succeeded=error is None,
571
        cumulative_gas_used=cumulative_gas_used,
572
        bloom=logs_bloom(logs),
573
        logs=logs,
574
    )
575
576
    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.osaka.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
585
    """
586
    Process a system transaction with the given code.
587
588
    Prefer calling `process_checked_system_transaction` or
589
    `process_unchecked_system_transaction` depending on whether missing code or
590
    an execution error should cause the block to be rejected.
591
592
    Parameters
593
    ----------
594
    block_env :
595
        The block scoped environment.
596
    target_address :
597
        Address of the contract to call.
598
    system_contract_code :
599
        Code of the contract to call.
600
    data :
601
        Data to pass to the contract.
602
603
    Returns
604
    -------
605
    system_tx_output : `MessageCallOutput`
606
        Output of processing the system transaction.
607
    """
608
    tx_env = vm.TransactionEnvironment(
609
        origin=SYSTEM_ADDRESS,
610
        gas_price=block_env.base_fee_per_gas,
611
        gas=SYSTEM_TRANSACTION_GAS,
612
        access_list_addresses=set(),
613
        access_list_storage_keys=set(),
614
        transient_storage=TransientStorage(),
615
        blob_versioned_hashes=(),
616
        authorizations=(),
617
        index_in_block=None,
618
        tx_hash=None,
619
        traces=[],
620
    )
621
622
    system_tx_message = Message(
623
        block_env=block_env,
624
        tx_env=tx_env,
625
        caller=SYSTEM_ADDRESS,
626
        target=target_address,
627
        gas=SYSTEM_TRANSACTION_GAS,
628
        value=U256(0),
629
        data=data,
630
        code=system_contract_code,
631
        depth=Uint(0),
632
        current_target=target_address,
633
        code_address=target_address,
634
        should_transfer_value=False,
635
        is_static=False,
636
        accessed_addresses=set(),
637
        accessed_storage_keys=set(),
638
        disable_precompiles=False,
639
        parent_evm=None,
640
    )
641
642
    system_tx_output = process_message_call(system_tx_message)
643
644
    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.osaka.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
652
    """
653
    Process a system transaction and raise an error if the contract does not
654
    contain code or if the transaction fails.
655
656
    Parameters
657
    ----------
658
    block_env :
659
        The block scoped environment.
660
    target_address :
661
        Address of the contract to call.
662
    data :
663
        Data to pass to the contract.
664
665
    Returns
666
    -------
667
    system_tx_output : `MessageCallOutput`
668
        Output of processing the system transaction.
669
    """
670
    system_contract_code = get_account(block_env.state, target_address).code
671
672
    if len(system_contract_code) == 0:
673
        raise InvalidBlock(
674
            f"System contract address {target_address.hex()} does not "
675
            "contain code"
676
        )
677
678
    system_tx_output = process_system_transaction(
679
        block_env,
680
        target_address,
681
        system_contract_code,
682
        data,
683
    )
684
685
    if system_tx_output.error:
686
        raise InvalidBlock(
687
            f"System contract ({target_address.hex()}) call failed: "
688
            f"{system_tx_output.error}"
689
        )
690
691
    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.osaka.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
699
    """
700
    Process a system transaction without checking if the contract contains code
701
    or if the transaction fails.
702
703
    Parameters
704
    ----------
705
    block_env :
706
        The block scoped environment.
707
    target_address :
708
        Address of the contract to call.
709
    data :
710
        Data to pass to the contract.
711
712
    Returns
713
    -------
714
    system_tx_output : `MessageCallOutput`
715
        Output of processing the system transaction.
716
    """
717
    system_contract_code = get_account(block_env.state, target_address).code
718
    return process_system_transaction(
719
        block_env,
720
        target_address,
721
        system_contract_code,
722
        data,
723
    )

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

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

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.osaka.vm.BlockEnvironment, ​​block_output: ethereum.osaka.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
999
    """
1000
    Increase the balance of the withdrawing account.
1001
    """
1002
1003
    def increase_recipient_balance(recipient: Account) -> None:
1004
        recipient.balance += wd.amount * U256(10**9)
1005
1006
    for i, wd in enumerate(withdrawals):
1007
        trie_set(
1008
            block_output.withdrawals_trie,
1009
            rlp.encode(Uint(i)),
1010
            rlp.encode(wd),
1011
        )
1012
1013
        modify_state(block_env.state, wd.address, increase_recipient_balance)
1014
1015
        if account_exists_and_is_empty(block_env.state, wd.address):
1016
            destroy_account(block_env.state, wd.address)

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