ethereum.forks.cancun.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

81
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

82
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

84
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

85
BEACON_ROOTS_ADDRESS = hex_to_address(
86
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
87
)

SYSTEM_TRANSACTION_GAS

88
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

89
MAX_BLOB_GAS_PER_BLOCK = U64(786432)

VERSIONED_HASH_VERSION_KZG

90
VERSIONED_HASH_VERSION_KZG = b"\x01"

BlockChain

History and current state of the block chain.

93
@dataclass
class BlockChain:

blocks

99
    blocks: List[Block]

state

100
    state: State

chain_id

101
    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:
105
    """
106
    Transforms the state from the previous hard fork (`old`) into the block
107
    chain object for this hard fork and returns it.
108
109
    When forks need to implement an irregular state transition, this function
110
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
111
    an example.
112
113
    Parameters
114
    ----------
115
    old :
116
        Previous block chain object.
117
118
    Returns
119
    -------
120
    new : `BlockChain`
121
        Upgraded block chain object for this hard fork.
122
123
    """
124
    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]:
128
    """
129
    Obtain the list of hashes of the previous 256 blocks in order of
130
    increasing block number.
131
132
    This function will return less hashes for the first 256 blocks.
133
134
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
135
    therefore this function retrieves them.
136
137
    Parameters
138
    ----------
139
    chain :
140
        History and current state.
141
142
    Returns
143
    -------
144
    recent_block_hashes : `List[Hash32]`
145
        Hashes of the recent 256 blocks in order of increasing block number.
146
147
    """
148
    recent_blocks = chain.blocks[-255:]
149
    # TODO: This function has not been tested rigorously
150
    if len(recent_blocks) == 0:
151
        return []
152
153
    recent_block_hashes = []
154
155
    for block in recent_blocks:
156
        prev_block_hash = block.header.parent_hash
157
        recent_block_hashes.append(prev_block_hash)
158
159
    # We are computing the hash only for the most recent block and not for
160
    # the rest of the blocks as they have successors which have the hash of
161
    # the current block as parent hash.
162
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
163
    recent_block_hashes.append(most_recent_block_hash)
164
165
    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:
169
    """
170
    Attempts to apply a block to an existing block chain.
171
172
    All parts of the block's contents need to be verified before being added
173
    to the chain. Blocks are verified by ensuring that the contents of the
174
    block make logical sense with the contents of the parent block. The
175
    information in the block's header must also match the corresponding
176
    information in the block.
177
178
    To implement Ethereum, in theory clients are only required to store the
179
    most recent 255 blocks of the chain since as far as execution is
180
    concerned, only those blocks are accessed. Practically, however, clients
181
    should store more blocks to handle reorgs.
182
183
    Parameters
184
    ----------
185
    chain :
186
        History and current state.
187
    block :
188
        Block to apply to `chain`.
189
190
    """
191
    validate_header(chain, block.header)
192
    if block.ommers != ():
193
        raise InvalidBlock
194
195
    block_env = vm.BlockEnvironment(
196
        chain_id=chain.chain_id,
197
        state=chain.state,
198
        block_gas_limit=block.header.gas_limit,
199
        block_hashes=get_last_256_block_hashes(chain),
200
        coinbase=block.header.coinbase,
201
        number=block.header.number,
202
        base_fee_per_gas=block.header.base_fee_per_gas,
203
        time=block.header.timestamp,
204
        prev_randao=block.header.prev_randao,
205
        excess_blob_gas=block.header.excess_blob_gas,
206
        parent_beacon_block_root=block.header.parent_beacon_block_root,
207
    )
208
209
    block_output = apply_body(
210
        block_env=block_env,
211
        transactions=block.transactions,
212
        withdrawals=block.withdrawals,
213
    )
214
    block_state_root = state_root(block_env.state)
215
    transactions_root = root(block_output.transactions_trie)
216
    receipt_root = root(block_output.receipts_trie)
217
    block_logs_bloom = logs_bloom(block_output.block_logs)
