ethereum.forks.prague.forkethereum.forks.osaka.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

89
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

90
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

91
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

92
GAS_LIMIT_MINIMUM = Uint(5000)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

94
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

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

SYSTEM_TRANSACTION_GAS

98
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

99
MAX_BLOB_GAS_PER_BLOCK = U64(1179648)

VERSIONED_HASH_VERSION_KZG

100
VERSIONED_HASH_VERSION_KZG = b"\x01"

WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS

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

CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS

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

HISTORY_STORAGE_ADDRESS

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

MAX_BLOCK_SIZE

111
MAX_BLOCK_SIZE = 10_485_760

SAFETY_MARGIN

112
SAFETY_MARGIN = 2_097_152

MAX_RLP_BLOCK_SIZE

113
MAX_RLP_BLOCK_SIZE = MAX_BLOCK_SIZE - SAFETY_MARGIN

BLOB_COUNT_LIMIT

114
BLOB_COUNT_LIMIT = 6

BlockChain

History and current state of the block chain.

117
@dataclass
class BlockChain:

blocks

123
    blocks: List[Block]

state

124
    state: State

chain_id

125
    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:
129
    """
130
    Transforms the state from the previous hard fork (`old`) into the block
131
    chain object for this hard fork and returns it.
132
133
    When forks need to implement an irregular state transition, this function
134
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
135
    an example.
136
137
    Parameters
138
    ----------
139
    old :
140
        Previous block chain object.
141
142
    Returns
143
    -------
144
    new : `BlockChain`
145
        Upgraded block chain object for this hard fork.
146
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
    """
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
    """
215
    if len(rlp.encode(block)) > MAX_RLP_BLOCK_SIZE:
216
        raise InvalidBlock("Block rlp size exceeds MAX_RLP_BLOCK_SIZE")
217
218
    validate_header(chain, block.header)
219
    if block.ommers != ():
220
        raise InvalidBlock
221
222
    block_env = vm.BlockEnvironment(
223
        chain_id=chain.chain_id,
224
        state=chain.state,
225
        block_gas_limit=block.header.gas_limit,
226
        block_hashes=get_last_256_block_hashes(chain),
227
        coinbase=block.header.coinbase,
228
        number=block.header.number,
229
        base_fee_per_gas=block.header.base_fee_per_gas,
230
        time=block.header.timestamp,
231
        prev_randao=block.header.prev_randao,
232
        excess_blob_gas=block.header.excess_blob_gas,
233
        parent_beacon_block_root=block.header.parent_beacon_block_root,
234
    )
235
236
    block_output = apply_body(
237
        block_env=block_env,
238
        transactions=block.transactions,
239
        withdrawals=block.withdrawals,
240
    )
241
    block_state_root = state_root(block_env.state)
242
    transactions_root = root(block_output.transactions_trie)
243
    receipt_root = root(block_output.receipts_trie)
244
    block_logs_bloom = logs_bloom(block_output.block_logs)
245
    withdrawals_root = root(block_output.withdrawals_trie)
246
    requests_hash = compute_requests_hash(block_output.requests)
247
248
    if block_output.block_gas_used != block.header.gas_used:
249
        raise InvalidBlock(
250
            f"{block_output.block_gas_used} != {block.header.gas_used}"
251
        )
252
    if transactions_root != block.header.transactions_root:
253
        raise InvalidBlock
254
    if block_state_root != block.header.state_root:
255
        raise InvalidBlock
256
    if receipt_root != block.header.receipt_root:
257
        raise InvalidBlock
258
    if block_logs_bloom != block.header.bloom:
259
        raise InvalidBlock
260
    if withdrawals_root != block.header.withdrawals_root:
261
        raise InvalidBlock
262
    if block_output.blob_gas_used != block.header.blob_gas_used:
263
        raise InvalidBlock
264
    if requests_hash != block.header.requests_hash:
265
        raise InvalidBlock
266
267
    chain.blocks.append(block)
268
    if len(chain.blocks) > 255:
269
        # Real clients have to store more blocks to deal with reorgs, but the
270
        # protocol only requires the last 255
271
        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:
280
    """
