ethereum.gray_glacier.forkethereum.paris.fork

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

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

Introduction

Entry point for the Ethereum specification.

BLOCK_REWARD

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

BASE_FEE_MAX_CHANGE_DENOMINATOR

53
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

54
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

55
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

56
GAS_LIMIT_MINIMUM = Uint(5000)

MINIMUM_DIFFICULTY

60
MINIMUM_DIFFICULTY = Uint(131072)

MAX_OMMER_DEPTH

61
MAX_OMMER_DEPTH = Uint(6)

BOMB_DELAY_BLOCKS

62
BOMB_DELAY_BLOCKS = 11400000

EMPTY_OMMER_HASH

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

BlockChain

History and current state of the block chain.

60
@dataclass
class BlockChain:

blocks

66
    blocks: List[Block]

state

67
    state: State

chain_id

68
    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:
72
    """
73
    Transforms the state from the previous hard fork (`old`) into the block
74
    chain object for this hard fork and returns it.
75
76
    When forks need to implement an irregular state transition, this function
77
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
78
    an example.
79
80
    Parameters
81
    ----------
82
    old :
83
        Previous block chain object.
84
85
    Returns
86
    -------
87
    new : `BlockChain`
88
        Upgraded block chain object for this hard fork.
89
    """
90
    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]:
94
    """
95
    Obtain the list of hashes of the previous 256 blocks in order of
96
    increasing block number.
97
98
    This function will return less hashes for the first 256 blocks.
99
100
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
101
    therefore this function retrieves them.
102
103
    Parameters
104
    ----------
105
    chain :
106
        History and current state.
107
108
    Returns
109
    -------
110
    recent_block_hashes : `List[Hash32]`
111
        Hashes of the recent 256 blocks in order of increasing block number.
112
    """
113
    recent_blocks = chain.blocks[-255:]
114
    # TODO: This function has not been tested rigorously
115
    if len(recent_blocks) == 0:
116
        return []
117
118
    recent_block_hashes = []
119
120
    for block in recent_blocks:
121
        prev_block_hash = block.header.parent_hash
122
        recent_block_hashes.append(prev_block_hash)
123
124
    # We are computing the hash only for the most recent block and not for
125
    # the rest of the blocks as they have successors which have the hash of
126
    # the current block as parent hash.
127
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
128
    recent_block_hashes.append(most_recent_block_hash)
129
130
    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:
134
    """
135
    Attempts to apply a block to an existing block chain.
136
137
    All parts of the block's contents need to be verified before being added
138
    to the chain. Blocks are verified by ensuring that the contents of the
139
    block make logical sense with the contents of the parent block. The
140
    information in the block's header must also match the corresponding
141
    information in the block.
142
143
    To implement Ethereum, in theory clients are only required to store the
144
    most recent 255 blocks of the chain since as far as execution is
145
    concerned, only those blocks are accessed. Practically, however, clients
146
    should store more blocks to handle reorgs.
147
148
    Parameters
149
    ----------
150
    chain :
151
        History and current state.
152
    block :
153
        Block to apply to `chain`.
154
    """
155
    parent_header = chain.blocks[-1].header
156
    validate_header(block.header, parent_header)
163
    validate_ommers(block.ommers, block.header, chain)
157
    if block.ommers != ():
158
        raise InvalidBlock
159
    apply_body_output = apply_body(
160
        chain.state,
161
        get_last_256_block_hashes(chain),
162
        block.header.coinbase,
163
        block.header.number,
164
        block.header.base_fee_per_gas,
165
        block.header.gas_limit,
166
        block.header.timestamp,
172
        block.header.difficulty,
167
        block.header.prev_randao,
168
        block.transactions,
174
        block.ommers,
169
        chain.chain_id,
170
    )
171
    if apply_body_output.block_gas_used != block.header.gas_used:
172
        raise InvalidBlock(
173
            f"{apply_body_output.block_gas_used} != {block.header.gas_used}"
174
        )
175
    if apply_body_output.transactions_root != block.header.transactions_root:
176
        raise InvalidBlock
177
    if apply_body_output.state_root != block.header.state_root:
178
        raise InvalidBlock
179
    if apply_body_output.receipt_root != block.header.receipt_root:
180
        raise InvalidBlock
