ethereum.arrow_glacier.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

56
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

57
ELASTICITY_MULTIPLIER = Uint(2)

GAS_LIMIT_ADJUSTMENT_FACTOR

58
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)

GAS_LIMIT_MINIMUM

59
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 = 10700000

EMPTY_OMMER_HASH

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

BlockChain

History and current state of the block chain.

66
@dataclass
class BlockChain:

blocks

72
    blocks: List[Block]

state

73
    state: State

chain_id

74
    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:
78
    """
79
    Transforms the state from the previous hard fork (`old`) into the block
80
    chain object for this hard fork and returns it.
81
82
    When forks need to implement an irregular state transition, this function
83
    is used to handle the irregularity. See the :ref:`DAO Fork <dao-fork>` for
84
    an example.
85
86
    Parameters
87
    ----------
88
    old :
89
        Previous block chain object.
90
91
    Returns
92
    -------
93
    new : `BlockChain`
94
        Upgraded block chain object for this hard fork.
95
    """
96
    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]:
100
    """
101
    Obtain the list of hashes of the previous 256 blocks in order of
102
    increasing block number.
103
104
    This function will return less hashes for the first 256 blocks.
105
106
    The ``BLOCKHASH`` opcode needs to access the latest hashes on the chain,
107
    therefore this function retrieves them.
108
109
    Parameters
110
    ----------
111
    chain :
112
        History and current state.
113
114
    Returns
115
    -------
116
    recent_block_hashes : `List[Hash32]`
117
        Hashes of the recent 256 blocks in order of increasing block number.
118
    """
119
    recent_blocks = chain.blocks[-255:]
120
    # TODO: This function has not been tested rigorously
121
    if len(recent_blocks) == 0:
122
        return []
123
124
    recent_block_hashes = []
125
126
    for block in recent_blocks:
127
        prev_block_hash = block.header.parent_hash
128
        recent_block_hashes.append(prev_block_hash)
129
130
    # We are computing the hash only for the most recent block and not for
131
    # the rest of the blocks as they have successors which have the hash of
132
    # the current block as parent hash.
133
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
134
    recent_block_hashes.append(most_recent_block_hash)
135
136
    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:
140
    """
141
    Attempts to apply a block to an existing block chain.
142
143
    All parts of the block's contents need to be verified before being added
144
    to the chain. Blocks are verified by ensuring that the contents of the
145
    block make logical sense with the contents of the parent block. The
146
    information in the block's header must also match the corresponding
147
    information in the block.
148
149
    To implement Ethereum, in theory clients are only required to store the
150
    most recent 255 blocks of the chain since as far as execution is
151
    concerned, only those blocks are accessed. Practically, however, clients
152
    should store more blocks to handle reorgs.
153
154
    Parameters
155
    ----------
156
    chain :
157
        History and current state.
158
    block :
159
        Block to apply to `chain`.
160
    """
161
    parent_header = chain.blocks[-1].header
162
    validate_header(block.header, parent_header)
163
    validate_ommers(block.ommers, block.header, chain)
164
    apply_body_output = apply_body(
165
        chain.state,
166
        get_last_256_block_hashes(chain),
167
        block.header.coinbase,
168
        block.header.number,
169
        block.header.base_fee_per_gas,
170
        block.header.gas_limit,
171
        block.header.timestamp,
172
        block.header.difficulty,
173
        block.transactions,
174
        block.ommers,
175
        chain.chain_id,
176
    )
177
    if apply_body_output.block_gas_used != block.header.gas_used:
178
        raise InvalidBlock(
179
            f"{apply_body_output.block_gas_used} != {block.header.gas_used}"
180
        )
181
    if apply_body_output.transactions_root != block.header.transactions_root:
182
        raise InvalidBlock
183
    if apply_body_output.state_root != block.header.state_root:
184
        raise InvalidBlock
185
    if apply_body_output.receipt_root != block.header.receipt_root:
186
        raise InvalidBlock
187
    if apply_body_output.block_logs_bloom != block.header.bloom:
188
        raise InvalidBlock
189
190
    chain.blocks.append(block)
191
    if len(chain.blocks) > 255:
192
        # Real clients have to store more blocks to deal with reorgs, but the
193
        # protocol only requires the last 255
