ethereum.gray_glacier.forkethereum.paris.fork

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

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

Introduction

Entry point for the Ethereum specification.

BLOCK_REWARD

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

BASE_FEE_MAX_CHANGE_DENOMINATOR

57
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

58
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

59
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

60
GAS_LIMIT_MINIMUM = Uint(5000)

MINIMUM_DIFFICULTY

64
MINIMUM_DIFFICULTY = Uint(131072)

MAX_OMMER_DEPTH

65
MAX_OMMER_DEPTH = Uint(6)

BOMB_DELAY_BLOCKS

66
BOMB_DELAY_BLOCKS = 11400000

EMPTY_OMMER_HASH

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

BlockChain

History and current state of the block chain.

64
@dataclass
class BlockChain:

blocks

70
    blocks: List[Block]

state

71
    state: State

chain_id

72
    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:
76
    """
77
    Transforms the state from the previous hard fork (`old`) into the block
78
    chain object for this hard fork and returns it.
79
80
    When forks need to implement an irregular state transition, this function
81
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
82
    an example.
83
84
    Parameters
85
    ----------
86
    old :
87
        Previous block chain object.
88
89
    Returns
90
    -------
91
    new : `BlockChain`
92
        Upgraded block chain object for this hard fork.
93
    """
94
    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]:
98
    """
99
    Obtain the list of hashes of the previous 256 blocks in order of
100
    increasing block number.
101
102
    This function will return less hashes for the first 256 blocks.
103
104
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
105
    therefore this function retrieves them.
106
107
    Parameters
108
    ----------
109
    chain :
110
        History and current state.
111
112
    Returns
113
    -------
114
    recent_block_hashes : `List[Hash32]`
115
        Hashes of the recent 256 blocks in order of increasing block number.
116
    """
117
    recent_blocks = chain.blocks[-255:]
118
    # TODO: This function has not been tested rigorously
119
    if len(recent_blocks) == 0:
120
        return []
121
122
    recent_block_hashes = []
123
124
    for block in recent_blocks:
125
        prev_block_hash = block.header.parent_hash
126
        recent_block_hashes.append(prev_block_hash)
127
128
    # We are computing the hash only for the most recent block and not for
129
    # the rest of the blocks as they have successors which have the hash of
130
    # the current block as parent hash.
131
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
132
    recent_block_hashes.append(most_recent_block_hash)
133
134
    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:
138
    """
139
    Attempts to apply a block to an existing block chain.
140
141
    All parts of the block's contents need to be verified before being added
142
    to the chain. Blocks are verified by ensuring that the contents of the
143
    block make logical sense with the contents of the parent block. The
144
    information in the block's header must also match the corresponding
145
    information in the block.
146
147
    To implement Ethereum, in theory clients are only required to store the
148
    most recent 255 blocks of the chain since as far as execution is
149
    concerned, only those blocks are accessed. Practically, however, clients
150
    should store more blocks to handle reorgs.
151
152
    Parameters
153
    ----------
154
    chain :
155
        History and current state.
156
    block :
157
        Block to apply to `chain`.
158
    """
159
    parent_header = chain.blocks[-1].header
160
    validate_header(block.header, parent_header)
167
    validate_ommers(block.ommers, block.header, chain)
161
    if block.ommers != ():
162
        raise InvalidBlock
163
    apply_body_output = apply_body(
164
        chain.state,
165
        get_last_256_block_hashes(chain),
166
        block.header.coinbase,
167
        block.header.number,
168
        block.header.base_fee_per_gas,
169
        block.header.gas_limit,
170
        block.header.timestamp,
176
        block.header.difficulty,
171
        block.header.prev_randao,
172
        block.transactions,
178
        block.ommers,
173
        chain.chain_id,
174
    )
175
    if apply_body_output.block_gas_used != block.header.gas_used:
176
        raise InvalidBlock(
177
            f"{apply_body_output.block_gas_used} != {block.header.gas_used}"
178
        )
179
    if apply_body_output.transactions_root != block.header.transactions_root:
180
        raise InvalidBlock
181
    if apply_body_output.state_root != block.header.state_root:
182
        raise InvalidBlock
183
    if apply_body_output.receipt_root != block.header.receipt_root:
184
        raise InvalidBlock
185
    if apply_body_output.block_logs_bloom != block.header.bloom:
186
        raise InvalidBlock
187
188
    chain.blocks.append(block)
189
    if len(chain.blocks) > 255:
190
        # Real clients have to store more blocks to deal with reorgs, but the
