ethereum.cancun.fork

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

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

79
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

80
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

81
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

82
GAS_LIMIT_MINIMUM = Uint(5000)

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

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:
497
    """
498
    Make the receipt for a transaction that was executed.
499
500
    Parameters
501
    ----------
502
    tx :
503
        The executed transaction.
504
    error :
505
        Error in the top level frame of the transaction, if any.
506
    cumulative_gas_used :
507
        The total gas used so far in the block after the transaction was
508
        executed.
509
    logs :
510
        The logs produced by the transaction.
511
512
    Returns
513
    -------
514
    receipt :
515
        The receipt for the transaction.
516
    """
517
    receipt = Receipt(
518
        succeeded=error is None,
519
        cumulative_gas_used=cumulative_gas_used,
520
        bloom=logs_bloom(logs),
521
        logs=logs,
522
    )
523
524
    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.cancun.vm.BlockEnvironment, ​​target_address: Address, ​​system_contract_code: Bytes, ​​data: Bytes) -> MessageCallOutput:
533
    """
534
    Process a system transaction with the given code.
535
536
    Prefer calling `process_unchecked_system_transaction` unless the contract
537
    code has already been read from the state.
538
539
    Parameters
540
    ----------
541
    block_env :
542
        The block scoped environment.
543
    target_address :
544
        Address of the contract to call.
545
    system_contract_code :
546
        Code of the contract to call.
547
    data :
548
        Data to pass to the contract.
549
550
    Returns
551
    -------
552
    system_tx_output : `MessageCallOutput`
553
        Output of processing the system transaction.
554
    """
555
    tx_env = vm.TransactionEnvironment(
556
        origin=SYSTEM_ADDRESS,
557
        gas_price=block_env.base_fee_per_gas,
558
        gas=SYSTEM_TRANSACTION_GAS,
559
        access_list_addresses=set(),
560
        access_list_storage_keys=set(),
561
        transient_storage=TransientStorage(),
562
        blob_versioned_hashes=(),
563
        index_in_block=None,
564
        tx_hash=None,
565
    )
566
567
    system_tx_message = Message(
568
        block_env=block_env,
569
        tx_env=tx_env,
570
        caller=SYSTEM_ADDRESS,
571
        target=target_address,
572
        gas=SYSTEM_TRANSACTION_GAS,
573
        value=U256(0),
574
        data=data,
575
        code=system_contract_code,
576
        depth=Uint(0),
577
        current_target=target_address,
578
        code_address=target_address,
579
        should_transfer_value=False,
580
        is_static=False,
581
        accessed_addresses=set(),
582
        accessed_storage_keys=set(),
583
        parent_evm=None,
584
    )
585
586
    system_tx_output = process_message_call(system_tx_message)
587
588
    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.cancun.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
596
    """
597
    Process a system transaction without checking if the contract contains code
598
    or if the transaction fails.
599
600
    Parameters
601
    ----------
602
    block_env :
603
        The block scoped environment.
604
    target_address :
605
        Address of the contract to call.
606
    data :
607
        Data to pass to the contract.
608
609
    Returns
610
    -------
611
    system_tx_output : `MessageCallOutput`
612
        Output of processing the system transaction.
613
    """
614
    system_contract_code = get_account(block_env.state, target_address).code
615
    return process_system_transaction(
616
        block_env,
617
        target_address,
618
        system_contract_code,
619
        data,
620
    )

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.cancun.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.cancun.vm.BlockOutput:
628
    """
629
    Executes a block.
630
631
    Many of the contents of a block are stored in data structures called
632
    tries. There is a transactions trie which is similar to a ledger of the
633
    transactions stored in the current block. There is also a receipts trie
634
    which stores the results of executing a transaction, like the post state
635
    and gas used. This function creates and executes the block that is to be
636
    added to the chain.
637
638
    Parameters
639
    ----------
640
    block_env :
641
        The block scoped environment.
642
    transactions :
643
        Transactions included in the block.
644
    withdrawals :
645
        Withdrawals to be processed in the current block.
646
647
    Returns
648
    -------
649
    block_output :
650
        The block output for the current block.
651
    """
652
    block_output = vm.BlockOutput()
653
654
    process_unchecked_system_transaction(
655
        block_env=block_env,
656
        target_address=BEACON_ROOTS_ADDRESS,
657
        data=block_env.parent_beacon_block_root,
658
    )
659
660
    for i, tx in enumerate(map(decode_transaction, transactions)):
661
        process_transaction(block_env, block_output, tx, Uint(i))
662
663
    process_withdrawals(block_env, block_output, withdrawals)
664
665
    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.cancun.vm.BlockEnvironment, ​​block_output: ethereum.cancun.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
674
    """
675
    Execute a transaction against the provided environment.
676
677
    This function processes the actions needed to execute a transaction.
678
    It decrements the sender's account balance after calculating the gas fee
679
    and refunds them the proper amount after execution. Calling contracts,
680
    deploying code, and incrementing nonces are all examples of actions that
681
    happen within this function or from a call made within this function.
682
683
    Accounts that are marked for deletion are processed and destroyed after
684
    execution.
685
686
    Parameters
687
    ----------
688
    block_env :
689
        Environment for the Ethereum Virtual Machine.
690
    block_output :
691
        The block output for the current block.
692
    tx :
693
        Transaction to execute.
694
    index:
695
        Index of the transaction in the block.
696
    """