181
    if apply_body_output.block_logs_bloom != block.header.bloom:
182
        raise InvalidBlock
183
184
    chain.blocks.append(block)
185
    if len(chain.blocks) > 255:
186
        # Real clients have to store more blocks to deal with reorgs, but the
187
        # protocol only requires the last 255
188
        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:
197
    """
198
    Calculates the base fee per gas for the block.
199
200
    Parameters
201
    ----------
202
    block_gas_limit :
203
        Gas limit of the block for which the base fee is being calculated.
204
    parent_gas_limit :
205
        Gas limit of the parent block.
206
    parent_gas_used :
207
        Gas used in the parent block.
208
    parent_base_fee_per_gas :
209
        Base fee per gas of the parent block.
210
211
    Returns
212
    -------
213
    base_fee_per_gas : `Uint`
214
        Base fee per gas for the block.
215
    """
216
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
217
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
218
        raise InvalidBlock
219
220
    if parent_gas_used == parent_gas_target:
221
        expected_base_fee_per_gas = parent_base_fee_per_gas
222
    elif parent_gas_used > parent_gas_target:
223
        gas_used_delta = parent_gas_used - parent_gas_target
224
225
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
226
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
227
228
        base_fee_per_gas_delta = max(
229
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
230
            Uint(1),
231
        )
232
233
        expected_base_fee_per_gas = (
234
            parent_base_fee_per_gas + base_fee_per_gas_delta
235
        )
236
    else:
237
        gas_used_delta = parent_gas_target - parent_gas_used
238
239
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
240
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
241
242
        base_fee_per_gas_delta = (
243
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
244
        )
245
246
        expected_base_fee_per_gas = (
247
            parent_base_fee_per_gas - base_fee_per_gas_delta
248
        )
249
250
    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

header : Header to check for correctness. parent_header : Parent Header of the header to check for correctness

def validate_header(header: Header, ​​parent_header: Header) -> None:
254
    """
255
    Verifies a block header.
256
257
    In order to consider a block's header valid, the logic for the
258
    quantities in the header should match the logic for the block itself.
259
    For example the header timestamp should be greater than the block's parent
260
    timestamp because the block was created *after* the parent block.
261
    Additionally, the block's number should be directly following the parent
262
    block's number since it is the next block in the sequence.
263
264
    Parameters
265
    ----------
266
    header :
267
        Header to check for correctness.
268
    parent_header :
269
        Parent Header of the header to check for correctness
270
    """
271
    if header.gas_used > header.gas_limit:
272
        raise InvalidBlock
273
274
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
275
        header.gas_limit,
276
        parent_header.gas_limit,
277
        parent_header.gas_used,
278
        parent_header.base_fee_per_gas,
279
    )
280
    if expected_base_fee_per_gas != header.base_fee_per_gas:
281
        raise InvalidBlock
288
289
    parent_has_ommers = parent_header.ommers_hash != EMPTY_OMMER_HASH
282
    if header.timestamp <= parent_header.timestamp:
283
        raise InvalidBlock
284
    if header.number != parent_header.number + Uint(1):
285
        raise InvalidBlock
286
    if len(header.extra_data) > 32:
287
        raise InvalidBlock
296
297
    block_difficulty = calculate_block_difficulty(
298
        header.number,
299
        header.timestamp,
300
        parent_header.timestamp,
301
        parent_header.difficulty,
302
        parent_has_ommers,
303
    )
304
    if header.difficulty != block_difficulty:
288
    if header.difficulty != 0:
289
        raise InvalidBlock
290
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
291
        raise InvalidBlock
292
    if header.ommers_hash != EMPTY_OMMER_HASH:
293
        raise InvalidBlock
294
295
    block_parent_hash = keccak256(rlp.encode(parent_header))
296
    if header.parent_hash != block_parent_hash:
309
        raise InvalidBlock
310
311
    validate_proof_of_work(header)
297
        raise InvalidBlock

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:
315
    """
316
    Generate rlp hash of the header which is to be used for Proof-of-Work
317
    verification.
318
319
    In other words, the PoW artefacts `mix_digest` and `nonce` are ignored
320
    while calculating this hash.
321
322
    A particular PoW is valid for a single hash, that hash is computed by
323
    this function. The `nonce` and `mix_digest` are omitted from this hash
324
    because they are being changed by miners in their search for a sufficient
325
    proof-of-work.
326
327
    Parameters
328
    ----------
329
    header :
330
        The header object for which the hash is to be generated.
331
332
    Returns
333
    -------
334
    hash : `Hash32`
335
        The PoW valid rlp hash of the passed in header.
336
    """
