ethereum.forks.london.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BLOCK_REWARD

67
BLOCK_REWARD = U256(2 * 10**18)

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)

MINIMUM_DIFFICULTY

72
MINIMUM_DIFFICULTY = Uint(131072)

INITIAL_BASE_FEE

73
INITIAL_BASE_FEE = Uint(1000000000)

MAX_OMMER_DEPTH

74
MAX_OMMER_DEPTH = Uint(6)

BOMB_DELAY_BLOCKS

75
BOMB_DELAY_BLOCKS = 9700000

EMPTY_OMMER_HASH

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

BlockChain

History and current state of the block chain.

79
@dataclass
class BlockChain:

blocks

85
    blocks: List[Block]

state

86
    state: State

chain_id

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

generate_header_hash_for_pow

Generate rlp hash of the header which is to be used for Proof-of-Work verification.

In other words, the PoW artefacts mix_digest and nonce are ignored while calculating this hash.

A particular PoW is valid for a single hash, that hash is computed by this function. The nonce and mix_digest are omitted from this hash because they are being changed by miners in their search for a sufficient proof-of-work.

Parameters

header : The header object for which the hash is to be generated.

Returns

hash : Hash32 The PoW valid rlp hash of the passed in header.

def generate_header_hash_for_pow(header: Header) -> Hash32:
365
    """
366
    Generate rlp hash of the header which is to be used for Proof-of-Work
367
    verification.
368
369
    In other words, the PoW artefacts `mix_digest` and `nonce` are ignored
370
    while calculating this hash.
371
372
    A particular PoW is valid for a single hash, that hash is computed by
373
    this function. The `nonce` and `mix_digest` are omitted from this hash
374
    because they are being changed by miners in their search for a sufficient
375
    proof-of-work.
376
377
    Parameters
378
    ----------
379
    header :
380
        The header object for which the hash is to be generated.
381
382
    Returns
383
    -------
384
    hash : `Hash32`
385
        The PoW valid rlp hash of the passed in header.
386
387
    """
388
    header_data_without_pow_artefacts = (
389
        header.parent_hash,
390
        header.ommers_hash,
391
        header.coinbase,
392
        header.state_root,
393
        header.transactions_root,
394
        header.receipt_root,
395
        header.bloom,
396
        header.difficulty,
397
        header.number,
398
        header.gas_limit,
399
        header.gas_used,
400
        header.timestamp,
401
        header.extra_data,
402
        header.base_fee_per_gas,
403
    )
404
405
    return keccak256(rlp.encode(header_data_without_pow_artefacts))

validate_proof_of_work

Validates the Proof of Work constraints.

In order to verify that a miner's proof-of-work is valid for a block, a mix-digest and result are calculated using the hashimoto_light hash function. The mix digest is a hash of the header and the nonce that is passed through and it confirms whether or not proof-of-work was done on the correct block. The result is the actual hash value of the block.

Parameters

header : Header of interest.

def validate_proof_of_work(header: Header) -> None:
409
    """
410
    Validates the Proof of Work constraints.
411
412
    In order to verify that a miner's proof-of-work is valid for a block, a
413
    ``mix-digest`` and ``result`` are calculated using the ``hashimoto_light``
414
    hash function. The mix digest is a hash of the header and the nonce that
415
    is passed through and it confirms whether or not proof-of-work was done
416
    on the correct block. The result is the actual hash value of the block.
417
418
    Parameters
419
    ----------
420
    header :
421
        Header of interest.
422
423
    """
424
    header_hash = generate_header_hash_for_pow(header)
425
    # TODO: Memoize this somewhere and read from that data instead of
426
    # calculating cache for every block validation.
427
    cache = generate_cache(header.number)
428
    mix_digest, result = hashimoto_light(
429
        header_hash, header.nonce, cache, dataset_size(header.number)
430
    )
431
    if mix_digest != header.mix_digest:
432
        raise InvalidBlock
433
434
    limit = Uint(U256.MAX_VALUE) + Uint(1)
