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
    )
620
621
    system_tx_message = Message(
622
        block_env=block_env,
623
        tx_env=tx_env,
624
        caller=SYSTEM_ADDRESS,
625
        target=target_address,
626
        gas=SYSTEM_TRANSACTION_GAS,
627
        value=U256(0),
628
        data=data,
629
        code=system_contract_code,
630
        depth=Uint(0),
631
        current_target=target_address,
632
        code_address=target_address,
633
        should_transfer_value=False,
634
        is_static=False,
635
        accessed_addresses=set(),
636
        accessed_storage_keys=set(),
637
        disable_precompiles=False,
638
        parent_evm=None,
639
    )
640
641
    system_tx_output = process_message_call(system_tx_message)
642
643
    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:
651
    """
652
    Process a system transaction and raise an error if the contract does not
653
    contain code or if the transaction fails.
654
655
    Parameters
656
    ----------
657
    block_env :
658
        The block scoped environment.
659
    target_address :
660
        Address of the contract to call.
661
    data :
662
        Data to pass to the contract.
663
664
    Returns
665
    -------
666
    system_tx_output : `MessageCallOutput`
667
        Output of processing the system transaction.
668
    """
669
    system_contract_code = get_account(block_env.state, target_address).code
670
671
    if len(system_contract_code) == 0:
672
        raise InvalidBlock(
673
            f"System contract address {target_address.hex()} does not "
674
            "contain code"
675
        )
676
677
    system_tx_output = process_system_transaction(
678
        block_env,
679
        target_address,
680
        system_contract_code,
681
        data,
682
    )
683
684
    if system_tx_output.error:
685
        raise InvalidBlock(
686
            f"System contract ({target_address.hex()}) call failed: "
687
            f"{system_tx_output.error}"
688
        )
689
690
    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:
698
    """
699
    Process a system transaction without checking if the contract contains code
700
    or if the transaction fails.
701
702
    Parameters
703
    ----------
704
    block_env :
705
        The block scoped environment.
706
    target_address :
707
        Address of the contract to call.
708
    data :
709
        Data to pass to the contract.
710
711
    Returns
712
    -------
713
    system_tx_output : `MessageCallOutput`
714
        Output of processing the system transaction.
715
    """
716
    system_contract_code = get_account(block_env.state, target_address).code
717
    return process_system_transaction(
718
        block_env,
719
        target_address,
720
        system_contract_code,
721
        data,
722
    )

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

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