194
        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:
203
    """
204
    Calculates the base fee per gas for the block.
205
206
    Parameters
207
    ----------
208
    block_gas_limit :
209
        Gas limit of the block for which the base fee is being calculated.
210
    parent_gas_limit :
211
        Gas limit of the parent block.
212
    parent_gas_used :
213
        Gas used in the parent block.
214
    parent_base_fee_per_gas :
215
        Base fee per gas of the parent block.
216
217
    Returns
218
    -------
219
    base_fee_per_gas : `Uint`
220
        Base fee per gas for the block.
221
    """
222
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
223
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
224
        raise InvalidBlock
225
226
    if parent_gas_used == parent_gas_target:
227
        expected_base_fee_per_gas = parent_base_fee_per_gas
228
    elif parent_gas_used > parent_gas_target:
229
        gas_used_delta = parent_gas_used - parent_gas_target
230
231
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
232
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
233
234
        base_fee_per_gas_delta = max(
235
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
236
            Uint(1),
237
        )
238
239
        expected_base_fee_per_gas = (
240
            parent_base_fee_per_gas + base_fee_per_gas_delta
241
        )
242
    else:
243
        gas_used_delta = parent_gas_target - parent_gas_used
244
245
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
246
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
247
248
        base_fee_per_gas_delta = (
249
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
250
        )
251
252
        expected_base_fee_per_gas = (
253
            parent_base_fee_per_gas - base_fee_per_gas_delta
254
        )
255
256
    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:
260
    """
261
    Verifies a block header.
262
263
    In order to consider a block's header valid, the logic for the
264
    quantities in the header should match the logic for the block itself.
265
    For example the header timestamp should be greater than the block's parent
266
    timestamp because the block was created *after* the parent block.
267
    Additionally, the block's number should be directly following the parent
268
    block's number since it is the next block in the sequence.
269
270
    Parameters
271
    ----------
272
    header :
273
        Header to check for correctness.
274
    parent_header :
275
        Parent Header of the header to check for correctness
276
    """
277
    if header.gas_used > header.gas_limit:
278
        raise InvalidBlock
279
280
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
281
        header.gas_limit,
282
        parent_header.gas_limit,
283
        parent_header.gas_used,
284
        parent_header.base_fee_per_gas,
285
    )
286
    if expected_base_fee_per_gas != header.base_fee_per_gas:
287
        raise InvalidBlock
288
289
    parent_has_ommers = parent_header.ommers_hash != EMPTY_OMMER_HASH
290
    if header.timestamp <= parent_header.timestamp:
291
        raise InvalidBlock
292
    if header.number != parent_header.number + Uint(1):
293
        raise InvalidBlock
294
    if len(header.extra_data) > 32:
295
        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:
305
        raise InvalidBlock
306
307
    block_parent_hash = keccak256(rlp.encode(parent_header))
308
    if header.parent_hash != block_parent_hash:
309
        raise InvalidBlock
310
311
    validate_proof_of_work(header)

generate_header_hash_for_pow

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

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

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

Parameters

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

Returns

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

def generate_header_hash_for_pow(header: Header) -> Hash32:
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]:
393
    """
394
    Check if the transaction is includable in the block.
395
396
    Parameters
397
    ----------
398
    tx :
399
        The transaction.
400
    base_fee_per_gas :
401
        The block base fee.
402
    gas_available :
403
        The gas remaining in the block.
404
    chain_id :
405
        The ID of the current chain.
406
407
    Returns
408
    -------
409
    sender_address :
410
        The sender of the transaction.
411
    effective_gas_price :
412
        The price to charge for gas when the transaction is executed.
413
414
    Raises
415
    ------
416
    InvalidBlock :
417
        If the transaction is not includable.
418
    """
419
    if tx.gas > gas_available:
420
        raise InvalidBlock
421
    sender_address = recover_sender(chain_id, tx)
422
423
    if isinstance(tx, FeeMarketTransaction):
424
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
425
            raise InvalidBlock
426
        if tx.max_fee_per_gas < base_fee_per_gas:
427
            raise InvalidBlock
428
429
        priority_fee_per_gas = min(
430
            tx.max_priority_fee_per_gas,
431
            tx.max_fee_per_gas - base_fee_per_gas,
432
        )