191
        # protocol only requires the last 255
192
        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:
201
    """
202
    Calculates the base fee per gas for the block.
203
204
    Parameters
205
    ----------
206
    block_gas_limit :
207
        Gas limit of the block for which the base fee is being calculated.
208
    parent_gas_limit :
209
        Gas limit of the parent block.
210
    parent_gas_used :
211
        Gas used in the parent block.
212
    parent_base_fee_per_gas :
213
        Base fee per gas of the parent block.
214
215
    Returns
216
    -------
217
    base_fee_per_gas : `Uint`
218
        Base fee per gas for the block.
219
    """
220
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
221
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
222
        raise InvalidBlock
223
224
    if parent_gas_used == parent_gas_target:
225
        expected_base_fee_per_gas = parent_base_fee_per_gas
226
    elif parent_gas_used > parent_gas_target:
227
        gas_used_delta = parent_gas_used - parent_gas_target
228
229
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
230
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
231
232
        base_fee_per_gas_delta = max(
233
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
234
            Uint(1),
235
        )
236
237
        expected_base_fee_per_gas = (
238
            parent_base_fee_per_gas + base_fee_per_gas_delta
239
        )
240
    else:
241
        gas_used_delta = parent_gas_target - parent_gas_used
242
243
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
244
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
245
246
        base_fee_per_gas_delta = (
247
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
248
        )
249
250
        expected_base_fee_per_gas = (
251
            parent_base_fee_per_gas - base_fee_per_gas_delta
252
        )
253
254
    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:
258
    """
259
    Verifies a block header.
260
261
    In order to consider a block's header valid, the logic for the
262
    quantities in the header should match the logic for the block itself.
263
    For example the header timestamp should be greater than the block's parent
264
    timestamp because the block was created *after* the parent block.
265
    Additionally, the block's number should be directly following the parent
266
    block's number since it is the next block in the sequence.
267
268
    Parameters
269
    ----------
270
    header :
271
        Header to check for correctness.
272
    parent_header :
273
        Parent Header of the header to check for correctness
274
    """
275
    if header.gas_used > header.gas_limit:
276
        raise InvalidBlock
277
278
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
279
        header.gas_limit,
280
        parent_header.gas_limit,
281
        parent_header.gas_used,
282
        parent_header.base_fee_per_gas,
283
    )
284
    if expected_base_fee_per_gas != header.base_fee_per_gas:
285
        raise InvalidBlock
292
293
    parent_has_ommers = parent_header.ommers_hash != EMPTY_OMMER_HASH
286
    if header.timestamp <= parent_header.timestamp:
287
        raise InvalidBlock
288
    if header.number != parent_header.number + Uint(1):
289
        raise InvalidBlock
290
    if len(header.extra_data) > 32:
291
        raise InvalidBlock
300
301
    block_difficulty = calculate_block_difficulty(
302
        header.number,
303
        header.timestamp,
304
        parent_header.timestamp,
305
        parent_header.difficulty,
306
        parent_has_ommers,
307
    )
308
    if header.difficulty != block_difficulty:
292
    if header.difficulty != 0:
293
        raise InvalidBlock
294
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
295
        raise InvalidBlock
296
    if header.ommers_hash != EMPTY_OMMER_HASH:
297
        raise InvalidBlock
298
299
    block_parent_hash = keccak256(rlp.encode(parent_header))
300
    if header.parent_hash != block_parent_hash:
313
        raise InvalidBlock
314
315
    validate_proof_of_work(header)
301
        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:
319
    """
320
    Generate rlp hash of the header which is to be used for Proof-of-Work
321
    verification.
322
323
    In other words, the PoW artefacts `mix_digest` and `nonce` are ignored
324
    while calculating this hash.
325
326
    A particular PoW is valid for a single hash, that hash is computed by
327
    this function. The `nonce` and `mix_digest` are omitted from this hash
328
    because they are being changed by miners in their search for a sufficient
329
    proof-of-work.
330
331
    Parameters
332
    ----------
333
    header :
334
        The header object for which the hash is to be generated.
335
336
    Returns
337
    -------
338
    hash : `Hash32`
339
        The PoW valid rlp hash of the passed in header.
340
    """
341
    header_data_without_pow_artefacts = (
342
        header.parent_hash,
343
        header.ommers_hash,
344
        header.coinbase,
345
        header.state_root,
346
        header.transactions_root,
347
        header.receipt_root,
348
        header.bloom,
349
        header.difficulty,
350
        header.number,
351
        header.gas_limit,
352
        header.gas_used,
353
        header.timestamp,
354
        header.extra_data,
355
        header.base_fee_per_gas,
356
    )