337
    header_data_without_pow_artefacts = (
338
        header.parent_hash,
339
        header.ommers_hash,
340
        header.coinbase,
341
        header.state_root,
342
        header.transactions_root,
343
        header.receipt_root,
344
        header.bloom,
345
        header.difficulty,
346
        header.number,
347
        header.gas_limit,
348
        header.gas_used,
349
        header.timestamp,
350
        header.extra_data,
351
        header.base_fee_per_gas,
352
    )
353
354
    return rlp.rlp_hash(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:
358
    """
359
    Validates the Proof of Work constraints.
360
361
    In order to verify that a miner's proof-of-work is valid for a block, a
362
    ``mix-digest`` and ``result`` are calculated using the ``hashimoto_light``
363
    hash function. The mix digest is a hash of the header and the nonce that
364
    is passed through and it confirms whether or not proof-of-work was done
365
    on the correct block. The result is the actual hash value of the block.
366
367
    Parameters
368
    ----------
369
    header :
370
        Header of interest.
371
    """
372
    header_hash = generate_header_hash_for_pow(header)
373
    # TODO: Memoize this somewhere and read from that data instead of
374
    # calculating cache for every block validation.
375
    cache = generate_cache(header.number)
376
    mix_digest, result = hashimoto_light(
377
        header_hash, header.nonce, cache, dataset_size(header.number)
378
    )
379
    if mix_digest != header.mix_digest:
380
        raise InvalidBlock
381
382
    limit = Uint(U256.MAX_VALUE) + Uint(1)
383
    if Uint.from_be_bytes(result) > (limit // header.difficulty):
384
        raise InvalidBlock

check_transaction

Check if the transaction is includable in the block.

Parameters

tx : The transaction. base_fee_per_gas : The block base fee. gas_available : The gas remaining in the block. chain_id : The ID of the current chain.

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.

def check_transaction(tx: Transaction, ​​base_fee_per_gas: Uint, ​​gas_available: Uint, ​​chain_id: U64) -> Tuple[Address, Uint]:
306
    """
307
    Check if the transaction is includable in the block.
308
309
    Parameters
310
    ----------
311
    tx :
312
        The transaction.
313
    base_fee_per_gas :
314
        The block base fee.
315
    gas_available :
316
        The gas remaining in the block.
317
    chain_id :
318
        The ID of the current chain.
319
320
    Returns
321
    -------
322
    sender_address :
323
        The sender of the transaction.
324
    effective_gas_price :
325
        The price to charge for gas when the transaction is executed.
326
327
    Raises
328
    ------
329
    InvalidBlock :
330
        If the transaction is not includable.
331
    """
332
    if tx.gas > gas_available:
333
        raise InvalidBlock
334
    sender_address = recover_sender(chain_id, tx)
335
336
    if isinstance(tx, FeeMarketTransaction):
337
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
338
            raise InvalidBlock
339
        if tx.max_fee_per_gas < base_fee_per_gas:
340
            raise InvalidBlock
341
342
        priority_fee_per_gas = min(
343
            tx.max_priority_fee_per_gas,
344
            tx.max_fee_per_gas - base_fee_per_gas,
345
        )
346
        effective_gas_price = priority_fee_per_gas + base_fee_per_gas
347
    else:
348
        if tx.gas_price < base_fee_per_gas:
349
            raise InvalidBlock
350
        effective_gas_price = tx.gas_price
351
352
    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.The error from the execution 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[Exception], ​​cumulative_gas_used: Uint, ​​logs: Tuple[Log, ...]) -> Union[Bytes, Receipt]:
361
    """
362
    Make the receipt for a transaction that was executed.
363
364
    Parameters
365
    ----------
366
    tx :
367
        The executed transaction.
368
    error :
456
        Error in the top level frame of the transaction, if any.
369
        The error from the execution if any.
370
    cumulative_gas_used :
371
        The total gas used so far in the block after the transaction was
372
        executed.
373
    logs :
374
        The logs produced by the transaction.
375
376
    Returns
377
    -------
378
    receipt :
379
        The receipt for the transaction.
380
    """
381
    receipt = Receipt(
382
        succeeded=error is None,
383
        cumulative_gas_used=cumulative_gas_used,
384
        bloom=logs_bloom(logs),
385
        logs=logs,
386
    )
387
388
    if isinstance(tx, AccessListTransaction):
389
        return b"\x01" + rlp.encode(receipt)
390
    elif isinstance(tx, FeeMarketTransaction):
391
        return b"\x02" + rlp.encode(receipt)
392
    else:
393
        return receipt

ApplyBodyOutput

Output from applying the block body to the present state.

Contains the following:

block_gas_used : ethereum.base_types.Uint Gas used for executing all transactions. transactions_root : ethereum.fork_types.Root Trie root of all the transactions in the block. receipt_root : ethereum.fork_types.Root Trie root of all the receipts in the block. block_logs_bloom : Bloom Logs bloom of all the logs included in all the transactions of the block. state_root : ethereum.fork_types.Root State root after all transactions have been executed.

396
@dataclass
class ApplyBodyOutput:

block_gas_used

416
    block_gas_used: Uint

transactions_root

417
    transactions_root: Root

receipt_root

418
    receipt_root: Root

block_logs_bloom

419
    block_logs_bloom: Bloom

state_root

420
    state_root: Root

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

state : Current account state. block_hashes : List of hashes of the previous 256 blocks in the order of increasing block number. coinbase : Address of account which receives block reward and transaction fees. block_number : Position of the block within the chain. base_fee_per_gas : Base fee per gas of within the block. block_gas_limit : Initial amount of gas available for execution in this block. block_time : Time the block was produced, measured in seconds since the epoch. block_difficulty :prev_randao : Difficulty of the block.The previous randao from the beacon chain. transactions : Transactions included in the block. ommers : Headers of ancestor blocks which are not direct parents (formerly uncles.) chain_id : ID of the executing chain.

Returns

apply_body_output : ApplyBodyOutput Output of applying the block body to the state.

def apply_body(state: State, ​​block_hashes: List[Hash32], ​​coinbase: Address, ​​block_number: Uint, ​​base_fee_per_gas: Uint, ​​block_gas_limit: Uint, ​​block_time: U256, ​​block_difficultyprev_randao: UintBytes32, ​​transactions: Tuple[Union[LegacyTransaction, Bytes], ...], ​​ommers: Tuple[Header, ...], ​​chain_id: U64) -> ApplyBodyOutput:
435
    """
436
    Executes a block.
437
438
    Many of the contents of a block are stored in data structures called
439
    tries. There is a transactions trie which is similar to a ledger of the
440
    transactions stored in the current block. There is also a receipts trie
441
    which stores the results of executing a transaction, like the post state
442
    and gas used. This function creates and executes the block that is to be
443
    added to the chain.
444
445
    Parameters
446
    ----------
447
    state :
448
        Current account state.
449
    block_hashes :
450
        List of hashes of the previous 256 blocks in the order of
451
        increasing block number.
452
    coinbase :
453
        Address of account which receives block reward and transaction fees.
454
    block_number :
455
        Position of the block within the chain.
456
    base_fee_per_gas :
457
        Base fee per gas of within the block.
458
    block_gas_limit :
459
        Initial amount of gas available for execution in this block.
460
    block_time :
461
        Time the block was produced, measured in seconds since the epoch.
550
    block_difficulty :
551
        Difficulty of the block.
462
    prev_randao :
463
        The previous randao from the beacon chain.
464
    transactions :
465
        Transactions included in the block.
466
    ommers :
467
        Headers of ancestor blocks which are not direct parents (formerly
468
        uncles.)
469
    chain_id :
470
        ID of the executing chain.
471
472
    Returns
473
    -------
474
    apply_body_output : `ApplyBodyOutput`
475
        Output of applying the block body to the state.
476
    """
477
    gas_available = block_gas_limit
478
    transactions_trie: Trie[
479
        Bytes, Optional[Union[Bytes, LegacyTransaction]]
480
    ] = Trie(secured=False, default=None)
481
    receipts_trie: Trie[Bytes, Optional[Union[Bytes, Receipt]]] = Trie(
482
        secured=False, default=None
483
    )
484
    block_logs: Tuple[Log, ...] = ()
485
486
    for i, tx in enumerate(map(decode_transaction, transactions)):
487
        trie_set(
488
            transactions_trie, rlp.encode(Uint(i)), encode_transaction(tx)
489
        )
490
491
        sender_address, effective_gas_price = check_transaction(
492
            tx, base_fee_per_gas, gas_available, chain_id
493
        )
494
495
        env = vm.Environment(
496
            caller=sender_address,
497
            origin=sender_address,
498
            block_hashes=block_hashes,
499
            coinbase=coinbase,
500
            number=block_number,
501
            gas_limit=block_gas_limit,
502
            base_fee_per_gas=base_fee_per_gas,
503
            gas_price=effective_gas_price,
504
            time=block_time,
593
            difficulty=block_difficulty,
505
            prev_randao=prev_randao,
506
            state=state,
507
            chain_id=chain_id,
508
            traces=[],
509
        )
510
511
        gas_used, logs, error = process_transaction(env, tx)
512
        gas_available -= gas_used
513
514
        receipt = make_receipt(
515
            tx, error, (block_gas_limit - gas_available), logs
516
        )
517
518
        trie_set(
519
            receipts_trie,
520
            rlp.encode(Uint(i)),
521
            receipt,
522
        )
523
524
        block_logs += logs
525
614
    pay_rewards(state, block_number, coinbase, ommers)
615
526
    block_gas_used = block_gas_limit - gas_available
527
528
    block_logs_bloom = logs_bloom(block_logs)
529
530
    return ApplyBodyOutput(
531
        block_gas_used,
532
        root(transactions_trie),
533
        root(receipts_trie),
534
        block_logs_bloom,
535
        state_root(state),
536
    )

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:
632
    """
633
    Validates the ommers mentioned in the block.
634
635
    An ommer block is a block that wasn't canonically added to the
636
    blockchain because it wasn't validated as fast as the canonical block
637
    but was mined at the same time.
638
639
    To be considered valid, the ommers must adhere to the rules defined in
640
    the Ethereum protocol. The maximum amount of ommers is 2 per block and
641
    there cannot be duplicate ommers in a block. Many of the other ommer
642
    constraints are listed in the in-line comments of this function.
643
644
    Parameters
645
    ----------
646
    ommers :
647
        List of ommers mentioned in the current block.
648
    block_header:
649
        The header of current block.
650
    chain :
651
        History and current state.
652
    """
653
    block_hash = rlp.rlp_hash(block_header)
654
    if rlp.rlp_hash(ommers) != block_header.ommers_hash:
655
        raise InvalidBlock
656
657
    if len(ommers) == 0:
658
        # Nothing to validate
659
        return
660
661
    # Check that each ommer satisfies the constraints of a header
662
    for ommer in ommers:
663
        if Uint(1) > ommer.number or ommer.number >= block_header.number:
664
            raise InvalidBlock
665
        ommer_parent_header = chain.blocks[
666
            -(block_header.number - ommer.number) - 1
667
        ].header
668
        validate_header(ommer, ommer_parent_header)
669
    if len(ommers) > 2:
670
        raise InvalidBlock
671
672
    ommers_hashes = [rlp.rlp_hash(ommer) for ommer in ommers]
673
    if len(ommers_hashes) != len(set(ommers_hashes)):
674
        raise InvalidBlock
675
676
    recent_canonical_blocks = chain.blocks[-(MAX_OMMER_DEPTH + Uint(1)) :]
677
    recent_canonical_block_hashes = {
678
        rlp.rlp_hash(block.header) for block in recent_canonical_blocks
679
    }
680
    recent_ommers_hashes: Set[Hash32] = set()
681
    for block in recent_canonical_blocks:
682
        recent_ommers_hashes = recent_ommers_hashes.union(
683
            {rlp.rlp_hash(ommer) for ommer in block.ommers}
684
        )
685
686
    for ommer_index, ommer in enumerate(ommers):
687
        ommer_hash = ommers_hashes[ommer_index]
688
        if ommer_hash == block_hash:
689
            raise InvalidBlock
690
        if ommer_hash in recent_canonical_block_hashes:
691
            raise InvalidBlock
692
        if ommer_hash in recent_ommers_hashes:
693
            raise InvalidBlock
694
695
        # Ommer age with respect to the current block. For example, an age of
696
        # 1 indicates that the ommer is a sibling of previous block.
697
        ommer_age = block_header.number - ommer.number
698
        if Uint(1) > ommer_age or ommer_age > MAX_OMMER_DEPTH:
699
            raise InvalidBlock
700
        if ommer.parent_hash not in recent_canonical_block_hashes:
701
            raise InvalidBlock
702
        if ommer.parent_hash == block_header.parent_hash:
703
            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:
712
    """
713
    Pay rewards to the block miner as well as the ommers miners.
714
715
    The miner of the canonical block is rewarded with the predetermined
716
    block reward, ``BLOCK_REWARD``, plus a variable award based off of the
717
    number of ommer blocks that were mined around the same time, and included
718
    in the canonical block's header. An ommer block is a block that wasn't
719
    added to the canonical blockchain because it wasn't validated as fast as
720
    the accepted block but was mined at the same time. Although not all blocks
721
    that are mined are added to the canonical chain, miners are still paid a
722
    reward for their efforts. This reward is called an ommer reward and is
723
    calculated based on the number associated with the ommer block that they
724
    mined.
725
726
    Parameters
727
    ----------
728
    state :
729
        Current account state.
730
    block_number :
731
        Position of the block within the chain.
732
    coinbase :
733
        Address of account which receives block reward and transaction fees.
734
    ommers :
735
        List of ommers mentioned in the current block.
736
    """
737
    ommer_count = U256(len(ommers))
738
    miner_reward = BLOCK_REWARD + (ommer_count * (BLOCK_REWARD // U256(32)))
739
    create_ether(state, coinbase, miner_reward)
740
741
    for ommer in ommers:
742
        # Ommer age with respect to the current block.
743
        ommer_age = U256(block_number - ommer.number)
744
        ommer_miner_reward = ((U256(8) - ommer_age) * BLOCK_REWARD) // U256(8)
745
        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 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

env : Environment for the Ethereum Virtual Machine. tx : Transaction to execute.

Returns

gas_left : ethereum.base_types.U256 Remaining gas after execution. logs : Tuple[ethereum.blocks.Log, ...] Logs generated during execution.

def process_transaction(env: ethereum.gray_glacier.vm.Environmentethereum.paris.vm.Environment, ​​tx: Transaction) -> Tuple[Uint, Tuple[Log, ...], Optional[Exception]]:
542
    """
543
    Execute a transaction against the provided environment.
544
545
    This function processes the actions needed to execute a transaction.
546
    It decrements the sender's account after calculating the gas fee and
547
    refunds them the proper amount after execution. Calling contracts,
548
    deploying code, and incrementing nonces are all examples of actions that
549
    happen within this function or from a call made within this function.
550
551
    Accounts that are marked for deletion are processed and destroyed after
552
    execution.
553
554
    Parameters
555
    ----------
556
    env :
557
        Environment for the Ethereum Virtual Machine.
558
    tx :
559
        Transaction to execute.
560
561
    Returns
562
    -------
563
    gas_left : `ethereum.base_types.U256`
564
        Remaining gas after execution.
565
    logs : `Tuple[ethereum.blocks.Log, ...]`
566
        Logs generated during execution.
567
    """
568
    if not validate_transaction(tx):
569
        raise InvalidBlock
570
571
    sender = env.origin
572
    sender_account = get_account(env.state, sender)
573
783
    max_gas_fee: Uint
574
    if isinstance(tx, FeeMarketTransaction):
785
        max_gas_fee = Uint(tx.gas) * Uint(tx.max_fee_per_gas)
575
        max_gas_fee = tx.gas * tx.max_fee_per_gas
576
    else:
787
        max_gas_fee = Uint(tx.gas) * Uint(tx.gas_price)
577
        max_gas_fee = tx.gas * tx.gas_price
578
    if sender_account.nonce != tx.nonce:
579
        raise InvalidBlock
580
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
581
        raise InvalidBlock
582
    if sender_account.code != bytearray():
583
        raise InvalidSenderError("not EOA")
584
585
    effective_gas_fee = tx.gas * env.gas_price
586
587
    gas = tx.gas - calculate_intrinsic_cost(tx)
588
    increment_nonce(env.state, sender)
589
590
    sender_balance_after_gas_fee = (
591
        Uint(sender_account.balance) - effective_gas_fee
592
    )
593
    set_account_balance(env.state, sender, U256(sender_balance_after_gas_fee))
594
595
    preaccessed_addresses = set()
596
    preaccessed_storage_keys = set()
597
    if isinstance(tx, (AccessListTransaction, FeeMarketTransaction)):
598
        for address, keys in tx.access_list:
599
            preaccessed_addresses.add(address)
600
            for key in keys:
601
                preaccessed_storage_keys.add((address, key))
602
603
    message = prepare_message(
604
        sender,
605
        tx.to,
606
        tx.value,
607
        tx.data,
608
        gas,
609
        env,
610
        preaccessed_addresses=frozenset(preaccessed_addresses),
611
        preaccessed_storage_keys=frozenset(preaccessed_storage_keys),
612
    )
613
614
    output = process_message_call(message, env)
615
616
    gas_used = tx.gas - output.gas_left
617
    gas_refund = min(gas_used // Uint(5), Uint(output.refund_counter))
618
    gas_refund_amount = (output.gas_left + gas_refund) * env.gas_price
619
620
    # For non-1559 transactions env.gas_price == tx.gas_price
621
    priority_fee_per_gas = env.gas_price - env.base_fee_per_gas
622
    transaction_fee = (
623
        tx.gas - output.gas_left - gas_refund
624
    ) * priority_fee_per_gas
625
626
    total_gas_used = gas_used - gas_refund
627
628
    # refund gas
629
    sender_balance_after_refund = get_account(
630
        env.state, sender
631
    ).balance + U256(gas_refund_amount)
632
    set_account_balance(env.state, sender, sender_balance_after_refund)
633
634
    # transfer miner fees
635
    coinbase_balance_after_mining_fee = get_account(
636
        env.state, env.coinbase
637
    ).balance + U256(transaction_fee)
638
    if coinbase_balance_after_mining_fee != 0:
639
        set_account_balance(
640
            env.state, env.coinbase, coinbase_balance_after_mining_fee
641
        )
642
    elif account_exists_and_is_empty(env.state, env.coinbase):
643
        destroy_account(env.state, env.coinbase)
644
645
    for address in output.accounts_to_delete:
646
        destroy_account(env.state, address)
647
648
    for address in output.touched_accounts:
649
        if account_exists_and_is_empty(env.state, address):
650
            destroy_account(env.state, address)
651
652
    return total_gas_used, output.logs, output.error

compute_header_hash

Computes the hash of a block header.

The header hash of a block is the canonical hash that is used to refer to a specific block and completely distinguishes a block from another.

keccak256 is a function that produces a 256 bit hash of any input. It also takes in any number of bytes as an input and produces a single hash for them. A hash is a completely unique output for a single input. So an input corresponds to one unique hash that can be used to identify the input exactly.

Prior to using the keccak256 hash function, the header must be encoded using the Recursive-Length Prefix. See :ref:rlp. RLP encoding the header converts it into a space-efficient format that allows for easy transfer of data between nodes. The purpose of RLP is to encode arbitrarily nested arrays of binary data, and RLP is the primary encoding method used to serialize objects in Ethereum's execution layer. The only purpose of RLP is to encode structure; encoding specific data types (e.g. strings, floats) is left up to higher-order protocols.

Parameters

header : Header of interest.

Returns

hash : ethereum.crypto.hash.Hash32 Hash of the header.

def compute_header_hash(header: Header) -> Hash32:
656
    """
657
    Computes the hash of a block header.
658
659
    The header hash of a block is the canonical hash that is used to refer
660
    to a specific block and completely distinguishes a block from another.
661
662
    ``keccak256`` is a function that produces a 256 bit hash of any input.
663
    It also takes in any number of bytes as an input and produces a single
664
    hash for them. A hash is a completely unique output for a single input.
665
    So an input corresponds to one unique hash that can be used to identify
666
    the input exactly.
667
668
    Prior to using the ``keccak256`` hash function, the header must be
669
    encoded using the Recursive-Length Prefix. See :ref:`rlp`.
670
    RLP encoding the header converts it into a space-efficient format that
671
    allows for easy transfer of data between nodes. The purpose of RLP is to
672
    encode arbitrarily nested arrays of binary data, and RLP is the primary
673
    encoding method used to serialize objects in Ethereum's execution layer.
674
    The only purpose of RLP is to encode structure; encoding specific data
675
    types (e.g. strings, floats) is left up to higher-order protocols.
676
677
    Parameters
678
    ----------
679
    header :
680
        Header of interest.
681
682
    Returns
683
    -------
684
    hash : `ethereum.crypto.hash.Hash32`
685
        Hash of the header.
686
    """
687
    return keccak256(rlp.encode(header))

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:
691
    """
692
    Validates the gas limit for a block.
693
694
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
695
    quotient of the parent block's gas limit and the
696
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
697
    passed through as a parameter is greater than or equal to the *sum* of
698
    the parent's gas and the adjustment delta then the limit for gas is too
699
    high and fails this function's check. Similarly, if the limit is less
700
    than or equal to the *difference* of the parent's gas and the adjustment
701
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
702
    check fails because the gas limit doesn't allow for a sufficient or
703
    reasonable amount of gas to be used on a block.
704
705
    Parameters
706
    ----------
707
    gas_limit :
708
        Gas limit to validate.
709
710
    parent_gas_limit :
711
        Gas limit of the parent block.
712
713
    Returns
714
    -------
715
    check : `bool`
716
        True if gas limit constraints are satisfied, False otherwise.
717
    """
718
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
719
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
720
        return False
721
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
722
        return False
723
    if gas_limit < GAS_LIMIT_MINIMUM:
724
        return False
725
726
    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:
946
    """
947
    Computes difficulty of a block using its header and parent header.
948
949
    The difficulty is determined by the time the block was created after its
950
    parent. The ``offset`` is calculated using the parent block's difficulty,
951
    ``parent_difficulty``, and the timestamp between blocks. This offset is
952
    then added to the parent difficulty and is stored as the ``difficulty``
953
    variable. If the time between the block and its parent is too short, the
954
    offset will result in a positive number thus making the sum of
955
    ``parent_difficulty`` and ``offset`` to be a greater value in order to
956
    avoid mass forking. But, if the time is long enough, then the offset
957
    results in a negative value making the block less difficult than
958
    its parent.
959
960
    The base standard for a block's difficulty is the predefined value
961
    set for the genesis block since it has no parent. So, a block
962
    can't be less difficult than the genesis block, therefore each block's
963
    difficulty is set to the maximum value between the calculated
964
    difficulty and the ``GENESIS_DIFFICULTY``.
965
966
    Parameters
967
    ----------
968
    block_number :
969
        Block number of the block.
970
    block_timestamp :
971
        Timestamp of the block.
972
    parent_timestamp :
973
        Timestamp of the parent block.
974
    parent_difficulty :
975
        difficulty of the parent block.
976
    parent_has_ommers:
977
        does the parent have ommers.
978
979
    Returns
980
    -------
981
    difficulty : `ethereum.base_types.Uint`
982
        Computed difficulty for a block.
983
    """
984
    offset = (
985
        int(parent_difficulty)
986
        // 2048
987
        * max(
988
            (2 if parent_has_ommers else 1)
989
            - int(block_timestamp - parent_timestamp) // 9,
990
            -99,
991
        )
992
    )
993
    difficulty = int(parent_difficulty) + offset
994
    # Historical Note: The difficulty bomb was not present in Ethereum at the
995
    # start of Frontier, but was added shortly after launch. However since the
996
    # bomb has no effect prior to block 200000 we pretend it existed from
997
    # genesis.
998
    # See https://github.com/ethereum/go-ethereum/pull/1588
999
    num_bomb_periods = ((int(block_number) - BOMB_DELAY_BLOCKS) // 100000) - 2
1000
    if num_bomb_periods >= 0:
1001
        difficulty += 2**num_bomb_periods
1002
1003
    # Some clients raise the difficulty to `MINIMUM_DIFFICULTY` prior to adding
1004
    # the bomb. This bug does not matter because the difficulty is always much
1005
    # greater than `MINIMUM_DIFFICULTY` on Mainnet.
1006
    return Uint(max(difficulty, int(MINIMUM_DIFFICULTY)))