433
        effective_gas_price = priority_fee_per_gas + base_fee_per_gas
434
    else:
435
        if tx.gas_price < base_fee_per_gas:
436
            raise InvalidBlock
437
        effective_gas_price = tx.gas_price
438
439
    return sender_address, effective_gas_price

make_receipt

Make the receipt for a transaction that was executed.

Parameters

tx : The executed transaction. error : Error in the top level frame of the transaction, if any. cumulative_gas_used : The total gas used so far in the block after the transaction was executed. logs : The logs produced by the transaction.

Returns

receipt : The receipt for the transaction.

def make_receipt(tx: Transaction, ​​error: Optional[Exception], ​​cumulative_gas_used: Uint, ​​logs: Tuple[Log, ...]) -> Union[Bytes, Receipt]:
448
    """
449
    Make the receipt for a transaction that was executed.
450
451
    Parameters
452
    ----------
453
    tx :
454
        The executed transaction.
455
    error :
456
        Error in the top level frame of the transaction, if any.
457
    cumulative_gas_used :
458
        The total gas used so far in the block after the transaction was
459
        executed.
460
    logs :
461
        The logs produced by the transaction.
462
463
    Returns
464
    -------
465
    receipt :
466
        The receipt for the transaction.
467
    """
468
    receipt = Receipt(
469
        succeeded=error is None,
470
        cumulative_gas_used=cumulative_gas_used,
471
        bloom=logs_bloom(logs),
472
        logs=logs,
473
    )
474
475
    if isinstance(tx, AccessListTransaction):
476
        return b"\x01" + rlp.encode(receipt)
477
    elif isinstance(tx, FeeMarketTransaction):
478
        return b"\x02" + rlp.encode(receipt)
479
    else:
480
        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.

483
@dataclass
class ApplyBodyOutput:

block_gas_used

503
    block_gas_used: Uint

transactions_root

504
    transactions_root: Root

receipt_root

505
    receipt_root: Root

block_logs_bloom

506
    block_logs_bloom: Bloom

state_root

507
    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 : Difficulty of the block. 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_difficulty: Uint, ​​transactions: Tuple[Union[LegacyTransaction, Bytes], ...], ​​ommers: Tuple[Header, ...], ​​chain_id: U64) -> ApplyBodyOutput:
523
    """
524
    Executes a block.
525
526
    Many of the contents of a block are stored in data structures called
527
    tries. There is a transactions trie which is similar to a ledger of the
528
    transactions stored in the current block. There is also a receipts trie
529
    which stores the results of executing a transaction, like the post state
530
    and gas used. This function creates and executes the block that is to be
531
    added to the chain.
532
533
    Parameters
534
    ----------
535
    state :
536
        Current account state.
537
    block_hashes :
538
        List of hashes of the previous 256 blocks in the order of
539
        increasing block number.
540
    coinbase :
541
        Address of account which receives block reward and transaction fees.
542
    block_number :
543
        Position of the block within the chain.
544
    base_fee_per_gas :
545
        Base fee per gas of within the block.
546
    block_gas_limit :
547
        Initial amount of gas available for execution in this block.
548
    block_time :
549
        Time the block was produced, measured in seconds since the epoch.
550
    block_difficulty :
551
        Difficulty of the block.
552
    transactions :
553
        Transactions included in the block.
554
    ommers :
555
        Headers of ancestor blocks which are not direct parents (formerly
556
        uncles.)
557
    chain_id :
558
        ID of the executing chain.
559
560
    Returns
561
    -------
562
    apply_body_output : `ApplyBodyOutput`
563
        Output of applying the block body to the state.
564
    """
565
    gas_available = block_gas_limit
566
    transactions_trie: Trie[
567
        Bytes, Optional[Union[Bytes, LegacyTransaction]]
568
    ] = Trie(secured=False, default=None)
569
    receipts_trie: Trie[Bytes, Optional[Union[Bytes, Receipt]]] = Trie(
570
        secured=False, default=None
571
    )
572
    block_logs: Tuple[Log, ...] = ()
573
574
    for i, tx in enumerate(map(decode_transaction, transactions)):
575
        trie_set(
576
            transactions_trie, rlp.encode(Uint(i)), encode_transaction(tx)
577
        )