218
    withdrawals_root = root(block_output.withdrawals_trie)
219
220
    if block_output.block_gas_used != block.header.gas_used:
221
        raise InvalidBlock(
222
            f"{block_output.block_gas_used} != {block.header.gas_used}"
223
        )
224
    if transactions_root != block.header.transactions_root:
225
        raise InvalidBlock
226
    if block_state_root != block.header.state_root:
227
        raise InvalidBlock
228
    if receipt_root != block.header.receipt_root:
229
        raise InvalidBlock
230
    if block_logs_bloom != block.header.bloom:
231
        raise InvalidBlock
232
    if withdrawals_root != block.header.withdrawals_root:
233
        raise InvalidBlock
234
    if block_output.blob_gas_used != block.header.blob_gas_used:
235
        raise InvalidBlock
236
237
    chain.blocks.append(block)
238
    if len(chain.blocks) > 255:
239
        # Real clients have to store more blocks to deal with reorgs, but the
240
        # protocol only requires the last 255
241
        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:
250
    """
251
    Calculates the base fee per gas for the block.
252
253
    Parameters
254
    ----------
255
    block_gas_limit :
256
        Gas limit of the block for which the base fee is being calculated.
257
    parent_gas_limit :
258
        Gas limit of the parent block.
259
    parent_gas_used :
260
        Gas used in the parent block.
261
    parent_base_fee_per_gas :
262
        Base fee per gas of the parent block.
263
264
    Returns
265
    -------
266
    base_fee_per_gas : `Uint`
267
        Base fee per gas for the block.
268
269
    """
270
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
271
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
272
        raise InvalidBlock
273
274
    if parent_gas_used == parent_gas_target:
275
        expected_base_fee_per_gas = parent_base_fee_per_gas
276
    elif parent_gas_used > parent_gas_target:
277
        gas_used_delta = parent_gas_used - parent_gas_target
278
279
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
280
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
281
282
        base_fee_per_gas_delta = max(
283
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
284
            Uint(1),
285
        )
286
287
        expected_base_fee_per_gas = (
288
            parent_base_fee_per_gas + base_fee_per_gas_delta
289
        )
290
    else:
291
        gas_used_delta = parent_gas_target - parent_gas_used
292
293
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
294
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
295
296
        base_fee_per_gas_delta = (
297
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
298
        )
299
300
        expected_base_fee_per_gas = (
301
            parent_base_fee_per_gas - base_fee_per_gas_delta
302
        )
303
304
    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:
308
    """
309
    Verifies a block header.
310
311
    In order to consider a block's header valid, the logic for the
312
    quantities in the header should match the logic for the block itself.
313
    For example the header timestamp should be greater than the block's parent
314
    timestamp because the block was created *after* the parent block.
315
    Additionally, the block's number should be directly following the parent
316
    block's number since it is the next block in the sequence.
317
318
    Parameters
319
    ----------
320
    chain :
321
        History and current state.
322
    header :
323
        Header to check for correctness.
324
325
    """
326
    if header.number < Uint(1):
327
        raise InvalidBlock
328
329
    parent_header = chain.blocks[-1].header
330
331
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
332
    if header.excess_blob_gas != excess_blob_gas:
333
        raise InvalidBlock
334
335
    if header.gas_used > header.gas_limit:
336
        raise InvalidBlock
337
338
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
339
        header.gas_limit,
340
        parent_header.gas_limit,
341
        parent_header.gas_used,
342
        parent_header.base_fee_per_gas,
343
    )
344
    if expected_base_fee_per_gas != header.base_fee_per_gas:
345
        raise InvalidBlock
346
    if header.timestamp <= parent_header.timestamp:
347
        raise InvalidBlock
348
    if header.number != parent_header.number + Uint(1):
349
        raise InvalidBlock
350
    if len(header.extra_data) > 32:
351
        raise InvalidBlock
352
    if header.difficulty != 0:
353
        raise InvalidBlock