281
    Calculates the base fee per gas for the block.
282
283
    Parameters
284
    ----------
285
    block_gas_limit :
286
        Gas limit of the block for which the base fee is being calculated.
287
    parent_gas_limit :
288
        Gas limit of the parent block.
289
    parent_gas_used :
290
        Gas used in the parent block.
291
    parent_base_fee_per_gas :
292
        Base fee per gas of the parent block.
293
294
    Returns
295
    -------
296
    base_fee_per_gas : `Uint`
297
        Base fee per gas for the block.
298
299
    """
300
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
301
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
302
        raise InvalidBlock
303
304
    if parent_gas_used == parent_gas_target:
305
        expected_base_fee_per_gas = parent_base_fee_per_gas
306
    elif parent_gas_used > parent_gas_target:
307
        gas_used_delta = parent_gas_used - parent_gas_target
308
309
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
310
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
311
312
        base_fee_per_gas_delta = max(
313
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
314
            Uint(1),
315
        )
316
317
        expected_base_fee_per_gas = (
318
            parent_base_fee_per_gas + base_fee_per_gas_delta
319
        )
320
    else:
321
        gas_used_delta = parent_gas_target - parent_gas_used
322
323
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
324
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
325
326
        base_fee_per_gas_delta = (
327
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
328
        )
329
330
        expected_base_fee_per_gas = (
331
            parent_base_fee_per_gas - base_fee_per_gas_delta
332
        )
333
334
    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:
338
    """
339
    Verifies a block header.
340
341
    In order to consider a block's header valid, the logic for the
342
    quantities in the header should match the logic for the block itself.
343
    For example the header timestamp should be greater than the block's parent
344
    timestamp because the block was created *after* the parent block.
345
    Additionally, the block's number should be directly following the parent
346
    block's number since it is the next block in the sequence.
347
348
    Parameters
349
    ----------
350
    chain :
351
        History and current state.
352
    header :
353
        Header to check for correctness.
354
355
    """
356
    if header.number < Uint(1):
357
        raise InvalidBlock
358
359
    parent_header = chain.blocks[-1].header
360
361
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
362
    if header.excess_blob_gas != excess_blob_gas:
363
        raise InvalidBlock
364
365
    if header.gas_used > header.gas_limit:
366
        raise InvalidBlock
367
368
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
369
        header.gas_limit,
370
        parent_header.gas_limit,
371
        parent_header.gas_used,
372
        parent_header.base_fee_per_gas,
373
    )
374
    if expected_base_fee_per_gas != header.base_fee_per_gas:
375
        raise InvalidBlock
376
    if header.timestamp <= parent_header.timestamp:
377
        raise InvalidBlock
378
    if header.number != parent_header.number + Uint(1):
379
        raise InvalidBlock
380
    if len(header.extra_data) > 32:
381
        raise InvalidBlock
382
    if header.difficulty != 0:
383
        raise InvalidBlock
384
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
385
        raise InvalidBlock
386
    if header.ommers_hash != EMPTY_OMMER_HASH:
387
        raise InvalidBlock
388
389
    block_parent_hash = keccak256(rlp.encode(parent_header))
390
    if header.parent_hash != block_parent_hash:
391
        raise InvalidBlock

check_transaction

Check if the transaction is includable in the block.

Parameters

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

Returns

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

Raises

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

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

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

process_system_transaction

Process a system transaction with the given code.

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

Parameters

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

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

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

process_checked_system_transaction

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

Parameters

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

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

def process_checked_system_transaction(block_env: ethereum.forks.prague.vm.BlockEnvironmentethereum.forks.osaka.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
657
    """
658
    Process a system transaction and raise an error if the contract does not
659
    contain code or if the transaction fails.
660
661
    Parameters
662
    ----------
663
    block_env :
664
        The block scoped environment.
665
    target_address :
666
        Address of the contract to call.
667
    data :
668
        Data to pass to the contract.
