ethereum.osaka.fork

Ethereum Specification ^^^^^^^^^^^^^^^^^^^^^^

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

90
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

91
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

92
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

93
GAS_LIMIT_MINIMUM = Uint(5000)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

95
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

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

SYSTEM_TRANSACTION_GAS

99
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

100
MAX_BLOB_GAS_PER_BLOCK = U64(1179648)

VERSIONED_HASH_VERSION_KZG

101
VERSIONED_HASH_VERSION_KZG = b"\x01"

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

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

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

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

HISTORY_STORAGE_ADDRESS

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

MAX_BLOCK_SIZE

112
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

113
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

114
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

115
BLOB_COUNT_LIMIT = 6

BlockChain

History and current state of the block chain.

118
@dataclass
class BlockChain:

blocks

124
    blocks: List[Block]

state

125
    state: State

chain_id

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

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

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

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.osaka.vm.BlockEnvironment, ​​block_output: ethereum.osaka.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
830
    """
831
    Execute a transaction against the provided environment.
832
833
    This function processes the actions needed to execute a transaction.
834
    It decrements the sender's account balance after calculating the gas fee
835
    and refunds them the proper amount after execution. Calling contracts,
836
    deploying code, and incrementing nonces are all examples of actions that
837
    happen within this function or from a call made within this function.
838
839
    Accounts that are marked for deletion are processed and destroyed after
840
    execution.
841
842
    Parameters
843
    ----------
844
    block_env :
845
        Environment for the Ethereum Virtual Machine.
846
    block_output :
847
        The block output for the current block.
848
    tx :
849
        Transaction to execute.
850
    index:
851
        Index of the transaction in the block.
852
    """
853
    trie_set(
854
        block_output.transactions_trie,
855
        rlp.encode(index),
856
        encode_transaction(tx),
857
    )
858
859
    intrinsic_gas, calldata_floor_gas_cost = validate_transaction(tx)
860
861
    (
862
        sender,
863
        effective_gas_price,
864
        blob_versioned_hashes,
865
        tx_blob_gas_used,
866
    ) = check_transaction(
867
        block_env=block_env,
868
        block_output=block_output,
869
        tx=tx,
870
    )
871
872
    sender_account = get_account(block_env.state, sender)
873
874
    if isinstance(tx, BlobTransaction):
875
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
876
    else:
877
        blob_gas_fee = Uint(0)
878
879
    effective_gas_fee = tx.gas * effective_gas_price
880
881
    gas = tx.gas - intrinsic_gas
882
    increment_nonce(block_env.state, sender)
883
884
    sender_balance_after_gas_fee = (
885
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
886
    )
887
    set_account_balance(
888
        block_env.state, sender, U256(sender_balance_after_gas_fee)
889
    )
890
891
    access_list_addresses = set()
892
    access_list_storage_keys = set()
893
    access_list_addresses.add(block_env.coinbase)
894
    if isinstance(
895
        tx,
896
        (
897
            AccessListTransaction,
898
            FeeMarketTransaction,
899
            BlobTransaction,
900
            SetCodeTransaction,
901
        ),
902
    ):
903
        for access in tx.access_list:
904
            access_list_addresses.add(access.account)
905
            for slot in access.slots:
906
                access_list_storage_keys.add((access.account, slot))
907
908
    authorizations: Tuple[Authorization, ...] = ()
909
    if isinstance(tx, SetCodeTransaction):
910
        authorizations = tx.authorizations
911
912
    tx_env = vm.TransactionEnvironment(
913
        origin=sender,
914
        gas_price=effective_gas_price,
915
        gas=gas,
916
        access_list_addresses=access_list_addresses,
917
        access_list_storage_keys=access_list_storage_keys,
918
        transient_storage=TransientStorage(),
919
        blob_versioned_hashes=blob_versioned_hashes,
920
        authorizations=authorizations,
921
        index_in_block=index,
922
        tx_hash=get_transaction_hash(encode_transaction(tx)),
923
    )
924
925
    message = prepare_message(block_env, tx_env, tx)
926
927
    tx_output = process_message_call(message)
928
929
    # For EIP-7623 we first calculate the execution_gas_used, which includes
930
    # the execution gas refund.
931
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
932
    tx_gas_refund = min(
933
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
934
    )
935
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
936
937
    # Transactions with less execution_gas_used than the floor pay at the
938
    # floor cost.
939
    tx_gas_used_after_refund = max(
940
        tx_gas_used_after_refund, calldata_floor_gas_cost
941
    )
942
943
    tx_gas_left = tx.gas - tx_gas_used_after_refund
944
    gas_refund_amount = tx_gas_left * effective_gas_price
945
946
    # For non-1559 transactions effective_gas_price == tx.gas_price
947
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
948
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
949
950
    # refund gas
951
    sender_balance_after_refund = get_account(
952
        block_env.state, sender
953
    ).balance + U256(gas_refund_amount)
954
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
955
956
    # transfer miner fees
957
    coinbase_balance_after_mining_fee = get_account(
958
        block_env.state, block_env.coinbase
959
    ).balance + U256(transaction_fee)
960
    set_account_balance(
961
        block_env.state,
962
        block_env.coinbase,
963
        coinbase_balance_after_mining_fee,
964
    )
965
966
    for address in tx_output.accounts_to_delete:
967
        destroy_account(block_env.state, address)
968
969
    block_output.block_gas_used += tx_gas_used_after_refund
970
    block_output.blob_gas_used += tx_blob_gas_used
971
972
    receipt = make_receipt(
973
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
974
    )
975
976
    receipt_key = rlp.encode(Uint(index))
977
    block_output.receipt_keys += (receipt_key,)
978
979
    trie_set(
980
        block_output.receipts_trie,
981
        receipt_key,
982
        receipt,
983
    )
984
985
    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:
993
    """
994
    Increase the balance of the withdrawing account.
995
    """
996
997
    def increase_recipient_balance(recipient: Account) -> None:
998
        recipient.balance += wd.amount * U256(10**9)
999
1000
    for i, wd in enumerate(withdrawals):
1001
        trie_set(
1002
            block_output.withdrawals_trie,
1003
            rlp.encode(Uint(i)),
1004
            rlp.encode(wd),
1005
        )
1006
1007
        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:
1011
    """
1012
    Validates the gas limit for a block.
1013
1014
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1015
    quotient of the parent block's gas limit and the
1016
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1017
    passed through as a parameter is greater than or equal to the *sum* of
1018
    the parent's gas and the adjustment delta then the limit for gas is too
1019
    high and fails this function's check. Similarly, if the limit is less
1020
    than or equal to the *difference* of the parent's gas and the adjustment
1021
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1022
    check fails because the gas limit doesn't allow for a sufficient or
1023
    reasonable amount of gas to be used on a block.
1024
1025
    Parameters
1026
    ----------
1027
    gas_limit :
1028
        Gas limit to validate.
1029
1030
    parent_gas_limit :
1031
        Gas limit of the parent block.
1032
1033
    Returns
1034
    -------
1035
    check : `bool`
1036
        True if gas limit constraints are satisfied, False otherwise.
1037
    """
1038
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1039
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1040
        return False
1041
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1042
        return False
1043
    if gas_limit < GAS_LIMIT_MINIMUM:
1044
        return False
1045
1046
    return True