354
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
355
        raise InvalidBlock
356
    if header.ommers_hash != EMPTY_OMMER_HASH:
357
        raise InvalidBlock
358
359
    block_parent_hash = keccak256(rlp.encode(parent_header))
360
    if header.parent_hash != block_parent_hash:
361
        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. TransactionTypeContractCreationError: If the transaction type is not allowed to create contracts.

def check_transaction(block_env: ethereum.forks.cancun.vm.BlockEnvironment, ​​block_output: ethereum.forks.cancun.vm.BlockOutput, ​​tx: Transaction) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
369
    """
370
    Check if the transaction is includable in the block.
371
372
    Parameters
373
    ----------
374
    block_env :
375
        The block scoped environment.
376
    block_output :
377
        The block output for the current block.
378
    tx :
379
        The transaction.
380
381
    Returns
382
    -------
383
    sender_address :
384
        The sender of the transaction.
385
    effective_gas_price :
386
        The price to charge for gas when the transaction is executed.
387
    blob_versioned_hashes :
388
        The blob versioned hashes of the transaction.
389
    tx_blob_gas_used:
390
        The blob gas used by the transaction.
391
392
    Raises
393
    ------
394
    InvalidBlock :
395
        If the transaction is not includable.
396
    GasUsedExceedsLimitError :
397
        If the gas used by the transaction exceeds the block's gas limit.
398
    NonceMismatchError :
399
        If the nonce of the transaction is not equal to the sender's nonce.
400
    InsufficientBalanceError :
401
        If the sender's balance is not enough to pay for the transaction.
402
    InvalidSenderError :
403
        If the transaction is from an address that does not exist anymore.
404
    PriorityFeeGreaterThanMaxFeeError :
405
        If the priority fee is greater than the maximum fee per gas.
406
    InsufficientMaxFeePerGasError :
407
        If the maximum fee per gas is insufficient for the transaction.
408
    InsufficientMaxFeePerBlobGasError :
409
        If the maximum fee per blob gas is insufficient for the transaction.
410
    BlobGasLimitExceededError :
411
        If the blob gas used by the transaction exceeds the block's blob gas
412
        limit.
413
    InvalidBlobVersionedHashError :
414
        If the transaction contains a blob versioned hash with an invalid
415
        version.
416
    NoBlobDataError :
417
        If the transaction is a type 3 but has no blobs.
418
    TransactionTypeContractCreationError:
419
        If the transaction type is not allowed to create contracts.
420
421
    """
422
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
423
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
424
425
    if tx.gas > gas_available:
426
        raise GasUsedExceedsLimitError("gas used exceeds limit")
427
428
    tx_blob_gas_used = calculate_total_blob_gas(tx)
429
    if tx_blob_gas_used > blob_gas_available:
430
        raise BlobGasLimitExceededError("blob gas limit exceeded")
431
432
    sender_address = recover_sender(block_env.chain_id, tx)
433
    sender_account = get_account(block_env.state, sender_address)
434
435
    if isinstance(tx, (FeeMarketTransaction, BlobTransaction)):
436
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
437
            raise PriorityFeeGreaterThanMaxFeeError(
438
                "priority fee greater than max fee"
439
            )
440
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
441
            raise InsufficientMaxFeePerGasError(
442
                tx.max_fee_per_gas, block_env.base_fee_per_gas
443
            )
444
445
        priority_fee_per_gas = min(
446
            tx.max_priority_fee_per_gas,
447
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
448
        )
449
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
450
        max_gas_fee = tx.gas * tx.max_fee_per_gas
451
    else:
452
        if tx.gas_price < block_env.base_fee_per_gas:
453
            raise InvalidBlock
454
        effective_gas_price = tx.gas_price
455
        max_gas_fee = tx.gas * tx.gas_price
456
457
    if isinstance(tx, BlobTransaction):
458
        if not isinstance(tx.to, Address):
459
            raise TransactionTypeContractCreationError(tx)