357
358
    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:
362
    """
363
    Validates the Proof of Work constraints.
364
365
    In order to verify that a miner's proof-of-work is valid for a block, a
366
    ``mix-digest`` and ``result`` are calculated using the ``hashimoto_light``
367
    hash function. The mix digest is a hash of the header and the nonce that
368
    is passed through and it confirms whether or not proof-of-work was done
369
    on the correct block. The result is the actual hash value of the block.
370
371
    Parameters
372
    ----------
373
    header :
374
        Header of interest.
375
    """
376
    header_hash = generate_header_hash_for_pow(header)
377
    # TODO: Memoize this somewhere and read from that data instead of
378
    # calculating cache for every block validation.
379
    cache = generate_cache(header.number)
380
    mix_digest, result = hashimoto_light(
381
        header_hash, header.nonce, cache, dataset_size(header.number)
382
    )
383
    if mix_digest != header.mix_digest:
384
        raise InvalidBlock
385
386
    limit = Uint(U256.MAX_VALUE) + Uint(1)
387
    if Uint.from_be_bytes(result) > (limit // header.difficulty):
388
        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]:
310
    """
311
    Check if the transaction is includable in the block.
312
313
    Parameters
314
    ----------
315
    tx :
316
        The transaction.
317
    base_fee_per_gas :
318
        The block base fee.
319
    gas_available :
320
        The gas remaining in the block.
321
    chain_id :
322
        The ID of the current chain.
323
324
    Returns
325
    -------
326
    sender_address :
327
        The sender of the transaction.
328
    effective_gas_price :
329
        The price to charge for gas when the transaction is executed.
330
331
    Raises
332
    ------
333
    InvalidBlock :
334
        If the transaction is not includable.
335
    """
336
    if tx.gas > gas_available:
337
        raise InvalidBlock
338
    sender_address = recover_sender(chain_id, tx)
339
340
    if isinstance(tx, FeeMarketTransaction):
341
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
342
            raise InvalidBlock
343
        if tx.max_fee_per_gas < base_fee_per_gas:
344
            raise InvalidBlock
345
346
        priority_fee_per_gas = min(
347
            tx.max_priority_fee_per_gas,
348
            tx.max_fee_per_gas - base_fee_per_gas,
349
        )
350
        effective_gas_price = priority_fee_per_gas + base_fee_per_gas
351
    else:
352
        if tx.gas_price < base_fee_per_gas:
353
            raise InvalidBlock
354
        effective_gas_price = tx.gas_price
355
356
    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[EthereumException], ​​cumulative_gas_used: Uint, ​​logs: Tuple[Log, ...]) -> Union[Bytes, Receipt]:
365
    """
366
    Make the receipt for a transaction that was executed.
367
368
    Parameters
369
    ----------
370
    tx :
371
        The executed transaction.
372
    error :
460
        Error in the top level frame of the transaction, if any.
373
        The error from the execution if any.
374
    cumulative_gas_used :
375
        The total gas used so far in the block after the transaction was
376
        executed.
377
    logs :
378
        The logs produced by the transaction.
379
380
    Returns
381
    -------
382
    receipt :
383
        The receipt for the transaction.
384
    """
385
    receipt = Receipt(
386
        succeeded=error is None,
387
        cumulative_gas_used=cumulative_gas_used,
388
        bloom=logs_bloom(logs),
389
        logs=logs,
390
    )
391
392
    if isinstance(tx, AccessListTransaction):
393
        return b"\x01" + rlp.encode(receipt)
394
    elif isinstance(tx, FeeMarketTransaction):
395
        return b"\x02" + rlp.encode(receipt)
396
    else:
397
        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.

400
@dataclass
class ApplyBodyOutput:

block_gas_used

420
    block_gas_used: Uint

transactions_root

421
    transactions_root: Root

receipt_root

422
    receipt_root: Root

block_logs_bloom

423
    block_logs_bloom: Bloom

state_root

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

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:
636
    """
637
    Validates the ommers mentioned in the block.
638
639
    An ommer block is a block that wasn't canonically added to the
640
    blockchain because it wasn't validated as fast as the canonical block
641
    but was mined at the same time.
642
643
    To be considered valid, the ommers must adhere to the rules defined in
644
    the Ethereum protocol. The maximum amount of ommers is 2 per block and
645
    there cannot be duplicate ommers in a block. Many of the other ommer
646
    constraints are listed in the in-line comments of this function.
647
648
    Parameters
649
    ----------
650
    ommers :
651
        List of ommers mentioned in the current block.
652
    block_header:
653
        The header of current block.
654
    chain :
655
        History and current state.
656
    """
