ethereum.forks.london.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BLOCK_REWARD

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

BASE_FEE_MAX_CHANGE_DENOMINATOR

67
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

68
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

69
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

70
GAS_LIMIT_MINIMUM = Uint(5000)

MINIMUM_DIFFICULTY

71
MINIMUM_DIFFICULTY = Uint(131072)

INITIAL_BASE_FEE

72
INITIAL_BASE_FEE = Uint(1000000000)

MAX_OMMER_DEPTH

73
MAX_OMMER_DEPTH = Uint(6)

BOMB_DELAY_BLOCKS

74
BOMB_DELAY_BLOCKS = 9700000

EMPTY_OMMER_HASH

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

BlockChain

History and current state of the block chain.

78
@dataclass
class BlockChain:

blocks

84
    blocks: List[Block]

state

85
    state: State

chain_id

86
    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:
90
    """
91
    Transforms the state from the previous hard fork (`old`) into the block
92
    chain object for this hard fork and returns it.
93
94
    When forks need to implement an irregular state transition, this function
95
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
96
    an example.
97
98
    Parameters
99
    ----------
100
    old :
101
        Previous block chain object.
102
103
    Returns
104
    -------
105
    new : `BlockChain`
106
        Upgraded block chain object for this hard fork.
107
108
    """
109
    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]:
113
    """
114
    Obtain the list of hashes of the previous 256 blocks in order of
115
    increasing block number.
116
117
    This function will return less hashes for the first 256 blocks.
118
119
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
120
    therefore this function retrieves them.
121
122
    Parameters
123
    ----------
124
    chain :
125
        History and current state.
126
127
    Returns
128
    -------
129
    recent_block_hashes : `List[Hash32]`
130
        Hashes of the recent 256 blocks in order of increasing block number.
131
132
    """
133
    recent_blocks = chain.blocks[-255:]
134
    # TODO: This function has not been tested rigorously
135
    if len(recent_blocks) == 0:
136
        return []
137
138
    recent_block_hashes = []
139
140
    for block in recent_blocks:
141
        prev_block_hash = block.header.parent_hash
142
        recent_block_hashes.append(prev_block_hash)
143
144
    # We are computing the hash only for the most recent block and not for
145
    # the rest of the blocks as they have successors which have the hash of
146
    # the current block as parent hash.
147
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
148
    recent_block_hashes.append(most_recent_block_hash)
149
150
    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:
154
    """
155
    Attempts to apply a block to an existing block chain.
156
157
    All parts of the block's contents need to be verified before being added
158
    to the chain. Blocks are verified by ensuring that the contents of the
159
    block make logical sense with the contents of the parent block. The
160
    information in the block's header must also match the corresponding
161
    information in the block.
162
163
    To implement Ethereum, in theory clients are only required to store the
164
    most recent 255 blocks of the chain since as far as execution is
165
    concerned, only those blocks are accessed. Practically, however, clients
166
    should store more blocks to handle reorgs.
167
168
    Parameters
169
    ----------
170
    chain :
171
        History and current state.
172
    block :
173
        Block to apply to `chain`.
174
175
    """
176
    validate_header(chain, block.header)
177
    validate_ommers(block.ommers, block.header, chain)
178
179
    block_env = vm.BlockEnvironment(
180
        chain_id=chain.chain_id,
181
        state=chain.state,
182
        block_gas_limit=block.header.gas_limit,
183
        block_hashes=get_last_256_block_hashes(chain),
184
        coinbase=block.header.coinbase,
185
        number=block.header.number,
186
        base_fee_per_gas=block.header.base_fee_per_gas,
187
        time=block.header.timestamp,
188
        difficulty=block.header.difficulty,
189
    )
190
191
    block_output = apply_body(
192
        block_env=block_env,
193
        transactions=block.transactions,
194
        ommers=block.ommers,
195
    )
196
    block_state_root = state_root(block_env.state)
197
    transactions_root = root(block_output.transactions_trie)