435
    if Uint.from_be_bytes(result) > (limit // header.difficulty):
436
        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.

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.

def check_transaction(block_env: ethereum.forks.london.vm.BlockEnvironment, ​​block_output: ethereum.forks.london.vm.BlockOutput, ​​tx: Transaction) -> Tuple[Address, Uint]:
444
    """
445
    Check if the transaction is includable in the block.
446
447
    Parameters
448
    ----------
449
    block_env :
450
        The block scoped environment.
451
    block_output :
452
        The block output for the current block.
453
    tx :
454
        The transaction.
455
456
    Returns
457
    -------
458
    sender_address :
459
        The sender of the transaction.
460
    effective_gas_price :
461
        The price to charge for gas when the transaction is executed.
462
463
    Raises
464
    ------
465
    InvalidBlock :
466
        If the transaction is not includable.
467
    GasUsedExceedsLimitError :
468
        If the gas used by the transaction exceeds the block's gas limit.
469
    NonceMismatchError :
470
        If the nonce of the transaction is not equal to the sender's nonce.
471
    InsufficientBalanceError :
472
        If the sender's balance is not enough to pay for the transaction.
473
    InvalidSenderError :
474
        If the transaction is from an address that does not exist anymore.
475
    PriorityFeeGreaterThanMaxFeeError:
476
        If the priority fee is greater than the maximum fee per gas.
477
    InsufficientMaxFeePerGasError :
478
        If the maximum fee per gas is insufficient for the transaction.
479
480
    """
481
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
482
    if tx.gas > gas_available:
483
        raise GasUsedExceedsLimitError("gas used exceeds limit")
484
    sender_address = recover_sender(block_env.chain_id, tx)
485
    sender_account = get_account(block_env.state, sender_address)
486
487
    if isinstance(tx, FeeMarketTransaction):
488
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
489
            raise PriorityFeeGreaterThanMaxFeeError(
490
                "priority fee greater than max fee"
491
            )
492
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
493
            raise InsufficientMaxFeePerGasError(
494
                tx.max_fee_per_gas, block_env.base_fee_per_gas
495
            )
496
497
        priority_fee_per_gas = min(
498
            tx.max_priority_fee_per_gas,
499
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
500
        )
501
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
502
        max_gas_fee = tx.gas * tx.max_fee_per_gas
503
    else:
504
        if tx.gas_price < block_env.base_fee_per_gas:
505
            raise InvalidBlock
506
        effective_gas_price = tx.gas_price
507
        max_gas_fee = tx.gas * tx.gas_price
508
509
    if sender_account.nonce > Uint(tx.nonce):
510
        raise NonceMismatchError("nonce too low")
511
    elif sender_account.nonce < Uint(tx.nonce):
512
        raise NonceMismatchError("nonce too high")
513
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
514
        raise InsufficientBalanceError("insufficient sender balance")
515
    if sender_account.code:
516
        raise InvalidSenderError("not EOA")
517
518
    return sender_address, effective_gas_price

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:
527
    """
528
    Make the receipt for a transaction that was executed.
529
530
    Parameters
531
    ----------
532
    tx :
533
        The executed transaction.
534
    error :
535
        Error in the top level frame of the transaction, if any.
536
    cumulative_gas_used :
537
        The total gas used so far in the block after the transaction was
538
        executed.
539
    logs :
540
        The logs produced by the transaction.
541
542
    Returns
543
    -------
544
    receipt :
545
        The receipt for the transaction.
546
547
    """
548
    receipt = Receipt(
549
        succeeded=error is None,
550
        cumulative_gas_used=cumulative_gas_used,
551
        bloom=logs_bloom(logs),
552
        logs=logs,
553
    )
554
555
    return encode_receipt(tx, receipt)

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. ommers : Headers of ancestor blocks which are not direct parents (formerly uncles.)

Returns

block_output : The block output for the current block.

def apply_body(block_env: ethereum.forks.london.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​ommers: Tuple[Header, ...]) -> ethereum.forks.london.vm.BlockOutput:
563
    """
564
    Executes a block.
565
566
    Many of the contents of a block are stored in data structures called
567
    tries. There is a transactions trie which is similar to a ledger of the
568
    transactions stored in the current block. There is also a receipts trie
569
    which stores the results of executing a transaction, like the post state
570
    and gas used. This function creates and executes the block that is to be
571
    added to the chain.
572
573
    Parameters
574
    ----------
575
    block_env :
576
        The block scoped environment.
577
    transactions :
578
        Transactions included in the block.
579
    ommers :
580
        Headers of ancestor blocks which are not direct parents (formerly
581
        uncles.)
582
583
    Returns
584
    -------
585
    block_output :
586
        The block output for the current block.
587
588
    """
589
    block_output = vm.BlockOutput()
590
591
    for i, tx in enumerate(map(decode_transaction, transactions)):
592
        process_transaction(block_env, block_output, tx, Uint(i))
593
594
    pay_rewards(block_env.state, block_env.number, block_env.coinbase, ommers)
595
596
    return block_output

validate_ommers

Validates the ommers mentioned in the block.

An ommer block is a block that wasn't canonically added to the blockchain because it wasn't validated as fast as the canonical block but was mined at the same time.

To be considered valid, the ommers must adhere to the rules defined in the Ethereum protocol. The maximum amount of ommers is 2 per block and there cannot be duplicate ommers in a block. Many of the other ommer constraints are listed in the in-line comments of this function.

Parameters

ommers : List of ommers mentioned in the current block. block_header: The header of current block. chain : History and current state.

def validate_ommers(ommers: Tuple[Header, ...], ​​block_header: Header, ​​chain: BlockChain) -> None:
602
    """
603
    Validates the ommers mentioned in the block.
604
605
    An ommer block is a block that wasn't canonically added to the
606
    blockchain because it wasn't validated as fast as the canonical block
607
    but was mined at the same time.
608
609
    To be considered valid, the ommers must adhere to the rules defined in
610
    the Ethereum protocol. The maximum amount of ommers is 2 per block and
611
    there cannot be duplicate ommers in a block. Many of the other ommer
612
    constraints are listed in the in-line comments of this function.
613
614
    Parameters
615
    ----------
616
    ommers :
617
        List of ommers mentioned in the current block.
618
    block_header:
619
        The header of current block.
620
    chain :
621
        History and current state.
622
623
    """
624
    block_hash = keccak256(rlp.encode(block_header))
625
    if keccak256(rlp.encode(ommers)) != block_header.ommers_hash:
626
        raise InvalidBlock
627
628
    if len(ommers) == 0:
629
        # Nothing to validate
630
        return
631
632
    # Check that each ommer satisfies the constraints of a header
633
    for ommer in ommers:
634
        if Uint(1) > ommer.number or ommer.number >= block_header.number:
635
            raise InvalidBlock
636
        validate_header(chain, ommer)
637
    if len(ommers) > 2:
638
        raise InvalidBlock
639
640
    ommers_hashes = [keccak256(rlp.encode(ommer)) for ommer in ommers]
641
    if len(ommers_hashes) != len(set(ommers_hashes)):
642
        raise InvalidBlock
643
644
    recent_canonical_blocks = chain.blocks[-(MAX_OMMER_DEPTH + Uint(1)) :]
645
    recent_canonical_block_hashes = {
646
        keccak256(rlp.encode(block.header))
647
        for block in recent_canonical_blocks
648
    }
649
    recent_ommers_hashes: Set[Hash32] = set()
650
    for block in recent_canonical_blocks:
651
        recent_ommers_hashes = recent_ommers_hashes.union(
652
            {keccak256(rlp.encode(ommer)) for ommer in block.ommers}
653
        )
654
655
    for ommer_index, ommer in enumerate(ommers):
656
        ommer_hash = ommers_hashes[ommer_index]
657
        if ommer_hash == block_hash:
658
            raise InvalidBlock
659
        if ommer_hash in recent_canonical_block_hashes:
660
            raise InvalidBlock
661
        if ommer_hash in recent_ommers_hashes:
662
            raise InvalidBlock
663
664
        # Ommer age with respect to the current block. For example, an age of
665
        # 1 indicates that the ommer is a sibling of previous block.
666
        ommer_age = block_header.number - ommer.number
667
        if Uint(1) > ommer_age or ommer_age > MAX_OMMER_DEPTH:
668
            raise InvalidBlock
669
        if ommer.parent_hash not in recent_canonical_block_hashes:
670
            raise InvalidBlock
671
        if ommer.parent_hash == block_header.parent_hash:
672
            raise InvalidBlock

pay_rewards

Pay rewards to the block miner as well as the ommers miners.

The miner of the canonical block is rewarded with the predetermined block reward, BLOCK_REWARD, plus a variable award based off of the number of ommer blocks that were mined around the same time, and included in the canonical block's header. An ommer block is a block that wasn't added to the canonical blockchain because it wasn't validated as fast as the accepted block but was mined at the same time. Although not all blocks that are mined are added to the canonical chain, miners are still paid a reward for their efforts. This reward is called an ommer reward and is calculated based on the number associated with the ommer block that they mined.

Parameters

state : Current account state. block_number : Position of the block within the chain. coinbase : Address of account which receives block reward and transaction fees. ommers : List of ommers mentioned in the current block.

def pay_rewards(state: State, ​​block_number: Uint, ​​coinbase: Address, ​​ommers: Tuple[Header, ...]) -> None:
681
    """
682
    Pay rewards to the block miner as well as the ommers miners.
683
684
    The miner of the canonical block is rewarded with the predetermined
685
    block reward, ``BLOCK_REWARD``, plus a variable award based off of the
686
    number of ommer blocks that were mined around the same time, and included
687
    in the canonical block's header. An ommer block is a block that wasn't
688
    added to the canonical blockchain because it wasn't validated as fast as
689
    the accepted block but was mined at the same time. Although not all blocks
690
    that are mined are added to the canonical chain, miners are still paid a
691
    reward for their efforts. This reward is called an ommer reward and is
692
    calculated based on the number associated with the ommer block that they
693
    mined.
694
695
    Parameters
696
    ----------
697
    state :
698
        Current account state.
699
    block_number :
700
        Position of the block within the chain.
701
    coinbase :
702
        Address of account which receives block reward and transaction fees.
703
    ommers :
704
        List of ommers mentioned in the current block.
705
706
    """
707
    ommer_count = U256(len(ommers))
708
    miner_reward = BLOCK_REWARD + (ommer_count * (BLOCK_REWARD // U256(32)))
709
    create_ether(state, coinbase, miner_reward)
710
711
    for ommer in ommers:
712
        # Ommer age with respect to the current block.
713
        ommer_age = U256(block_number - ommer.number)
714
        ommer_miner_reward = ((U256(8) - ommer_age) * BLOCK_REWARD) // U256(8)
715
        create_ether(state, ommer.coinbase, ommer_miner_reward)

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.london.vm.BlockEnvironment, ​​block_output: ethereum.forks.london.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
724
    """
725
    Execute a transaction against the provided environment.
726
727
    This function processes the actions needed to execute a transaction.
728
    It decrements the sender's account balance after calculating the gas fee
729
    and refunds them the proper amount after execution. Calling contracts,
730
    deploying code, and incrementing nonces are all examples of actions that
731
    happen within this function or from a call made within this function.
732
733
    Accounts that are marked for deletion are processed and destroyed after
734
    execution.
735
736
    Parameters
737
    ----------
738
    block_env :
739
        Environment for the Ethereum Virtual Machine.
740
    block_output :
741
        The block output for the current block.
742
    tx :
743
        Transaction to execute.
744
    index:
745
        Index of the transaction in the block.
746
747
    """
748
    trie_set(
749
        block_output.transactions_trie,
750
        rlp.encode(index),
751
        encode_transaction(tx),
752
    )
753
754
    intrinsic_gas = validate_transaction(tx)
755
756
    (
757
        sender,
758
        effective_gas_price,
759
    ) = check_transaction(
760
        block_env=block_env,
761
        block_output=block_output,
762
        tx=tx,
763
    )
764
765
    sender_account = get_account(block_env.state, sender)
766
767
    effective_gas_fee = tx.gas * effective_gas_price
768
769
    gas = tx.gas - intrinsic_gas
770
    increment_nonce(block_env.state, sender)
771
772
    sender_balance_after_gas_fee = (
773
        Uint(sender_account.balance) - effective_gas_fee
774
    )
775
    set_account_balance(
776
        block_env.state, sender, U256(sender_balance_after_gas_fee)
777
    )
778
779
    access_list_addresses = set()
780
    access_list_storage_keys = set()
781
    if isinstance(tx, (AccessListTransaction, FeeMarketTransaction)):
782
        for access in tx.access_list:
783
            access_list_addresses.add(access.account)
784
            for slot in access.slots:
785
                access_list_storage_keys.add((access.account, slot))
786
787
    tx_env = vm.TransactionEnvironment(
788
        origin=sender,
789
        gas_price=effective_gas_price,
790
        gas=gas,
791
        access_list_addresses=access_list_addresses,
792
        access_list_storage_keys=access_list_storage_keys,
793
        index_in_block=index,
794
        tx_hash=get_transaction_hash(encode_transaction(tx)),
795
    )
796
797
    message = prepare_message(block_env, tx_env, tx)
798
799
    tx_output = process_message_call(message)
800
801
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
802
    tx_gas_refund = min(
803
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
804
    )
805
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
806
    tx_gas_left = tx.gas - tx_gas_used_after_refund
807
    gas_refund_amount = tx_gas_left * effective_gas_price
808
809
    # For non-1559 transactions effective_gas_price == tx.gas_price
810
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
811
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
812
813
    # refund gas
814
    sender_balance_after_refund = get_account(
815
        block_env.state, sender
816
    ).balance + U256(gas_refund_amount)
817
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
818
819
    # transfer miner fees
820
    coinbase_balance_after_mining_fee = get_account(
821
        block_env.state, block_env.coinbase
822
    ).balance + U256(transaction_fee)
823
    if coinbase_balance_after_mining_fee != 0:
824
        set_account_balance(
825
            block_env.state,
826
            block_env.coinbase,
827
            coinbase_balance_after_mining_fee,
828
        )
829
    elif account_exists_and_is_empty(block_env.state, block_env.coinbase):
830
        destroy_account(block_env.state, block_env.coinbase)
831
832
    for address in tx_output.accounts_to_delete:
833
        destroy_account(block_env.state, address)
834
835
    destroy_touched_empty_accounts(block_env.state, tx_output.touched_accounts)
836
837
    block_output.block_gas_used += tx_gas_used_after_refund
838
839
    receipt = make_receipt(
840
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
841
    )
842
843
    receipt_key = rlp.encode(Uint(index))
844
    block_output.receipt_keys += (receipt_key,)
845
846
    trie_set(
847
        block_output.receipts_trie,
848
        receipt_key,
849
        receipt,
850
    )
851
852
    block_output.block_logs += tx_output.logs

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:
856
    """
857
    Validates the gas limit for a block.
858
859
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
860
    quotient of the parent block's gas limit and the
861
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
862
    passed through as a parameter is greater than or equal to the *sum* of
863
    the parent's gas and the adjustment delta then the limit for gas is too
864
    high and fails this function's check. Similarly, if the limit is less
865
    than or equal to the *difference* of the parent's gas and the adjustment
866
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
867
    check fails because the gas limit doesn't allow for a sufficient or
868
    reasonable amount of gas to be used on a block.
869
870
    Parameters
871
    ----------
872
    gas_limit :
873
        Gas limit to validate.
874
875
    parent_gas_limit :
876
        Gas limit of the parent block.
877
878
    Returns
879
    -------
880
    check : `bool`
881
        True if gas limit constraints are satisfied, False otherwise.
882
883
    """
884
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
885
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
886
        return False
887
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
888
        return False
889
    if gas_limit < GAS_LIMIT_MINIMUM:
890
        return False
891
892
    return True

calculate_block_difficulty

Computes difficulty of a block using its header and parent header.

The difficulty is determined by the time the block was created after its parent. The offset is calculated using the parent block's difficulty, parent_difficulty, and the timestamp between blocks. This offset is then added to the parent difficulty and is stored as the difficulty variable. If the time between the block and its parent is too short, the offset will result in a positive number thus making the sum of parent_difficulty and offset to be a greater value in order to avoid mass forking. But, if the time is long enough, then the offset results in a negative value making the block less difficult than its parent.

The base standard for a block's difficulty is the predefined value set for the genesis block since it has no parent. So, a block can't be less difficult than the genesis block, therefore each block's difficulty is set to the maximum value between the calculated difficulty and the MINIMUM_DIFFICULTY.

Parameters

block_number : Block number of the block. block_timestamp : Timestamp of the block. parent_timestamp : Timestamp of the parent block. parent_difficulty : difficulty of the parent block. parent_has_ommers: does the parent have ommers.

Returns

difficulty : ethereum.base_types.Uint Computed difficulty for a block.

def calculate_block_difficulty(block_number: Uint, ​​block_timestamp: U256, ​​parent_timestamp: U256, ​​parent_difficulty: Uint, ​​parent_has_ommers: bool) -> Uint:
902
    """
903
    Computes difficulty of a block using its header and parent header.
904
905
    The difficulty is determined by the time the block was created after its
906
    parent. The ``offset`` is calculated using the parent block's difficulty,
907
    ``parent_difficulty``, and the timestamp between blocks. This offset is
908
    then added to the parent difficulty and is stored as the ``difficulty``
909
    variable. If the time between the block and its parent is too short, the
910
    offset will result in a positive number thus making the sum of
911
    ``parent_difficulty`` and ``offset`` to be a greater value in order to
912
    avoid mass forking. But, if the time is long enough, then the offset
913
    results in a negative value making the block less difficult than
914
    its parent.
915
916
    The base standard for a block's difficulty is the predefined value
917
    set for the genesis block since it has no parent. So, a block
918
    can't be less difficult than the genesis block, therefore each block's
919
    difficulty is set to the maximum value between the calculated
920
    difficulty and the ``MINIMUM_DIFFICULTY``.
921
922
    Parameters
923
    ----------
924
    block_number :
925
        Block number of the block.
926
    block_timestamp :
927
        Timestamp of the block.
928
    parent_timestamp :
929
        Timestamp of the parent block.
930
    parent_difficulty :
931
        difficulty of the parent block.
932
    parent_has_ommers:
933
        does the parent have ommers.
934
935
    Returns
936
    -------
937
    difficulty : `ethereum.base_types.Uint`
938
        Computed difficulty for a block.
939
940
    """
941
    offset = (
942
        int(parent_difficulty)
943
        // 2048
944
        * max(
945
            (2 if parent_has_ommers else 1)
946
            - int(block_timestamp - parent_timestamp) // 9,
947
            -99,
948
        )
949
    )
950
    difficulty = int(parent_difficulty) + offset
951
    # Historical Note: The difficulty bomb was not present in Ethereum at the
952
    # start of Frontier, but was added shortly after launch. However since the
953
    # bomb has no effect prior to block 200000 we pretend it existed from
954
    # genesis.
955
    # See https://github.com/ethereum/go-ethereum/pull/1588
956
    num_bomb_periods = ((int(block_number) - BOMB_DELAY_BLOCKS) // 100000) - 2
957
    if num_bomb_periods >= 0:
958
        difficulty += 2**num_bomb_periods
959
960
    # Some clients raise the difficulty to `MINIMUM_DIFFICULTY` prior to adding
961
    # the bomb. This bug does not matter because the difficulty is always much
962
    # greater than `MINIMUM_DIFFICULTY` on Mainnet.
963
    return Uint(max(difficulty, int(MINIMUM_DIFFICULTY)))