657
    block_hash = keccak256(rlp.encode(block_header))
658
    if keccak256(rlp.encode(ommers)) != block_header.ommers_hash:
659
        raise InvalidBlock
660
661
    if len(ommers) == 0:
662
        # Nothing to validate
663
        return
664
665
    # Check that each ommer satisfies the constraints of a header
666
    for ommer in ommers:
667
        if Uint(1) > ommer.number or ommer.number >= block_header.number:
668
            raise InvalidBlock
669
        ommer_parent_header = chain.blocks[
670
            -(block_header.number - ommer.number) - 1
671
        ].header
672
        validate_header(ommer, ommer_parent_header)
673
    if len(ommers) > 2:
674
        raise InvalidBlock
675
676
    ommers_hashes = [keccak256(rlp.encode(ommer)) for ommer in ommers]
677
    if len(ommers_hashes) != len(set(ommers_hashes)):
678
        raise InvalidBlock
679
680
    recent_canonical_blocks = chain.blocks[-(MAX_OMMER_DEPTH + Uint(1)) :]
681
    recent_canonical_block_hashes = {
682
        keccak256(rlp.encode(block.header))
683
        for block in recent_canonical_blocks
684
    }
685
    recent_ommers_hashes: Set[Hash32] = set()
686
    for block in recent_canonical_blocks:
687
        recent_ommers_hashes = recent_ommers_hashes.union(
688
            {keccak256(rlp.encode(ommer)) for ommer in block.ommers}
689
        )
690
691
    for ommer_index, ommer in enumerate(ommers):
692
        ommer_hash = ommers_hashes[ommer_index]
693
        if ommer_hash == block_hash:
694
            raise InvalidBlock
695
        if ommer_hash in recent_canonical_block_hashes:
696
            raise InvalidBlock
697
        if ommer_hash in recent_ommers_hashes:
698
            raise InvalidBlock
699
700
        # Ommer age with respect to the current block. For example, an age of
701
        # 1 indicates that the ommer is a sibling of previous block.
702
        ommer_age = block_header.number - ommer.number
703
        if Uint(1) > ommer_age or ommer_age > MAX_OMMER_DEPTH:
704
            raise InvalidBlock
705
        if ommer.parent_hash not in recent_canonical_block_hashes:
706
            raise InvalidBlock
707
        if ommer.parent_hash == block_header.parent_hash:
708
            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:
717
    """
718
    Pay rewards to the block miner as well as the ommers miners.
719
720
    The miner of the canonical block is rewarded with the predetermined
721
    block reward, ``BLOCK_REWARD``, plus a variable award based off of the
722
    number of ommer blocks that were mined around the same time, and included
723
    in the canonical block's header. An ommer block is a block that wasn't
724
    added to the canonical blockchain because it wasn't validated as fast as
725
    the accepted block but was mined at the same time. Although not all blocks
726
    that are mined are added to the canonical chain, miners are still paid a
727
    reward for their efforts. This reward is called an ommer reward and is
728
    calculated based on the number associated with the ommer block that they
729
    mined.
730
731
    Parameters
732
    ----------
733
    state :
734
        Current account state.
735
    block_number :
736
        Position of the block within the chain.
737
    coinbase :
738
        Address of account which receives block reward and transaction fees.
739
    ommers :
740
        List of ommers mentioned in the current block.
741
    """
742
    ommer_count = U256(len(ommers))