198
    receipt_root = root(block_output.receipts_trie)
199
    block_logs_bloom = logs_bloom(block_output.block_logs)
200
201
    if block_output.block_gas_used != block.header.gas_used:
202
        raise InvalidBlock(
203
            f"{block_output.block_gas_used} != {block.header.gas_used}"
204
        )
205
    if transactions_root != block.header.transactions_root:
206
        raise InvalidBlock
207
    if block_state_root != block.header.state_root:
208
        raise InvalidBlock
209
    if receipt_root != block.header.receipt_root:
210
        raise InvalidBlock
211
    if block_logs_bloom != block.header.bloom:
212
        raise InvalidBlock
213
214
    chain.blocks.append(block)
215
    if len(chain.blocks) > 255:
216
        # Real clients have to store more blocks to deal with reorgs, but the
217
        # protocol only requires the last 255
218
        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:
227
    """
228
    Calculates the base fee per gas for the block.
229
230
    Parameters
231
    ----------
232
    block_gas_limit :
233
        Gas limit of the block for which the base fee is being calculated.
234
    parent_gas_limit :
235
        Gas limit of the parent block.
236
    parent_gas_used :
237
        Gas used in the parent block.
238
    parent_base_fee_per_gas :
239
        Base fee per gas of the parent block.
240
241
    Returns
242
    -------
243
    base_fee_per_gas : `Uint`
244
        Base fee per gas for the block.
245
246
    """
247
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
248
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
249
        raise InvalidBlock
250
251
    if parent_gas_used == parent_gas_target:
252
        expected_base_fee_per_gas = parent_base_fee_per_gas
253
    elif parent_gas_used > parent_gas_target:
254
        gas_used_delta = parent_gas_used - parent_gas_target
255
256
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
257
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
258
259
        base_fee_per_gas_delta = max(
260
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
261
            Uint(1),
262
        )
263
264
        expected_base_fee_per_gas = (
265
            parent_base_fee_per_gas + base_fee_per_gas_delta
266
        )
267
    else:
268
        gas_used_delta = parent_gas_target - parent_gas_used
269
270
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
271
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
272
273
        base_fee_per_gas_delta = (
274
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
275
        )
276
277
        expected_base_fee_per_gas = (
278
            parent_base_fee_per_gas - base_fee_per_gas_delta
279
        )
280
281
    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:
285
    """
286
    Verifies a block header.
287
288
    In order to consider a block's header valid, the logic for the
289
    quantities in the header should match the logic for the block itself.
290
    For example the header timestamp should be greater than the block's parent
291
    timestamp because the block was created *after* the parent block.
292
    Additionally, the block's number should be directly following the parent
293
    block's number since it is the next block in the sequence.
294
295
    Parameters
296
    ----------
297
    chain :
298
        History and current state.
299
    header :
300
        Header to check for correctness.
301
302
    """
303
    if header.number < Uint(1):
304
        raise InvalidBlock
305
    parent_header_number = header.number - Uint(1)
306
    first_block_number = chain.blocks[0].header.number
307
    last_block_number = chain.blocks[-1].header.number
308
309
    if (
310
        parent_header_number < first_block_number
311
        or parent_header_number > last_block_number
312
    ):
313
        raise InvalidBlock
314
315
    parent_header = chain.blocks[
316
        parent_header_number - first_block_number
317
    ].header
318
319
    if header.gas_used > header.gas_limit:
320
        raise InvalidBlock
321
322
    expected_base_fee_per_gas = INITIAL_BASE_FEE
323
    if header.number != FORK_CRITERIA.block_number:
324
        # For every block except the first, calculate the base fee per gas
325
        # based on the parent block.
326
        expected_base_fee_per_gas = calculate_base_fee_per_gas(
327
            header.gas_limit,
328
            parent_header.gas_limit,
329
            parent_header.gas_used,
330
            parent_header.base_fee_per_gas,
331
        )
332
333
    if expected_base_fee_per_gas != header.base_fee_per_gas:
334
        raise InvalidBlock
335
336
    parent_has_ommers = parent_header.ommers_hash != EMPTY_OMMER_HASH
337
    if header.timestamp <= parent_header.timestamp:
338
        raise InvalidBlock
339
    if header.number != parent_header.number + Uint(1):
340
        raise InvalidBlock
341
    if len(header.extra_data) > 32:
342
        raise InvalidBlock
343
344
    block_difficulty = calculate_block_difficulty(
345
        header.number,
346
        header.timestamp,
347
        parent_header.timestamp,
348
        parent_header.difficulty,
349
        parent_has_ommers,
350
    )
351
    if header.difficulty != block_difficulty:
352
        raise InvalidBlock
353
354
    block_parent_hash = keccak256(rlp.encode(parent_header))
355
    if header.parent_hash != block_parent_hash:
356
        raise InvalidBlock
357
358
    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:
362
    """
363
    Generate rlp hash of the header which is to be used for Proof-of-Work
364
    verification.
365
366
    In other words, the PoW artefacts `mix_digest` and `nonce` are ignored
367
    while calculating this hash.
368
369
    A particular PoW is valid for a single hash, that hash is computed by
370
    this function. The `nonce` and `mix_digest` are omitted from this hash
371
    because they are being changed by miners in their search for a sufficient
372
    proof-of-work.
373
374
    Parameters
375
    ----------
376
    header :
377
        The header object for which the hash is to be generated.
378
379
    Returns
380
    -------
381
    hash : `Hash32`
382
        The PoW valid rlp hash of the passed in header.
383
384
    """
385
    header_data_without_pow_artefacts = (
386
        header.parent_hash,
387
        header.ommers_hash,
388
        header.coinbase,
389
        header.state_root,
390
        header.transactions_root,
391
        header.receipt_root,
392
        header.bloom,
393
        header.difficulty,
394
        header.number,
395
        header.gas_limit,
396
        header.gas_used,
397
        header.timestamp,
398
        header.extra_data,
399
        header.base_fee_per_gas,
400
    )
401
402
    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:
406
    """
407
    Validates the Proof of Work constraints.
408
409
    In order to verify that a miner's proof-of-work is valid for a block, a
410
    ``mix-digest`` and ``result`` are calculated using the ``hashimoto_light``
411
    hash function. The mix digest is a hash of the header and the nonce that
412
    is passed through and it confirms whether or not proof-of-work was done
413
    on the correct block. The result is the actual hash value of the block.
414
415
    Parameters
416
    ----------
417
    header :
418
        Header of interest.
419
420
    """
421
    header_hash = generate_header_hash_for_pow(header)
422
    # TODO: Memoize this somewhere and read from that data instead of
423
    # calculating cache for every block validation.
424
    cache = generate_cache(header.number)
425
    mix_digest, result = hashimoto_light(
426
        header_hash, header.nonce, cache, dataset_size(header.number)
427
    )
428
    if mix_digest != header.mix_digest:
429
        raise InvalidBlock
430
431
    limit = Uint(U256.MAX_VALUE) + Uint(1)
432
    if Uint.from_be_bytes(result) > (limit // header.difficulty):
433
        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]:
441
    """
442
    Check if the transaction is includable in the block.
443
444
    Parameters
445
    ----------
446
    block_env :
447
        The block scoped environment.
448
    block_output :
449
        The block output for the current block.
450
    tx :
451
        The transaction.
452
453
    Returns
454
    -------
455
    sender_address :
456
        The sender of the transaction.
457
    effective_gas_price :
458
        The price to charge for gas when the transaction is executed.
459
460
    Raises
461
    ------
462
    InvalidBlock :
463
        If the transaction is not includable.
464
    GasUsedExceedsLimitError :
465
        If the gas used by the transaction exceeds the block's gas limit.
466
    NonceMismatchError :
467
        If the nonce of the transaction is not equal to the sender's nonce.
468
    InsufficientBalanceError :
469
        If the sender's balance is not enough to pay for the transaction.
470
    InvalidSenderError :
471
        If the transaction is from an address that does not exist anymore.
472
    PriorityFeeGreaterThanMaxFeeError:
473
        If the priority fee is greater than the maximum fee per gas.
474
    InsufficientMaxFeePerGasError :
475
        If the maximum fee per gas is insufficient for the transaction.
476
477
    """