460
        if len(tx.blob_versioned_hashes) == 0:
461
            raise NoBlobDataError("no blob data in transaction")
462
        for blob_versioned_hash in tx.blob_versioned_hashes:
463
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
464
                raise InvalidBlobVersionedHashError(
465
                    "invalid blob versioned hash"
466
                )
467
468
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
469
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
470
            raise InsufficientMaxFeePerBlobGasError(
471
                "insufficient max fee per blob gas"
472
            )
473
474
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
475
            tx.max_fee_per_blob_gas
476
        )
477
        blob_versioned_hashes = tx.blob_versioned_hashes
478
    else:
479
        blob_versioned_hashes = ()
480
    if sender_account.nonce > Uint(tx.nonce):
481
        raise NonceMismatchError("nonce too low")
482
    elif sender_account.nonce < Uint(tx.nonce):
483
        raise NonceMismatchError("nonce too high")
484
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
485
        raise InsufficientBalanceError("insufficient sender balance")
486
    if sender_account.code_hash != EMPTY_CODE_HASH:
487
        raise InvalidSenderError("not EOA")
488
489
    return (
490
        sender_address,
491
        effective_gas_price,
492
        blob_versioned_hashes,
493
        tx_blob_gas_used,
494
    )

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:
503
    """
504
    Make the receipt for a transaction that was executed.
505
506
    Parameters
507
    ----------
508
    tx :
509
        The executed transaction.
510
    error :
511
        Error in the top level frame of the transaction, if any.
512
    cumulative_gas_used :
513
        The total gas used so far in the block after the transaction was
514
        executed.
515
    logs :
516
        The logs produced by the transaction.
517
518
    Returns
519
    -------
520
    receipt :
521
        The receipt for the transaction.
522
523
    """
524
    receipt = Receipt(
525
        succeeded=error is None,
526
        cumulative_gas_used=cumulative_gas_used,
527
        bloom=logs_bloom(logs),
528
        logs=logs,
529
    )
530
531
    return encode_receipt(tx, receipt)

process_system_transaction

Process a system transaction with the given code.

Prefer calling process_unchecked_system_transaction unless the contract code has already been read from the state.

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.cancun.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
540
    """
541
    Process a system transaction with the given code.
542
543
    Prefer calling `process_unchecked_system_transaction` unless the contract
544
    code has already been read from the state.
545
546
    Parameters
547
    ----------
548
    block_env :
549
        The block scoped environment.
550
    target_address :
551
        Address of the contract to call.
552
    system_contract_code :
553
        Code of the contract to call.
554
    data :
555
        Data to pass to the contract.
556
557
    Returns
558
    -------
559
    system_tx_output : `MessageCallOutput`
560
        Output of processing the system transaction.
561
562
    """
563
    tx_env = vm.TransactionEnvironment(
564
        origin=SYSTEM_ADDRESS,
565
        gas_price=block_env.base_fee_per_gas,
566
        gas=SYSTEM_TRANSACTION_GAS,
567
        access_list_addresses=set(),
568
        access_list_storage_keys=set(),
569
        transient_storage=TransientStorage(),
570
        blob_versioned_hashes=(),
571
        index_in_block=None,
572
        tx_hash=None,
573
    )
574
575
    system_tx_message = Message(
576
        block_env=block_env,
577
        tx_env=tx_env,
578
        caller=SYSTEM_ADDRESS,
579
        target=target_address,
580
        gas=SYSTEM_TRANSACTION_GAS,
581
        value=U256(0),
582
        data=data,
583
        code=system_contract_code,
584
        depth=Uint(0),
585
        current_target=target_address,
586
        code_address=target_address,
587
        should_transfer_value=False,
588
        is_static=False,
589
        accessed_addresses=set(),
590
        accessed_storage_keys=set(),
591
        parent_evm=None,
592
    )
593
594
    system_tx_output = process_message_call(system_tx_message)
595
596
    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.cancun.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
