ethereum.shanghai.forkethereum.cancun.fork

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

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

68
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

69
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

70
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

71
GAS_LIMIT_MINIMUM = Uint(5000)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

73
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

74
BEACON_ROOTS_ADDRESS = hex_to_address(
75
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
76
)

SYSTEM_TRANSACTION_GAS

77
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

78
MAX_BLOB_GAS_PER_BLOCK = U64(786432)

VERSIONED_HASH_VERSION_KZG

79
VERSIONED_HASH_VERSION_KZG = b"\x01"

BlockChain

History and current state of the block chain.

82
@dataclass
class BlockChain:

blocks

88
    blocks: List[Block]

state

89
    state: State

chain_id

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

def check_transaction(block_env: ethereum.shanghai.vm.BlockEnvironmentethereum.cancun.vm.BlockEnvironment, ​​block_output: ethereum.shanghai.vm.BlockOutputethereum.cancun.vm.BlockOutput, ​​tx: Transaction) -> Tuple[Address, Uint, Tuple[VersionedHash, ...]U64]:
353
    """
354
    Check if the transaction is includable in the block.
355
356
    Parameters
357
    ----------
358
    block_env :
359
        The block scoped environment.
360
    block_output :
361
        The block output for the current block.
362
    tx :
363
        The transaction.
364
365
    Returns
366
    -------
367
    sender_address :
368
        The sender of the transaction.
369
    effective_gas_price :
370
        The price to charge for gas when the transaction is executed.
371
    blob_versioned_hashes :
372
        The blob versioned hashes of the transaction.
373
    tx_blob_gas_used:
374
        The blob gas used by the transaction.
375
376
    Raises
377
    ------
378
    InvalidBlock :
379
        If the transaction is not includable.
380
    """
381
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
382
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
383
384
    if tx.gas > gas_available:
385
        raise InvalidBlock
386
387
    tx_blob_gas_used = calculate_total_blob_gas(tx)
388
    if tx_blob_gas_used > blob_gas_available:
389
        raise InvalidBlock
390
391
    sender_address = recover_sender(block_env.chain_id, tx)
392
    sender_account = get_account(block_env.state, sender_address)
393
358
    if isinstance(tx, FeeMarketTransaction):
394
    if isinstance(tx, (FeeMarketTransaction, BlobTransaction)):
395
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
396
            raise InvalidBlock
397
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
398
            raise InvalidBlock
399
400
        priority_fee_per_gas = min(
401
            tx.max_priority_fee_per_gas,
402
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
403
        )
404
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
405
        max_gas_fee = tx.gas * tx.max_fee_per_gas
406
    else:
407
        if tx.gas_price < block_env.base_fee_per_gas:
408
            raise InvalidBlock
409
        effective_gas_price = tx.gas_price
410
        max_gas_fee = tx.gas * tx.gas_price
411
412
    if isinstance(tx, BlobTransaction):
413
        if not isinstance(tx.to, Address):
414
            raise InvalidBlock
415
        if len(tx.blob_versioned_hashes) == 0:
416
            raise InvalidBlock
417
        for blob_versioned_hash in tx.blob_versioned_hashes:
418
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
419
                raise InvalidBlock
420
421
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
422
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
423
            raise InvalidBlock
424
425
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
426
            tx.max_fee_per_blob_gas
427
        )
428
        blob_versioned_hashes = tx.blob_versioned_hashes
429
    else:
430
        blob_versioned_hashes = ()
431
    if sender_account.nonce != tx.nonce:
432
        raise InvalidBlock
433
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
434
        raise InvalidBlock
435
    if sender_account.code:
436
        raise InvalidSenderError("not EOA")
437
383
    return sender_address, effective_gas_price