478
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
479
    if tx.gas > gas_available:
480
        raise GasUsedExceedsLimitError("gas used exceeds limit")
481
    sender_address = recover_sender(block_env.chain_id, tx)
482
    sender_account = get_account(block_env.state, sender_address)
483
484
    if isinstance(tx, FeeMarketTransaction):
485
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
486
            raise PriorityFeeGreaterThanMaxFeeError(
487
                "priority fee greater than max fee"
488
            )
489
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
490
            raise InsufficientMaxFeePerGasError(
491
                tx.max_fee_per_gas, block_env.base_fee_per_gas
492
            )
493
494
        priority_fee_per_gas = min(
495
            tx.max_priority_fee_per_gas,
496
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
497
        )
498
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
499
        max_gas_fee = tx.gas * tx.max_fee_per_gas
500
    else:
501
        if tx.gas_price < block_env.base_fee_per_gas:
502
            raise InvalidBlock
503
        effective_gas_price = tx.gas_price
504
        max_gas_fee = tx.gas * tx.gas_price
505
506
    if sender_account.nonce > Uint(tx.nonce):
507
        raise NonceMismatchError("nonce too low")
508
    elif sender_account.nonce < Uint(tx.nonce):
509
        raise NonceMismatchError("nonce too high")
510
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
511
        raise InsufficientBalanceError("insufficient sender balance")
512
    if sender_account.code:
513
        raise InvalidSenderError("not EOA")
514
515
    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:
524
    """
525
    Make the receipt for a transaction that was executed.
526
527
    Parameters
528
    ----------
529
    tx :
530
        The executed transaction.
531
    error :
532
        Error in the top level frame of the transaction, if any.
533
    cumulative_gas_used :
534
        The total gas used so far in the block after the transaction was
535
        executed.
536
    logs :
537
        The logs produced by the transaction.
538
539
    Returns
540
    -------
541
    receipt :
542
        The receipt for the transaction.
543
544
    """
545
    receipt = Receipt(
546
        succeeded=error is None,
547
        cumulative_gas_used=cumulative_gas_used,
548
        bloom=logs_bloom(logs),
549
        logs=logs,
550
    )
551
552
    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:
560
    """
561
    Executes a block.
562
563
    Many of the contents of a block are stored in data structures called
564
    tries. There is a transactions trie which is similar to a ledger of the
565
    transactions stored in the current block. There is also a receipts trie
566
    which stores the results of executing a transaction, like the post state
567
    and gas used. This function creates and executes the block that is to be
568
    added to the chain.
569
570
    Parameters
571
    ----------
572
    block_env :
573
        The block scoped environment.
574
    transactions :
575
        Transactions included in the block.
576
    ommers :
577
        Headers of ancestor blocks which are not direct parents (formerly
578
        uncles.)
579
580
    Returns
581
    -------
582
    block_output :
583
        The block output for the current block.
584
585
    """
586
    block_output = vm.BlockOutput()
587
588
    for i, tx in enumerate(map(decode_transaction, transactions)):
589
        process_transaction(block_env, block_output, tx, Uint(i))
590
591
    pay_rewards(block_env.state, block_env.number, block_env.coinbase, ommers)
592
593
    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:
599
    """
600
    Validates the ommers mentioned in the block.
601
602
    An ommer block is a block that wasn't canonically added to the
603
    blockchain because it wasn't validated as fast as the canonical block
604
    but was mined at the same time.
605
606
    To be considered valid, the ommers must adhere to the rules defined in
607
    the Ethereum protocol. The maximum amount of ommers is 2 per block and
608
    there cannot be duplicate ommers in a block. Many of the other ommer
609
    constraints are listed in the in-line comments of this function.
610
611
    Parameters
612
    ----------
613
    ommers :
614
        List of ommers mentioned in the current block.
615
    block_header:
616
        The header of current block.
617
    chain :
618
        History and current state.
619
620
    """
621
    block_hash = keccak256(rlp.encode(block_header))
622
    if keccak256(rlp.encode(ommers)) != block_header.ommers_hash:
623
        raise InvalidBlock
624
625
    if len(ommers) == 0:
626
        # Nothing to validate
627
        return
628
629
    # Check that each ommer satisfies the constraints of a header
630
    for ommer in ommers:
631
        if Uint(1) > ommer.number or ommer.number >= block_header.number:
632
            raise InvalidBlock
633
        validate_header(chain, ommer)
634
    if len(ommers) > 2:
635
        raise InvalidBlock
636
637
    ommers_hashes = [keccak256(rlp.encode(ommer)) for ommer in ommers]
638
    if len(ommers_hashes) != len(set(ommers_hashes)):
639
        raise InvalidBlock
640
641
    recent_canonical_blocks = chain.blocks[-(MAX_OMMER_DEPTH + Uint(1)) :]
642
    recent_canonical_block_hashes = {
643
        keccak256(rlp.encode(block.header))
644
        for block in recent_canonical_blocks
645
    }
646
    recent_ommers_hashes: Set[Hash32] = set()
647
    for block in recent_canonical_blocks:
648
        recent_ommers_hashes = recent_ommers_hashes.union(
649
            {keccak256(rlp.encode(ommer)) for ommer in block.ommers}
650
        )
651
652
    for ommer_index, ommer in enumerate(ommers):
653
        ommer_hash = ommers_hashes[ommer_index]
654
        if ommer_hash == block_hash:
655
            raise InvalidBlock
656
        if ommer_hash in recent_canonical_block_hashes:
657
            raise InvalidBlock
658
        if ommer_hash in recent_ommers_hashes:
659
            raise InvalidBlock
660
661
        # Ommer age with respect to the current block. For example, an age of
662
        # 1 indicates that the ommer is a sibling of previous block.
663
        ommer_age = block_header.number - ommer.number
664
        if Uint(1) > ommer_age or ommer_age > MAX_OMMER_DEPTH:
665
            raise InvalidBlock
666
        if ommer.parent_hash not in recent_canonical_block_hashes:
667
            raise InvalidBlock
668
        if ommer.parent_hash == block_header.parent_hash:
669
            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:
678
    """
679
    Pay rewards to the block miner as well as the ommers miners.
680
681
    The miner of the canonical block is rewarded with the predetermined
682
    block reward, ``BLOCK_REWARD``, plus a variable award based off of the
683
    number of ommer blocks that were mined around the same time, and included
684
    in the canonical block's header. An ommer block is a block that wasn't
685
    added to the canonical blockchain because it wasn't validated as fast as
686
    the accepted block but was mined at the same time. Although not all blocks
687
    that are mined are added to the canonical chain, miners are still paid a
688
    reward for their efforts. This reward is called an ommer reward and is
689
    calculated based on the number associated with the ommer block that they
690
    mined.
691
692
    Parameters
693
    ----------
694
    state :
695
        Current account state.
696
    block_number :
697
        Position of the block within the chain.
698
    coinbase :
699
        Address of account which receives block reward and transaction fees.
700
    ommers :
701
        List of ommers mentioned in the current block.
702
703
    """
704
    ommer_count = U256(len(ommers))