604
    """
605
    Process a system transaction without checking if the contract contains code
606
    or if the transaction fails.
607
608
    Parameters
609
    ----------
610
    block_env :
611
        The block scoped environment.
612
    target_address :
613
        Address of the contract to call.
614
    data :
615
        Data to pass to the contract.
616
617
    Returns
618
    -------
619
    system_tx_output : `MessageCallOutput`
620
        Output of processing the system transaction.
621
622
    """
623
    system_contract_code = get_code(
624
        block_env.state,
625
        get_account(block_env.state, target_address).code_hash,
626
    )
627
    return process_system_transaction(
628
        block_env,
629
        target_address,
630
        system_contract_code,
631
        data,
632
    )

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.cancun.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.forks.cancun.vm.BlockOutput:
640
    """
641
    Executes a block.
642
643
    Many of the contents of a block are stored in data structures called
644
    tries. There is a transactions trie which is similar to a ledger of the
645
    transactions stored in the current block. There is also a receipts trie
646
    which stores the results of executing a transaction, like the post state
647
    and gas used. This function creates and executes the block that is to be
648
    added to the chain.
649
650
    Parameters
651
    ----------
652
    block_env :
653
        The block scoped environment.
654
    transactions :
655
        Transactions included in the block.
656
    withdrawals :
657
        Withdrawals to be processed in the current block.
658
659
    Returns
660
    -------
661
    block_output :
662
        The block output for the current block.
663
664
    """
665
    block_output = vm.BlockOutput()
666
667
    process_unchecked_system_transaction(
668
        block_env=block_env,
669
        target_address=BEACON_ROOTS_ADDRESS,
670
        data=block_env.parent_beacon_block_root,
671
    )
672
673
    for i, tx in enumerate(map(decode_transaction, transactions)):
674
        process_transaction(block_env, block_output, tx, Uint(i))
675
676
    process_withdrawals(block_env, block_output, withdrawals)
677
678
    return block_output

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.cancun.vm.BlockEnvironment, ​​block_output: ethereum.forks.cancun.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
687
    """
688
    Execute a transaction against the provided environment.
689
690
    This function processes the actions needed to execute a transaction.
691
    It decrements the sender's account balance after calculating the gas fee
692
    and refunds them the proper amount after execution. Calling contracts,
693
    deploying code, and incrementing nonces are all examples of actions that
694
    happen within this function or from a call made within this function.
695
696
    Accounts that are marked for deletion are processed and destroyed after
697
    execution.
698
699
    Parameters
700
    ----------
701
    block_env :
702
        Environment for the Ethereum Virtual Machine.
703
    block_output :
704
        The block output for the current block.
705
    tx :
706
        Transaction to execute.
707
    index:
708
        Index of the transaction in the block.
709
710
    """
711
    trie_set(
712
        block_output.transactions_trie,
713
        rlp.encode(index),
714
        encode_transaction(tx),
715
    )
716
717
    intrinsic_gas = validate_transaction(tx)
718
719
    (
720
        sender,
721
        effective_gas_price,
722
        blob_versioned_hashes,
723
        tx_blob_gas_used,
724
    ) = check_transaction(
725
        block_env=block_env,
726
        block_output=block_output,
727
        tx=tx,
728
    )
729
730
    sender_account = get_account(block_env.state, sender)
731
732
    if isinstance(tx, BlobTransaction):
733
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
734
    else:
735
        blob_gas_fee = Uint(0)
736
737
    effective_gas_fee = tx.gas * effective_gas_price
738
739
    gas = tx.gas - intrinsic_gas
740
    increment_nonce(block_env.state, sender)
741
742
    sender_balance_after_gas_fee = (
743
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
744
    )
745
    set_account_balance(
746
        block_env.state, sender, U256(sender_balance_after_gas_fee)
747
    )
748
749
    access_list_addresses = set()
750
    access_list_storage_keys = set()
751
    access_list_addresses.add(block_env.coinbase)