438
    return (
439
        sender_address,
440
        effective_gas_price,
441
        blob_versioned_hashes,
442
        tx_blob_gas_used,
443
    )

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, ...]) -> Union[Bytes, Receipt]:
452
    """
453
    Make the receipt for a transaction that was executed.
454
455
    Parameters
456
    ----------
457
    tx :
458
        The executed transaction.
459
    error :
460
        Error in the top level frame of the transaction, if any.
461
    cumulative_gas_used :
462
        The total gas used so far in the block after the transaction was
463
        executed.
464
    logs :
465
        The logs produced by the transaction.
466
467
    Returns
468
    -------
469
    receipt :
470
        The receipt for the transaction.
471
    """
472
    receipt = Receipt(
473
        succeeded=error is None,
474
        cumulative_gas_used=cumulative_gas_used,
475
        bloom=logs_bloom(logs),
476
        logs=logs,
477
    )
478
479
    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:
488
    """
489
    Process a system transaction with the given code.
490
491
    Prefer calling `process_unchecked_system_transaction` unless the contract
492
    code has already been read from the state.
493
494
    Parameters
495
    ----------
496
    block_env :
497
        The block scoped environment.
498
    target_address :
499
        Address of the contract to call.
500
    system_contract_code :
501
        Code of the contract to call.
502
    data :
503
        Data to pass to the contract.
504
505
    Returns
506
    -------
507
    system_tx_output : `MessageCallOutput`
508
        Output of processing the system transaction.
509
    """
510
    tx_env = vm.TransactionEnvironment(
511
        origin=SYSTEM_ADDRESS,
512
        gas_price=block_env.base_fee_per_gas,
513
        gas=SYSTEM_TRANSACTION_GAS,
514
        access_list_addresses=set(),
515
        access_list_storage_keys=set(),
516
        transient_storage=TransientStorage(),
517
        blob_versioned_hashes=(),
518
        index_in_block=None,
519
        tx_hash=None,
520
        traces=[],
521
    )
522
523
    system_tx_message = Message(
524
        block_env=block_env,
525
        tx_env=tx_env,
526
        caller=SYSTEM_ADDRESS,
527
        target=target_address,
528
        gas=SYSTEM_TRANSACTION_GAS,
529
        value=U256(0),
530
        data=data,
531
        code=system_contract_code,
532
        depth=Uint(0),
533
        current_target=target_address,
534
        code_address=target_address,
535
        should_transfer_value=False,
536
        is_static=False,
537
        accessed_addresses=set(),
538
        accessed_storage_keys=set(),
539
        parent_evm=None,
540
    )
541
542
    system_tx_output = process_message_call(system_tx_message)
543
544
    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:
552
    """
553
    Process a system transaction without checking if the contract contains code
554
    or if the transaction fails.
555
556
    Parameters
557
    ----------
558
    block_env :
559
        The block scoped environment.
560
    target_address :
561
        Address of the contract to call.
562
    data :
563
        Data to pass to the contract.
564
565
    Returns
566
    -------
567
    system_tx_output : `MessageCallOutput`
568
        Output of processing the system transaction.
569
    """
570
    system_contract_code = get_account(block_env.state, target_address).code