697
    trie_set(
698
        block_output.transactions_trie,
699
        rlp.encode(index),
700
        encode_transaction(tx),
701
    )
702
703
    intrinsic_gas = validate_transaction(tx)
704
705
    (
706
        sender,
707
        effective_gas_price,
708
        blob_versioned_hashes,
709
        tx_blob_gas_used,
710
    ) = check_transaction(
711
        block_env=block_env,
712
        block_output=block_output,
713
        tx=tx,
714
    )
715
716
    sender_account = get_account(block_env.state, sender)
717
718
    if isinstance(tx, BlobTransaction):
719
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
720
    else:
721
        blob_gas_fee = Uint(0)
722
723
    effective_gas_fee = tx.gas * effective_gas_price
724
725
    gas = tx.gas - intrinsic_gas
726
    increment_nonce(block_env.state, sender)
727
728
    sender_balance_after_gas_fee = (
729
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
730
    )
731
    set_account_balance(
732
        block_env.state, sender, U256(sender_balance_after_gas_fee)
733
    )
734
735
    access_list_addresses = set()
736
    access_list_storage_keys = set()
737
    access_list_addresses.add(block_env.coinbase)
738
    if isinstance(
739
        tx, (AccessListTransaction, FeeMarketTransaction, BlobTransaction)
740
    ):
741
        for access in tx.access_list:
742
            access_list_addresses.add(access.account)
743
            for slot in access.slots:
744
                access_list_storage_keys.add((access.account, slot))
745
746
    tx_env = vm.TransactionEnvironment(
747
        origin=sender,
748
        gas_price=effective_gas_price,
749
        gas=gas,
750
        access_list_addresses=access_list_addresses,
751
        access_list_storage_keys=access_list_storage_keys,
752
        transient_storage=TransientStorage(),
753
        blob_versioned_hashes=blob_versioned_hashes,
754
        index_in_block=index,
755
        tx_hash=get_transaction_hash(encode_transaction(tx)),
756
    )
757
758
    message = prepare_message(block_env, tx_env, tx)
759
760
    tx_output = process_message_call(message)
761
762
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
763
    tx_gas_refund = min(
764
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
765
    )
766
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
767
    tx_gas_left = tx.gas - tx_gas_used_after_refund
768
    gas_refund_amount = tx_gas_left * effective_gas_price
769
770
    # For non-1559 transactions effective_gas_price == tx.gas_price
771
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
772
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
773
774
    # refund gas
775
    sender_balance_after_refund = get_account(
776
        block_env.state, sender
777
    ).balance + U256(gas_refund_amount)
778
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
779
780
    # transfer miner fees
781
    coinbase_balance_after_mining_fee = get_account(
782
        block_env.state, block_env.coinbase
783
    ).balance + U256(transaction_fee)
784
    set_account_balance(
785
        block_env.state,
786
        block_env.coinbase,
787
        coinbase_balance_after_mining_fee,
788
    )
789
790
    for address in tx_output.accounts_to_delete:
791
        destroy_account(block_env.state, address)
792
793
    block_output.block_gas_used += tx_gas_used_after_refund
794
    block_output.blob_gas_used += tx_blob_gas_used
795
796
    receipt = make_receipt(
797
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
798
    )
799
800
    receipt_key = rlp.encode(Uint(index))
801
    block_output.receipt_keys += (receipt_key,)
802
803
    trie_set(
804
        block_output.receipts_trie,
805
        receipt_key,
806
        receipt,
807
    )
808
809
    block_output.block_logs += tx_output.logs

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.cancun.vm.BlockEnvironment, ​​block_output: ethereum.cancun.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
817
    """
818
    Increase the balance of the withdrawing account.
819
    """
820
821
    def increase_recipient_balance(recipient: Account) -> None:
822
        recipient.balance += wd.amount * U256(10**9)
823
824
    for i, wd in enumerate(withdrawals):
825
        trie_set(
826
            block_output.withdrawals_trie,
827
            rlp.encode(Uint(i)),
828
            rlp.encode(wd),
829
        )
830
831
        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:
835
    """
836
    Validates the gas limit for a block.
837
838
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
839
    quotient of the parent block's gas limit and the
840
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
841
    passed through as a parameter is greater than or equal to the *sum* of
842
    the parent's gas and the adjustment delta then the limit for gas is too
843
    high and fails this function's check. Similarly, if the limit is less
844
    than or equal to the *difference* of the parent's gas and the adjustment
845
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
846
    check fails because the gas limit doesn't allow for a sufficient or
847
    reasonable amount of gas to be used on a block.
848
849
    Parameters
850
    ----------
851
    gas_limit :
852
        Gas limit to validate.
853
854
    parent_gas_limit :
855
        Gas limit of the parent block.
856
857
    Returns
858
    -------
859
    check : `bool`
860
        True if gas limit constraints are satisfied, False otherwise.
861
    """
862
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
863
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
864
        return False
865
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
866
        return False
867
    if gas_limit < GAS_LIMIT_MINIMUM:
868
        return False
869
870
    return True