578
579
        sender_address, effective_gas_price = check_transaction(
580
            tx, base_fee_per_gas, gas_available, chain_id
581
        )
582
583
        env = vm.Environment(
584
            caller=sender_address,
585
            origin=sender_address,
586
            block_hashes=block_hashes,
587
            coinbase=coinbase,
588
            number=block_number,
589
            gas_limit=block_gas_limit,
590
            base_fee_per_gas=base_fee_per_gas,
591
            gas_price=effective_gas_price,
592
            time=block_time,
593
            difficulty=block_difficulty,
594
            state=state,
595
            chain_id=chain_id,
596
            traces=[],
597
        )
598
599
        gas_used, logs, error = process_transaction(env, tx)
600
        gas_available -= gas_used
601
602
        receipt = make_receipt(
603
            tx, error, (block_gas_limit - gas_available), logs
604
        )
605
606
        trie_set(
607
            receipts_trie,
608
            rlp.encode(Uint(i)),
609
            receipt,
610
        )
611
612
        block_logs += logs
613
614
    pay_rewards(state, block_number, coinbase, ommers)
615
616
    block_gas_used = block_gas_limit - gas_available
617
618
    block_logs_bloom = logs_bloom(block_logs)
619
620
    return ApplyBodyOutput(
621
        block_gas_used,
622
        root(transactions_trie),
623
        root(receipts_trie),
624
        block_logs_bloom,
625
        state_root(state),
626
    )

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.arrow_glacier.vm.Environment, ​​tx: Transaction) -> Tuple[Uint, Tuple[Log, ...], Optional[Exception]]:
751
    """
752
    Execute a transaction against the provided environment.
753
754
    This function processes the actions needed to execute a transaction.
755
    It decrements the sender's account after calculating the gas fee and
756
    refunds them the proper amount after execution. Calling contracts,
757
    deploying code, and incrementing nonces are all examples of actions that
758
    happen within this function or from a call made within this function.
759
760
    Accounts that are marked for deletion are processed and destroyed after
761
    execution.
762
763
    Parameters
764
    ----------
765
    env :
766
        Environment for the Ethereum Virtual Machine.
767
    tx :
768
        Transaction to execute.
769
770
    Returns
771
    -------
772
    gas_left : `ethereum.base_types.U256`
773
        Remaining gas after execution.
774
    logs : `Tuple[ethereum.blocks.Log, ...]`
775
        Logs generated during execution.
776
    """
777
    if not validate_transaction(tx):
778
        raise InvalidBlock
779
780
    sender = env.origin
781
    sender_account = get_account(env.state, sender)
782
783
    max_gas_fee: Uint
784
    if isinstance(tx, FeeMarketTransaction):
785
        max_gas_fee = Uint(tx.gas) * Uint(tx.max_fee_per_gas)
786
    else:
787
        max_gas_fee = Uint(tx.gas) * Uint(tx.gas_price)
788
    if sender_account.nonce != tx.nonce:
789
        raise InvalidBlock
790
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
791
        raise InvalidBlock
792
    if sender_account.code != bytearray():
793
        raise InvalidSenderError("not EOA")
794
795
    effective_gas_fee = tx.gas * env.gas_price
796
797
    gas = tx.gas - calculate_intrinsic_cost(tx)
798
    increment_nonce(env.state, sender)
799
800
    sender_balance_after_gas_fee = (
801
        Uint(sender_account.balance) - effective_gas_fee
802
    )
803
    set_account_balance(env.state, sender, U256(sender_balance_after_gas_fee))
804
805
    preaccessed_addresses = set()
806
    preaccessed_storage_keys = set()
807
    if isinstance(tx, (AccessListTransaction, FeeMarketTransaction)):
808
        for address, keys in tx.access_list:
809
            preaccessed_addresses.add(address)
810
            for key in keys:
811
                preaccessed_storage_keys.add((address, key))
812
813
    message = prepare_message(
814
        sender,
815
        tx.to,
816
        tx.value,
817
        tx.data,
818
        gas,
819
        env,
820
        preaccessed_addresses=frozenset(preaccessed_addresses),
821
        preaccessed_storage_keys=frozenset(preaccessed_storage_keys),
822
    )