669
670
    Returns
671
    -------
672
    system_tx_output : `MessageCallOutput`
673
        Output of processing the system transaction.
674
675
    """
676
    system_contract_code = get_account(block_env.state, target_address).code
677
678
    if len(system_contract_code) == 0:
679
        raise InvalidBlock(
680
            f"System contract address {target_address.hex()} does not "
681
            "contain code"
682
        )
683
684
    system_tx_output = process_system_transaction(
685
        block_env,
686
        target_address,
687
        system_contract_code,
688
        data,
689
    )
690
691
    if system_tx_output.error:
692
        raise InvalidBlock(
693
            f"System contract ({target_address.hex()}) call failed: "
694
            f"{system_tx_output.error}"
695
        )
696
697
    return system_tx_output

process_unchecked_system_transaction

Process a system transaction without checking if the contract contains code or if the transaction fails.

Parameters

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

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

def process_unchecked_system_transaction(block_env: ethereum.forks.prague.vm.BlockEnvironmentethereum.forks.osaka.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
705
    """
706
    Process a system transaction without checking if the contract contains code
707
    or if the transaction fails.
708
709
    Parameters
710
    ----------
711
    block_env :
712
        The block scoped environment.
713
    target_address :
714
        Address of the contract to call.
715
    data :
716
        Data to pass to the contract.
717
718
    Returns
719
    -------
720
    system_tx_output : `MessageCallOutput`
721
        Output of processing the system transaction.
722
723
    """
724
    system_contract_code = get_account(block_env.state, target_address).code
725
    return process_system_transaction(
726
        block_env,
727
        target_address,
728
        system_contract_code,
729
        data,
730
    )

apply_body

Executes a block.

Many of the contents of a block are stored in data structures called tries. There is a transactions trie which is similar to a ledger of the transactions stored in the current block. There is also a receipts trie which stores the results of executing a transaction, like the post state and gas used. This function creates and executes the block that is to be added to the chain.

Parameters

block_env : The block scoped environment. transactions : Transactions included in the block. withdrawals : Withdrawals to be processed in the current block.

Returns

block_output : The block output for the current block.

def apply_body(block_env: ethereum.forks.prague.vm.BlockEnvironmentethereum.forks.osaka.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.forks.prague.vm.BlockOutputethereum.forks.osaka.vm.BlockOutput:
738
    """
739
    Executes a block.
740
741
    Many of the contents of a block are stored in data structures called
742
    tries. There is a transactions trie which is similar to a ledger of the
743
    transactions stored in the current block. There is also a receipts trie
744
    which stores the results of executing a transaction, like the post state
745
    and gas used. This function creates and executes the block that is to be
746
    added to the chain.
747
748
    Parameters
749
    ----------
750
    block_env :
751
        The block scoped environment.
752
    transactions :
753
        Transactions included in the block.
754
    withdrawals :
755
        Withdrawals to be processed in the current block.
756
757
    Returns
758
    -------
759
    block_output :
760
        The block output for the current block.
761
762
    """
763
    block_output = vm.BlockOutput()
764
765
    process_unchecked_system_transaction(
766
        block_env=block_env,
767
        target_address=BEACON_ROOTS_ADDRESS,
768
        data=block_env.parent_beacon_block_root,
769
    )
770
771
    process_unchecked_system_transaction(
772
        block_env=block_env,
773
        target_address=HISTORY_STORAGE_ADDRESS,
774
        data=block_env.block_hashes[-1],  # The parent hash
775
    )
776
777
    for i, tx in enumerate(map(decode_transaction, transactions)):
778
        process_transaction(block_env, block_output, tx, Uint(i))
779
780
    process_withdrawals(block_env, block_output, withdrawals)
781
782
    process_general_purpose_requests(
783
        block_env=block_env,
784
        block_output=block_output,
785
    )
786
787
    return block_output

process_general_purpose_requests

Process all the requests in the block.

Parameters

block_env : The execution environment for the Block. block_output : The block output for the current block.

def process_general_purpose_requests(block_env: ethereum.forks.prague.vm.BlockEnvironmentethereum.forks.osaka.vm.BlockEnvironment, ​​block_output: ethereum.forks.prague.vm.BlockOutputethereum.forks.osaka.vm.BlockOutput) -> None:
794
    """