571
    return process_system_transaction(
572
        block_env,
573
        target_address,
574
        system_contract_code,
575
        data,
576
    )

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. block_output : The block output for the current block. 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.shanghai.vm.BlockEnvironmentethereum.cancun.vm.BlockEnvironment, ​​transactions: Tuple[Union[LegacyTransaction, Bytes], ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.shanghai.vm.BlockOutputethereum.cancun.vm.BlockOutput:
584
    """
585
    Executes a block.
586
587
    Many of the contents of a block are stored in data structures called
588
    tries. There is a transactions trie which is similar to a ledger of the
589
    transactions stored in the current block. There is also a receipts trie
590
    which stores the results of executing a transaction, like the post state
591
    and gas used. This function creates and executes the block that is to be
592
    added to the chain.
593
594
    Parameters
595
    ----------
596
    block_env :
597
        The block scoped environment.
441
    block_output :
442
        The block output for the current block.
598
    transactions :
599
        Transactions included in the block.
600
    withdrawals :
601
        Withdrawals to be processed in the current block.
602
603
    Returns
604
    -------
605
    block_output :
606
        The block output for the current block.
607
    """
608
    block_output = vm.BlockOutput()
609
610
    process_unchecked_system_transaction(
611
        block_env=block_env,
612
        target_address=BEACON_ROOTS_ADDRESS,
613
        data=block_env.parent_beacon_block_root,
614
    )
615
616
    for i, tx in enumerate(map(decode_transaction, transactions)):
617
        process_transaction(block_env, block_output, tx, Uint(i))
618
619
    process_withdrawals(block_env, block_output, withdrawals)
620
621
    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 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.shanghai.vm.BlockEnvironmentethereum.cancun.vm.BlockEnvironment, ​​block_output: ethereum.shanghai.vm.BlockOutputethereum.cancun.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
630
    """
631
    Execute a transaction against the provided environment.
632
633
    This function processes the actions needed to execute a transaction.
634
    It decrements the sender's account after calculating the gas fee and
635
    refunds them the proper amount after execution. Calling contracts,
636
    deploying code, and incrementing nonces are all examples of actions that
637
    happen within this function or from a call made within this function.
638
639
    Accounts that are marked for deletion are processed and destroyed after
640
    execution.
641
642
    Parameters
643
    ----------
644
    block_env :
645
        Environment for the Ethereum Virtual Machine.
646
    block_output :
647
        The block output for the current block.
648
    tx :
649
        Transaction to execute.
650
    index:
651
        Index of the transaction in the block.
652
    """
653
    trie_set(
654
        block_output.transactions_trie,
655
        rlp.encode(index),
656
        encode_transaction(tx),
657
    )
658
659
    intrinsic_gas = validate_transaction(tx)
660
661
    (
662
        sender,
502
        effective_gas_price,
663
        effective_gas_price,
664
        blob_versioned_hashes,
665
        tx_blob_gas_used,
666
    ) = check_transaction(
667
        block_env=block_env,
668
        block_output=block_output,
669
        tx=tx,
670
    )
671
672
    sender_account = get_account(block_env.state, sender)
673
674
    if isinstance(tx, BlobTransaction):
675
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
676
    else:
677
        blob_gas_fee = Uint(0)
678
679
    effective_gas_fee = tx.gas * effective_gas_price
680
681
    gas = tx.gas - intrinsic_gas
682
    increment_nonce(block_env.state, sender)
683
684
    sender_balance_after_gas_fee = (
517
        Uint(sender_account.balance) - effective_gas_fee
685
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
686
    )
687
    set_account_balance(
688
        block_env.state, sender, U256(sender_balance_after_gas_fee)
689
    )
690
691
    access_list_addresses = set()
692
    access_list_storage_keys = set()
693
    access_list_addresses.add(block_env.coinbase)
526
    if isinstance(tx, (AccessListTransaction, FeeMarketTransaction)):
694
    if isinstance(
695
        tx, (AccessListTransaction, FeeMarketTransaction, BlobTransaction)
696
    ):
697
        for access in tx.access_list:
698
            access_list_addresses.add(access.account)
699
            for slot in access.slots:
700
                access_list_storage_keys.add((access.account, slot))
701
702
    tx_env = vm.TransactionEnvironment(
703
        origin=sender,
704
        gas_price=effective_gas_price,
705
        gas=gas,
706
        access_list_addresses=access_list_addresses,
707
        access_list_storage_keys=access_list_storage_keys,
708
        transient_storage=TransientStorage(),
709
        blob_versioned_hashes=blob_versioned_hashes,
710
        index_in_block=index,
711
        tx_hash=get_transaction_hash(encode_transaction(tx)),
712
        traces=[],
713
    )
714
715
    message = prepare_message(block_env, tx_env, tx)
716
717
    tx_output = process_message_call(message)
718
719
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
720
    tx_gas_refund = min(
721
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
722
    )
723
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
724
    tx_gas_left = tx.gas - tx_gas_used_after_refund
725
    gas_refund_amount = tx_gas_left * effective_gas_price
726
727
    # For non-1559 transactions effective_gas_price == tx.gas_price
728
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
729
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
730
731
    # refund gas
732
    sender_balance_after_refund = get_account(
733
        block_env.state, sender
734
    ).balance + U256(gas_refund_amount)
735
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
736
737
    # transfer miner fees
738
    coinbase_balance_after_mining_fee = get_account(
739
        block_env.state, block_env.coinbase
740
    ).balance + U256(transaction_fee)
741
    if coinbase_balance_after_mining_fee != 0:
742
        set_account_balance(
743
            block_env.state,
744
            block_env.coinbase,
745
            coinbase_balance_after_mining_fee,
746
        )
747
    elif account_exists_and_is_empty(block_env.state, block_env.coinbase):
748
        destroy_account(block_env.state, block_env.coinbase)
749
750
    for address in tx_output.accounts_to_delete:
751
        destroy_account(block_env.state, address)
752
753
    block_output.block_gas_used += tx_gas_used_after_refund
754
    block_output.blob_gas_used += tx_blob_gas_used
755
756
    receipt = make_receipt(
757
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
758
    )
759
760
    receipt_key = rlp.encode(Uint(index))
761
    block_output.receipt_keys += (receipt_key,)
762
763
    trie_set(
764
        block_output.receipts_trie,
765
        receipt_key,
766
        receipt,
767
    )
768
769
    block_output.block_logs += tx_output.logs

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.shanghai.vm.BlockEnvironmentethereum.cancun.vm.BlockEnvironment, ​​block_output: ethereum.shanghai.vm.BlockOutputethereum.cancun.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
777
    """
778
    Increase the balance of the withdrawing account.
779
    """
780
781
    def increase_recipient_balance(recipient: Account) -> None:
782
        recipient.balance += wd.amount * U256(10**9)
783
784
    for i, wd in enumerate(withdrawals):
785
        trie_set(
786
            block_output.withdrawals_trie,
787
            rlp.encode(Uint(i)),
788
            rlp.encode(wd),
789
        )
790
791
        modify_state(block_env.state, wd.address, increase_recipient_balance)
792
793
        if account_exists_and_is_empty(block_env.state, wd.address):
794
            destroy_account(block_env.state, wd.address)

check_gas_limit

Validates the gas limit for a block.

The bounds of the gas limit, max_adjustment_delta, is set as the quotient of the parent block's gas limit and the GAS_LIMIT_ADJUSTMENT_FACTOR. Therefore, if the gas limit that is passed through as a parameter is greater than or equal to the sum of the parent's gas and the adjustment delta then the limit for gas is too high and fails this function's check. Similarly, if the limit is less than or equal to the difference of the parent's gas and the adjustment delta or the predefined GAS_LIMIT_MINIMUM then this function's check fails because the gas limit doesn't allow for a sufficient or reasonable amount of gas to be used on a block.

Parameters

gas_limit : Gas limit to validate.

parent_gas_limit : Gas limit of the parent block.

Returns

check : bool True if gas limit constraints are satisfied, False otherwise.

def check_gas_limit(gas_limit: Uint, ​​parent_gas_limit: Uint) -> bool:
798
    """
799
    Validates the gas limit for a block.
800
801
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
802
    quotient of the parent block's gas limit and the
803
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
804
    passed through as a parameter is greater than or equal to the *sum* of
805
    the parent's gas and the adjustment delta then the limit for gas is too
806
    high and fails this function's check. Similarly, if the limit is less
807
    than or equal to the *difference* of the parent's gas and the adjustment
808
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
809
    check fails because the gas limit doesn't allow for a sufficient or
810
    reasonable amount of gas to be used on a block.
811
812
    Parameters
813
    ----------
814
    gas_limit :
815
        Gas limit to validate.
816
817
    parent_gas_limit :
818
        Gas limit of the parent block.
819
820
    Returns
821
    -------
822
    check : `bool`
823
        True if gas limit constraints are satisfied, False otherwise.
824
    """
825
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
826
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
827
        return False
828
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
829
        return False
830
    if gas_limit < GAS_LIMIT_MINIMUM:
831
        return False
832
833
    return True