823
824
    output = process_message_call(message, env)
825
826
    gas_used = tx.gas - output.gas_left
827
    gas_refund = min(gas_used // Uint(5), Uint(output.refund_counter))
828
    gas_refund_amount = (output.gas_left + gas_refund) * env.gas_price
829
830
    # For non-1559 transactions env.gas_price == tx.gas_price
831
    priority_fee_per_gas = env.gas_price - env.base_fee_per_gas
832
    transaction_fee = (
833
        tx.gas - output.gas_left - gas_refund
834
    ) * priority_fee_per_gas
835
836
    total_gas_used = gas_used - gas_refund
837
838
    # refund gas
839
    sender_balance_after_refund = get_account(
840
        env.state, sender
841
    ).balance + U256(gas_refund_amount)
842
    set_account_balance(env.state, sender, sender_balance_after_refund)
843
844
    # transfer miner fees
845
    coinbase_balance_after_mining_fee = get_account(
846
        env.state, env.coinbase
847
    ).balance + U256(transaction_fee)
848
    if coinbase_balance_after_mining_fee != 0:
849
        set_account_balance(
850
            env.state, env.coinbase, coinbase_balance_after_mining_fee
851
        )
852
    elif account_exists_and_is_empty(env.state, env.coinbase):
853
        destroy_account(env.state, env.coinbase)
854
855
    for address in output.accounts_to_delete:
856
        destroy_account(env.state, address)
857
858
    for address in output.touched_accounts:
859
        if account_exists_and_is_empty(env.state, address):
860
            destroy_account(env.state, address)
861
862
    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:
866
    """
867
    Computes the hash of a block header.
868
869
    The header hash of a block is the canonical hash that is used to refer
870
    to a specific block and completely distinguishes a block from another.
871
872
    ``keccak256`` is a function that produces a 256 bit hash of any input.
873
    It also takes in any number of bytes as an input and produces a single
874
    hash for them. A hash is a completely unique output for a single input.
875
    So an input corresponds to one unique hash that can be used to identify
876
    the input exactly.
877
878
    Prior to using the ``keccak256`` hash function, the header must be
879
    encoded using the Recursive-Length Prefix. See :ref:`rlp`.
880
    RLP encoding the header converts it into a space-efficient format that
881
    allows for easy transfer of data between nodes. The purpose of RLP is to
882
    encode arbitrarily nested arrays of binary data, and RLP is the primary
883
    encoding method used to serialize objects in Ethereum's execution layer.
884
    The only purpose of RLP is to encode structure; encoding specific data
885
    types (e.g. strings, floats) is left up to higher-order protocols.
886
887
    Parameters
888
    ----------
889
    header :
890
        Header of interest.
891
892
    Returns
893
    -------
894
    hash : `ethereum.crypto.hash.Hash32`
895
        Hash of the header.
896
    """
897
    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:
901
    """
902
    Validates the gas limit for a block.
903
904
    The bounds of the gas limit, ``max_adjustment_delta``, is set as the
905
    quotient of the parent block's gas limit and the
906
    ``GAS_LIMIT_ADJUSTMENT_FACTOR``. Therefore, if the gas limit that is
907
    passed through as a parameter is greater than or equal to the *sum* of
908
    the parent's gas and the adjustment delta then the limit for gas is too
909
    high and fails this function's check. Similarly, if the limit is less
910
    than or equal to the *difference* of the parent's gas and the adjustment
911
    delta *or* the predefined ``GAS_LIMIT_MINIMUM`` then this function's
912
    check fails because the gas limit doesn't allow for a sufficient or
913
    reasonable amount of gas to be used on a block.
914
915
    Parameters
916
    ----------
917
    gas_limit :
918
        Gas limit to validate.
919
920
    parent_gas_limit :
921
        Gas limit of the parent block.
922
923
    Returns
924
    -------
925
    check : `bool`
926
        True if gas limit constraints are satisfied, False otherwise.
927
    """
928
    max_adjustment_delta = parent_gas_limit // GAS_LIMIT_ADJUSTMENT_FACTOR
929
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
930
        return False
931
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
932
        return False
933
    if gas_limit < GAS_LIMIT_MINIMUM:
934
        return False
935
936
    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)))