705
    miner_reward = BLOCK_REWARD + (ommer_count * (BLOCK_REWARD // U256(32)))
706
    create_ether(state, coinbase, miner_reward)
707
708
    for ommer in ommers:
709
        # Ommer age with respect to the current block.
710
        ommer_age = U256(block_number - ommer.number)
711
        ommer_miner_reward = ((U256(8) - ommer_age) * BLOCK_REWARD) // U256(8)
712
        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:
721
    """
722
    Execute a transaction against the provided environment.
723
724
    This function processes the actions needed to execute a transaction.
725
    It decrements the sender's account balance after calculating the gas fee
726
    and refunds them the proper amount after execution. Calling contracts,
727
    deploying code, and incrementing nonces are all examples of actions that
728
    happen within this function or from a call made within this function.
729
730
    Accounts that are marked for deletion are processed and destroyed after
731
    execution.
732
733
    Parameters
734
    ----------
735
    block_env :
736
        Environment for the Ethereum Virtual Machine.
737
    block_output :
738
        The block output for the current block.
739
    tx :
740
        Transaction to execute.
741
    index:
742
        Index of the transaction in the block.
743
744
    """
745
    trie_set(
746
        block_output.transactions_trie,
747
        rlp.encode(index),
748
        encode_transaction(tx),
749
    )
750
751
    intrinsic_gas = validate_transaction(tx)
752
753
    (
754
        sender,
755
        effective_gas_price,
756
    ) = check_transaction(
757
        block_env=block_env,
758
        block_output=block_output,
759
        tx=tx,
760
    )
761
762
    sender_account = get_account(block_env.state, sender)
763
764
    effective_gas_fee = tx.gas * effective_gas_price
765
766
    gas = tx.gas - intrinsic_gas
767
    increment_nonce(block_env.state, sender)
768
769
    sender_balance_after_gas_fee = (
770
        Uint(sender_account.balance) - effective_gas_fee
771
    )
772
    set_account_balance(
773
        block_env.state, sender, U256(sender_balance_after_gas_fee)
774
    )
775
776
    access_list_addresses = set()
777
    access_list_storage_keys = set()
778
    if isinstance(tx, (AccessListTransaction, FeeMarketTransaction)):
779
        for access in tx.access_list:
780
            access_list_addresses.add(access.account)
781
            for slot in access.slots:
782
                access_list_storage_keys.add((access.account, slot))
783
784
    tx_env = vm.TransactionEnvironment(
785
        origin=sender,
786
        gas_price=effective_gas_price,
787
        gas=gas,
788
        access_list_addresses=access_list_addresses,
789
        access_list_storage_keys=access_list_storage_keys,
790
        index_in_block=index,
791
        tx_hash=get_transaction_hash(encode_transaction(tx)),
792
    )
793
794
    message = prepare_message(block_env, tx_env, tx)
795
796
    tx_output = process_message_call(message)
797
798
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
799
    tx_gas_refund = min(
800
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
801
    )
802
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
803
    tx_gas_left = tx.gas - tx_gas_used_after_refund
804
    gas_refund_amount = tx_gas_left * effective_gas_price
805
806
    # For non-1559 transactions effective_gas_price == tx.gas_price
807
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
808
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
809
810
    # refund gas
811
    sender_balance_after_refund = get_account(
812
        block_env.state, sender
813
    ).balance + U256(gas_refund_amount)
814
    set_account_balance(block_env.state, sender, sender_balance_after_refund)
815
816
    # transfer miner fees
817
    coinbase_balance_after_mining_fee = get_account(
818
        block_env.state, block_env.coinbase
819
    ).balance + U256(transaction_fee)
820
    if coinbase_balance_after_mining_fee != 0:
821
        set_account_balance(
822
            block_env.state,
823
            block_env.coinbase,
824
            coinbase_balance_after_mining_fee,
825
        )
826
    elif account_exists_and_is_empty(block_env.state, block_env.coinbase):
827
        destroy_account(block_env.state, block_env.coinbase)
828
829
    for address in tx_output.accounts_to_delete:
830
        destroy_account(block_env.state, address)
831
832
    destroy_touched_empty_accounts(block_env.state, tx_output.touched_accounts)
833
834
    block_output.block_gas_used += tx_gas_used_after_refund
835
836
    receipt = make_receipt(
837
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
838
    )
839
840
    receipt_key = rlp.encode(Uint(index))
841
    block_output.receipt_keys += (receipt_key,)
842
843
    trie_set(
844
        block_output.receipts_trie,
845
        receipt_key,
846
        receipt,
847
    )
848
849
    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:
853
    """
854
    Validates the gas limit for a block.
855
856
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
857
    quotient of the parent block's gas limit and the
858
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
859
    passed through as a parameter is greater than or equal to the *sum* of
860
    the parent's gas and the adjustment delta then the limit for gas is too
861
    high and fails this function's check. Similarly, if the limit is less
862
    than or equal to the *difference* of the parent's gas and the adjustment
863
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
864
    check fails because the gas limit doesn't allow for a sufficient or
865
    reasonable amount of gas to be used on a block.
866
867
    Parameters
868
    ----------
869
    gas_limit :
870
        Gas limit to validate.
871
872
    parent_gas_limit :
873
        Gas limit of the parent block.
874
875
    Returns
876
    -------
877
    check : `bool`
878
        True if gas limit constraints are satisfied, False otherwise.
879
880
    """
881
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
882
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
883
        return False
884
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
885
        return False
886
    if gas_limit < GAS_LIMIT_MINIMUM:
887
        return False
888
889
    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 GENESIS_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:
899
    """
900
    Computes difficulty of a block using its header and parent header.
901
902
    The difficulty is determined by the time the block was created after its
903
    parent. The ``offset`` is calculated using the parent block's difficulty,
904
    ``parent_difficulty``, and the timestamp between blocks. This offset is
905
    then added to the parent difficulty and is stored as the ``difficulty``
906
    variable. If the time between the block and its parent is too short, the
907
    offset will result in a positive number thus making the sum of
908
    ``parent_difficulty`` and ``offset`` to be a greater value in order to
909
    avoid mass forking. But, if the time is long enough, then the offset
910
    results in a negative value making the block less difficult than
911
    its parent.
912
913
    The base standard for a block's difficulty is the predefined value
914
    set for the genesis block since it has no parent. So, a block
915
    can't be less difficult than the genesis block, therefore each block's
916
    difficulty is set to the maximum value between the calculated
917
    difficulty and the ``GENESIS_DIFFICULTY``.
918
919
    Parameters
920
    ----------
921
    block_number :
922
        Block number of the block.
923
    block_timestamp :
924
        Timestamp of the block.
925
    parent_timestamp :
926
        Timestamp of the parent block.
927
    parent_difficulty :
928
        difficulty of the parent block.
929
    parent_has_ommers:
930
        does the parent have ommers.
931
932
    Returns
933
    -------
934
    difficulty : `ethereum.base_types.Uint`
935
        Computed difficulty for a block.
936
937
    """
938
    offset = (
939
        int(parent_difficulty)
940
        // 2048
941
        * max(
942
            (2 if parent_has_ommers else 1)
943
            - int(block_timestamp - parent_timestamp) // 9,
944
            -99,
945
        )
946
    )
947
    difficulty = int(parent_difficulty) + offset
948
    # Historical Note: The difficulty bomb was not present in Ethereum at the
949
    # start of Frontier, but was added shortly after launch. However since the
950
    # bomb has no effect prior to block 200000 we pretend it existed from
951
    # genesis.
952
    # See https://github.com/ethereum/go-ethereum/pull/1588
953
    num_bomb_periods = ((int(block_number) - BOMB_DELAY_BLOCKS) // 100000) - 2
954
    if num_bomb_periods >= 0:
955
        difficulty += 2**num_bomb_periods
956
957
    # Some clients raise the difficulty to `MINIMUM_DIFFICULTY` prior to adding
958
    # the bomb. This bug does not matter because the difficulty is always much
959
    # greater than `MINIMUM_DIFFICULTY` on Mainnet.
960
    return Uint(max(difficulty, int(MINIMUM_DIFFICULTY)))