743
    miner_reward = BLOCK_REWARD + (ommer_count * (BLOCK_REWARD // U256(32)))
744
    create_ether(state, coinbase, miner_reward)
745
746
    for ommer in ommers:
747
        # Ommer age with respect to the current block.
748
        ommer_age = U256(block_number - ommer.number)
749
        ommer_miner_reward = ((U256(8) - ommer_age) * BLOCK_REWARD) // U256(8)
750
        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[EthereumException]]:
546
    """
547
    Execute a transaction against the provided environment.
548
549
    This function processes the actions needed to execute a transaction.
550
    It decrements the sender's account after calculating the gas fee and
551
    refunds them the proper amount after execution. Calling contracts,
552
    deploying code, and incrementing nonces are all examples of actions that
553
    happen within this function or from a call made within this function.
554
555
    Accounts that are marked for deletion are processed and destroyed after
556
    execution.
557
558
    Parameters
559
    ----------
560
    env :
561
        Environment for the Ethereum Virtual Machine.
562
    tx :
563
        Transaction to execute.
564
565
    Returns
566
    -------
567
    gas_left : `ethereum.base_types.U256`
568
        Remaining gas after execution.
569
    logs : `Tuple[ethereum.blocks.Log, ...]`
570
        Logs generated during execution.
571
    """
572
    if not validate_transaction(tx):
573
        raise InvalidBlock
574
575
    sender = env.origin
576
    sender_account = get_account(env.state, sender)
577
788
    max_gas_fee: Uint
578
    if isinstance(tx, FeeMarketTransaction):
790
        max_gas_fee = Uint(tx.gas) * Uint(tx.max_fee_per_gas)
579
        max_gas_fee = tx.gas * tx.max_fee_per_gas
580
    else:
792
        max_gas_fee = Uint(tx.gas) * Uint(tx.gas_price)
581
        max_gas_fee = tx.gas * tx.gas_price
582
    if sender_account.nonce != tx.nonce:
583
        raise InvalidBlock
584
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
585
        raise InvalidBlock
586
    if sender_account.code != bytearray():
587
        raise InvalidSenderError("not EOA")
588
589
    effective_gas_fee = tx.gas * env.gas_price
590
591
    gas = tx.gas - calculate_intrinsic_cost(tx)
592
    increment_nonce(env.state, sender)
593
594
    sender_balance_after_gas_fee = (
595
        Uint(sender_account.balance) - effective_gas_fee
596
    )
597
    set_account_balance(env.state, sender, U256(sender_balance_after_gas_fee))
598
599
    preaccessed_addresses = set()
600
    preaccessed_storage_keys = set()
601
    if isinstance(tx, (AccessListTransaction, FeeMarketTransaction)):
602
        for address, keys in tx.access_list:
603
            preaccessed_addresses.add(address)
604
            for key in keys:
605
                preaccessed_storage_keys.add((address, key))
606
607
    message = prepare_message(
608
        sender,
609
        tx.to,
610
        tx.value,
611
        tx.data,
612
        gas,
613
        env,
614
        preaccessed_addresses=frozenset(preaccessed_addresses),
615
        preaccessed_storage_keys=frozenset(preaccessed_storage_keys),
616
    )
617
618
    output = process_message_call(message, env)
619
620
    gas_used = tx.gas - output.gas_left
621
    gas_refund = min(gas_used // Uint(5), Uint(output.refund_counter))
622
    gas_refund_amount = (output.gas_left + gas_refund) * env.gas_price
623
624
    # For non-1559 transactions env.gas_price == tx.gas_price
625
    priority_fee_per_gas = env.gas_price - env.base_fee_per_gas
626
    transaction_fee = (
627
        tx.gas - output.gas_left - gas_refund
628
    ) * priority_fee_per_gas
629
630
    total_gas_used = gas_used - gas_refund
631
632
    # refund gas
633
    sender_balance_after_refund = get_account(
634
        env.state, sender
635
    ).balance + U256(gas_refund_amount)
636
    set_account_balance(env.state, sender, sender_balance_after_refund)
637
638
    # transfer miner fees
639
    coinbase_balance_after_mining_fee = get_account(
640
        env.state, env.coinbase
641
    ).balance + U256(transaction_fee)
642
    if coinbase_balance_after_mining_fee != 0:
643
        set_account_balance(
644
            env.state, env.coinbase, coinbase_balance_after_mining_fee
645
        )
646
    elif account_exists_and_is_empty(env.state, env.coinbase):
647
        destroy_account(env.state, env.coinbase)
648
649
    for address in output.accounts_to_delete:
650
        destroy_account(env.state, address)
651
652
    for address in output.touched_accounts:
653
        if account_exists_and_is_empty(env.state, address):
654
            destroy_account(env.state, address)
655
656
    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:
660
    """
661
    Computes the hash of a block header.
662
663
    The header hash of a block is the canonical hash that is used to refer
664
    to a specific block and completely distinguishes a block from another.
665
666
    ``keccak256`` is a function that produces a 256 bit hash of any input.
667
    It also takes in any number of bytes as an input and produces a single
668
    hash for them. A hash is a completely unique output for a single input.
669
    So an input corresponds to one unique hash that can be used to identify
670
    the input exactly.
671
672
    Prior to using the ``keccak256`` hash function, the header must be
673
    encoded using the Recursive-Length Prefix. See :ref:`rlp`.
674
    RLP encoding the header converts it into a space-efficient format that
675
    allows for easy transfer of data between nodes. The purpose of RLP is to
676
    encode arbitrarily nested arrays of binary data, and RLP is the primary
677
    encoding method used to serialize objects in Ethereum's execution layer.
678
    The only purpose of RLP is to encode structure; encoding specific data
679
    types (e.g. strings, floats) is left up to higher-order protocols.
680
681
    Parameters
682
    ----------
683
    header :
684
        Header of interest.
685
686
    Returns
687
    -------
688
    hash : `ethereum.crypto.hash.Hash32`
689
        Hash of the header.
690
    """
691
    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:
695
    """
696
    Validates the gas limit for a block.
697
698
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
699
    quotient of the parent block's gas limit and the
700
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
701
    passed through as a parameter is greater than or equal to the *sum* of
702
    the parent's gas and the adjustment delta then the limit for gas is too
703
    high and fails this function's check. Similarly, if the limit is less
704
    than or equal to the *difference* of the parent's gas and the adjustment
705
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
706
    check fails because the gas limit doesn't allow for a sufficient or
707
    reasonable amount of gas to be used on a block.
708
709
    Parameters
710
    ----------
711
    gas_limit :
712
        Gas limit to validate.
713
714
    parent_gas_limit :
715
        Gas limit of the parent block.
716
717
    Returns
718
    -------
719
    check : `bool`
720
        True if gas limit constraints are satisfied, False otherwise.
721
    """
722
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
723
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
724
        return False
725
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
726
        return False
727
    if gas_limit < GAS_LIMIT_MINIMUM:
728
        return False
729
730
    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:
951
    """
952
    Computes difficulty of a block using its header and parent header.
953
954
    The difficulty is determined by the time the block was created after its
955
    parent. The ``offset`` is calculated using the parent block's difficulty,
956
    ``parent_difficulty``, and the timestamp between blocks. This offset is
957
    then added to the parent difficulty and is stored as the ``difficulty``
958
    variable. If the time between the block and its parent is too short, the
959
    offset will result in a positive number thus making the sum of
960
    ``parent_difficulty`` and ``offset`` to be a greater value in order to
961
    avoid mass forking. But, if the time is long enough, then the offset
962
    results in a negative value making the block less difficult than
963
    its parent.
964
965
    The base standard for a block's difficulty is the predefined value
966
    set for the genesis block since it has no parent. So, a block
967
    can't be less difficult than the genesis block, therefore each block's
968
    difficulty is set to the maximum value between the calculated
969
    difficulty and the ``GENESIS_DIFFICULTY``.
970
971
    Parameters
972
    ----------
973
    block_number :
974
        Block number of the block.
975
    block_timestamp :
976
        Timestamp of the block.
977
    parent_timestamp :
978
        Timestamp of the parent block.
979
    parent_difficulty :
980
        difficulty of the parent block.
981
    parent_has_ommers:
982
        does the parent have ommers.
983
984
    Returns
985
    -------
986
    difficulty : `ethereum.base_types.Uint`
987
        Computed difficulty for a block.
988
    """
989
    offset = (
990
        int(parent_difficulty)
991
        // 2048
992
        * max(
993
            (2 if parent_has_ommers else 1)
994
            - int(block_timestamp - parent_timestamp) // 9,
995
            -99,
996
        )
997
    )
998
    difficulty = int(parent_difficulty) + offset
999
    # Historical Note: The difficulty bomb was not present in Ethereum at the
1000
    # start of Frontier, but was added shortly after launch. However since the
1001
    # bomb has no effect prior to block 200000 we pretend it existed from
1002
    # genesis.
1003
    # See https://github.com/ethereum/go-ethereum/pull/1588
1004
    num_bomb_periods = ((int(block_number) - BOMB_DELAY_BLOCKS) // 100000) - 2
1005
    if num_bomb_periods >= 0:
1006
        difficulty += 2**num_bomb_periods
1007
1008
    # Some clients raise the difficulty to `MINIMUM_DIFFICULTY` prior to adding
1009
    # the bomb. This bug does not matter because the difficulty is always much
1010
    # greater than `MINIMUM_DIFFICULTY` on Mainnet.
1011
    return Uint(max(difficulty, int(MINIMUM_DIFFICULTY)))