795
    Process all the requests in the block.
796
797
    Parameters
798
    ----------
799
    block_env :
800
        The execution environment for the Block.
801
    block_output :
802
        The block output for the current block.
803
804
    """
805
    # Requests are to be in ascending order of request type
806
    deposit_requests = parse_deposit_requests(block_output)
807
    requests_from_execution = block_output.requests
808
    if len(deposit_requests) > 0:
809
        requests_from_execution.append(DEPOSIT_REQUEST_TYPE + deposit_requests)
810
811
    system_withdrawal_tx_output = process_checked_system_transaction(
812
        block_env=block_env,
813
        target_address=WITHDRAWAL_REQUEST_PREDEPLOY_ADDRESS,
814
        data=b"",
815
    )
816
817
    if len(system_withdrawal_tx_output.return_data) > 0:
818
        requests_from_execution.append(
819
            WITHDRAWAL_REQUEST_TYPE + system_withdrawal_tx_output.return_data
820
        )
821
822
    system_consolidation_tx_output = process_checked_system_transaction(
823
        block_env=block_env,
824
        target_address=CONSOLIDATION_REQUEST_PREDEPLOY_ADDRESS,
825
        data=b"",
826
    )
827
828
    if len(system_consolidation_tx_output.return_data) > 0:
829
        requests_from_execution.append(
830
            CONSOLIDATION_REQUEST_TYPE
831
            + system_consolidation_tx_output.return_data
832
        )

process_transaction

Execute a transaction against the provided environment.

This function processes the actions needed to execute a transaction. It decrements the sender's account balance after calculating the gas fee and refunds them the proper amount after execution. Calling contracts, deploying code, and incrementing nonces are all examples of actions that happen within this function or from a call made within this function.

Accounts that are marked for deletion are processed and destroyed after execution.

Parameters

block_env : Environment for the Ethereum Virtual Machine. block_output : The block output for the current block. tx : Transaction to execute. index: Index of the transaction in the block.

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

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.prague.vm.BlockEnvironmentethereum.forks.osaka.vm.BlockEnvironment, ​​block_output: ethereum.forks.prague.vm.BlockOutputethereum.forks.osaka.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
1005
    """
1006
    Increase the balance of the withdrawing account.
1007
    """
1008
1009
    def increase_recipient_balance(recipient: Account) -> None:
1010
        recipient.balance += wd.amount * U256(10**9)
1011
1012
    for i, wd in enumerate(withdrawals):
1013
        trie_set(
1014
            block_output.withdrawals_trie,
1015
            rlp.encode(Uint(i)),
1016
            rlp.encode(wd),
1017
        )
1018
1019
        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:
1023
    """
1024
    Validates the gas limit for a block.
1025
1026
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
1027
    quotient of the parent block's gas limit and the
1028
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
1029
    passed through as a parameter is greater than or equal to the *sum* of
1030
    the parent's gas and the adjustment delta then the limit for gas is too
1031
    high and fails this function's check. Similarly, if the limit is less
1032
    than or equal to the *difference* of the parent's gas and the adjustment
1033
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
1034
    check fails because the gas limit doesn't allow for a sufficient or
1035
    reasonable amount of gas to be used on a block.
1036
1037
    Parameters
1038
    ----------
1039
    gas_limit :
1040
        Gas limit to validate.
1041
1042
    parent_gas_limit :
1043
        Gas limit of the parent block.
1044
1045
    Returns
1046
    -------
1047
    check : `bool`
1048
        True if gas limit constraints are satisfied, False otherwise.
1049
1050
    """
1051
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
1052
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
1053
        return False
1054
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
1055
        return False
1056
    if gas_limit < GAS_LIMIT_MINIMUM:
1057
        return False
1058
1059
    return True