752
    if isinstance(
753
        tx, (AccessListTransaction, FeeMarketTransaction, BlobTransaction)
754
    ):
755
        for access in tx.access_list:
756
            access_list_addresses.add(access.account)
757
            for slot in access.slots:
758
                access_list_storage_keys.add((access.account, slot))
759
760
    tx_env = vm.TransactionEnvironment(
761
        origin=sender,
762
        gas_price=effective_gas_price,
763
        gas=gas,
764
        access_list_addresses=access_list_addresses,
765
        access_list_storage_keys=access_list_storage_keys,
766
        transient_storage=TransientStorage(),
767
        blob_versioned_hashes=blob_versioned_hashes,
768
        index_in_block=index,
769
        tx_hash=get_transaction_hash(encode_transaction(tx)),
770
    )
771
772
    message = prepare_message(block_env, tx_env, tx)
773
774
    tx_output = process_message_call(message)
775
776
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
777
    tx_gas_refund = min(
778
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
779
    )
780
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
781
    tx_gas_left = tx.gas - tx_gas_used_after_refund
782
    gas_refund_amount = tx_gas_left * effective_gas_price
783
784
    # For non-1559 transactions effective_gas_price == tx.gas_price
785
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
786
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
787
788
    # refund gas
789
    sender_balance_after_refund = get_account(
790
        block_env.state, sender
791
    ).balance + U256(gas_refund_amount)
792
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
793
794
    # transfer miner fees
795
    coinbase_balance_after_mining_fee = get_account(
796
        block_env.state, block_env.coinbase
797
    ).balance + U256(transaction_fee)
798
    set_account_balance(
799
        block_env.state,
800
        block_env.coinbase,
801
        coinbase_balance_after_mining_fee,
802
    )
803
804
    for address in tx_output.accounts_to_delete:
805
        destroy_account(block_env.state, address)
806
807
    block_output.block_gas_used += tx_gas_used_after_refund
808
    block_output.blob_gas_used += tx_blob_gas_used
809
810
    receipt = make_receipt(
811
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
812
    )
813
814
    receipt_key = rlp.encode(Uint(index))
815
    block_output.receipt_keys += (receipt_key,)
816
817
    trie_set(
818
        block_output.receipts_trie,
819
        receipt_key,
820
        receipt,
821
    )
822
823
    block_output.block_logs += tx_output.logs

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.cancun.vm.BlockEnvironment, ​​block_output: ethereum.forks.cancun.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
831
    """
832
    Increase the balance of the withdrawing account.
833
    """
834
835
    def increase_recipient_balance(recipient: Account) -> None:
836
        recipient.balance += wd.amount * U256(10**9)
837
838
    for i, wd in enumerate(withdrawals):
839
        trie_set(
840
            block_output.withdrawals_trie,
841
            rlp.encode(Uint(i)),
842
            rlp.encode(wd),
843
        )
844
845
        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 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 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:
849
    """
850
    Validates the gas limit for a block.
851
852
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
853
    quotient of the parent block's gas limit and the
854
    ``LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
855
    passed through as a parameter is greater than or equal to the *sum* of
856
    the parent's gas and the adjustment delta then the limit for gas is too
857
    high and fails this function's check. Similarly, if the limit is less
858
    than or equal to the *difference* of the parent's gas and the adjustment
859
    delta *or* the predefined ``LIMIT_MINIMUM`` then this function's
860
    check fails because the gas limit doesn't allow for a sufficient or
861
    reasonable amount of gas to be used on a block.
862
863
    Parameters
864
    ----------
865
    gas_limit :
866
        Gas limit to validate.
867
868
    parent_gas_limit :
869
        Gas limit of the parent block.
870
871
    Returns
872
    -------
873
    check : `bool`
874
        True if gas limit constraints are satisfied, False otherwise.
875
876
    """
877
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
878
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
879
        return False
880
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
881
        return False
882
    if gas_limit < GasCosts.LIMIT_MINIMUM:
883
        return False
884
885
    return True