ethereum.forks.cancun.fork

Ethereum Specification.

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

Introduction

Entry point for the Ethereum specification.

BASE_FEE_MAX_CHANGE_DENOMINATOR

86
BASE_FEE_MAX_CHANGE_DENOMINATOR = Uint(8)

ELASTICITY_MULTIPLIER

87
ELASTICITY_MULTIPLIER = Uint(2)

EMPTY_OMMER_HASH

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

SYSTEM_ADDRESS

89
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")

BEACON_ROOTS_ADDRESS

90
BEACON_ROOTS_ADDRESS = hex_to_address(
91
    "0x000F3df6D732807Ef1319fB7B8bB8522d0Beac02"
92
)

SYSTEM_TRANSACTION_GAS

93
SYSTEM_TRANSACTION_GAS = Uint(30000000)

MAX_BLOB_GAS_PER_BLOCK

94
MAX_BLOB_GAS_PER_BLOCK: Final[U64] = U64(786432)

VERSIONED_HASH_VERSION_KZG

95
VERSIONED_HASH_VERSION_KZG = b"\x01"

BlockChain

History and current state of the block chain.

98
@final
99
@dataclass
class BlockChain:

blocks

105
    blocks: List[Block]

state

106
    state: State

chain_id

107
    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:
111
    <snip>
130
    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]:
134
    <snip>
154
    recent_blocks = chain.blocks[-255:]
155
    # TODO: This function has not been tested rigorously
156
    if len(recent_blocks) == 0:
157
        return []
158
159
    recent_block_hashes = []
160
161
    for block in recent_blocks:
162
        prev_block_hash = block.header.parent_hash
163
        recent_block_hashes.append(prev_block_hash)
164
165
    # We are computing the hash only for the most recent block and not for
166
    # the rest of the blocks as they have successors which have the hash of
167
    # the current block as parent hash.
168
    most_recent_block_hash = keccak256(rlp.encode(recent_blocks[-1].header))
169
    recent_block_hashes.append(most_recent_block_hash)
170
171
    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:
175
    <snip>
197
    validate_header(chain, block.header)
198
    if block.ommers != ():
199
        raise InvalidBlock
200
201
    block_state = BlockState(pre_state=chain.state)
202
203
    block_env = vm.BlockEnvironment(
204
        chain_id=chain.chain_id,
205
        state=block_state,
206
        block_gas_limit=block.header.gas_limit,
207
        block_hashes=get_last_256_block_hashes(chain),
208
        coinbase=block.header.coinbase,
209
        number=block.header.number,
210
        base_fee_per_gas=block.header.base_fee_per_gas,
211
        time=block.header.timestamp,
212
        prev_randao=block.header.prev_randao,
213
        excess_blob_gas=block.header.excess_blob_gas,
214
        parent_beacon_block_root=block.header.parent_beacon_block_root,
215
    )
216
217
    block_output = apply_body(
218
        block_env=block_env,
219
        transactions=block.transactions,
220
        withdrawals=block.withdrawals,
221
    )
222
    block_diff = extract_block_diff(block_state)
223
    block_state_root = chain.state.compute_state_root(block_diff)
224
    transactions_root = root(block_output.transactions_trie)
225
    receipt_root = root(block_output.receipts_trie)
226
    block_logs_bloom = logs_bloom(block_output.block_logs)
227
    withdrawals_root = root(block_output.withdrawals_trie)
228
229
    if block_output.block_gas_used != block.header.gas_used:
230
        raise InvalidBlock(
231
            f"{block_output.block_gas_used} != {block.header.gas_used}"
232
        )
233
    if transactions_root != block.header.transactions_root:
234
        raise InvalidBlock
235
    if block_state_root != block.header.state_root:
236
        raise InvalidBlock
237
    if receipt_root != block.header.receipt_root:
238
        raise InvalidBlock
239
    if block_logs_bloom != block.header.bloom:
240
        raise InvalidBlock
241
    if withdrawals_root != block.header.withdrawals_root:
242
        raise InvalidBlock
243
    if block_output.blob_gas_used != block.header.blob_gas_used:
244
        raise InvalidBlock
245
246
    apply_changes_to_state(chain.state, block_diff)
247
    chain.blocks.append(block)
248
    if len(chain.blocks) > 255:
249
        # Real clients have to store more blocks to deal with reorgs, but the
250
        # protocol only requires the last 255
251
        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:
260
    <snip>
280
    parent_gas_target = parent_gas_limit // ELASTICITY_MULTIPLIER
281
    if not check_gas_limit(block_gas_limit, parent_gas_limit):
282
        raise InvalidBlock
283
284
    if parent_gas_used == parent_gas_target:
285
        expected_base_fee_per_gas = parent_base_fee_per_gas
286
    elif parent_gas_used > parent_gas_target:
287
        gas_used_delta = parent_gas_used - parent_gas_target
288
289
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
290
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
291
292
        base_fee_per_gas_delta = max(
293
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR,
294
            Uint(1),
295
        )
296
297
        expected_base_fee_per_gas = (
298
            parent_base_fee_per_gas + base_fee_per_gas_delta
299
        )
300
    else:
301
        gas_used_delta = parent_gas_target - parent_gas_used
302
303
        parent_fee_gas_delta = parent_base_fee_per_gas * gas_used_delta
304
        target_fee_gas_delta = parent_fee_gas_delta // parent_gas_target
305
306
        base_fee_per_gas_delta = (
307
            target_fee_gas_delta // BASE_FEE_MAX_CHANGE_DENOMINATOR
308
        )
309
310
        expected_base_fee_per_gas = (
311
            parent_base_fee_per_gas - base_fee_per_gas_delta
312
        )
313
314
    return Uint(expected_base_fee_per_gas)

validate_header

Verifies a block header.

In order to consider a block's header valid, the logic for the quantities in the header should match the logic for the block itself. For example the header timestamp should be greater than the block's parent timestamp because the block was created after the parent block. Additionally, the block's number should be directly following the parent block's number since it is the next block in the sequence.

Parameters

chain : History and current state. header : Header to check for correctness.

def validate_header(chain: BlockChain, ​​header: Header) -> None:
318
    <snip>
336
    if header.number < Uint(1):
337
        raise InvalidBlock
338
339
    parent_header = chain.blocks[-1].header
340
341
    excess_blob_gas = calculate_excess_blob_gas(parent_header)
342
    if header.excess_blob_gas != excess_blob_gas:
343
        raise InvalidBlock
344
345
    if header.gas_used > header.gas_limit:
346
        raise InvalidBlock
347
348
    expected_base_fee_per_gas = calculate_base_fee_per_gas(
349
        header.gas_limit,
350
        parent_header.gas_limit,
351
        parent_header.gas_used,
352
        parent_header.base_fee_per_gas,
353
    )
354
    if expected_base_fee_per_gas != header.base_fee_per_gas:
355
        raise InvalidBlock
356
    if header.timestamp <= parent_header.timestamp:
357
        raise InvalidBlock
358
    if header.number != parent_header.number + Uint(1):
359
        raise InvalidBlock
360
    if len(header.extra_data) > 32:
361
        raise InvalidBlock
362
    if header.difficulty != 0:
363
        raise InvalidBlock
364
    if header.nonce != b"\x00\x00\x00\x00\x00\x00\x00\x00":
365
        raise InvalidBlock
366
    if header.ommers_hash != EMPTY_OMMER_HASH:
367
        raise InvalidBlock
368
369
    block_parent_hash = keccak256(rlp.encode(parent_header))
370
    if header.parent_hash != block_parent_hash:
371
        raise InvalidBlock

check_transaction

Check if the transaction is includable in the block.

Parameters

block_env : The block scoped environment. block_output : The block output for the current block. tx : The transaction. tx_state : The transaction state tracker.

Returns

sender_address : The sender of the transaction. effective_gas_price : The price to charge for gas when the transaction is executed. blob_versioned_hashes : The blob versioned hashes of the transaction. tx_blob_gas_used: The blob gas used by the transaction.

Raises

InvalidBlock : If the transaction is not includable. GasUsedExceedsLimitError : If the gas used by the transaction exceeds the block's gas limit. NonceMismatchError : If the nonce of the transaction is not equal to the sender's nonce. InsufficientBalanceError : If the sender's balance is not enough to pay for the transaction. InvalidSenderError : If the transaction is from an address that does not exist anymore. PriorityFeeGreaterThanMaxFeeError : If the priority fee is greater than the maximum fee per gas. InsufficientMaxFeePerGasError : If the maximum fee per gas is insufficient for the transaction. InsufficientMaxFeePerBlobGasError : If the maximum fee per blob gas is insufficient for the transaction. BlobGasLimitExceededError : If the blob gas used by the transaction exceeds the block's blob gas limit. InvalidBlobVersionedHashError : If the transaction contains a blob versioned hash with an invalid version. NoBlobDataError : If the transaction is a type 3 but has no blobs. TransactionTypeContractCreationError: If the transaction type is not allowed to create contracts.

def check_transaction(block_env: ethereum.forks.cancun.vm.BlockEnvironment, ​​block_output: ethereum.forks.cancun.vm.BlockOutput, ​​tx: Transaction, ​​tx_state: TransactionState) -> Tuple[Address, Uint, Tuple[VersionedHash, ...], U64]:
380
    <snip>
435
    gas_available = block_env.block_gas_limit - block_output.block_gas_used
436
    blob_gas_available = MAX_BLOB_GAS_PER_BLOCK - block_output.blob_gas_used
437
438
    if tx.gas > gas_available:
439
        raise GasUsedExceedsLimitError("gas used exceeds limit")
440
441
    tx_blob_gas_used = calculate_total_blob_gas(tx)
442
    if tx_blob_gas_used > blob_gas_available:
443
        raise BlobGasLimitExceededError("blob gas limit exceeded")
444
445
    tx_chain_id = chain_id(tx)
446
    if tx_chain_id is not None and tx_chain_id != block_env.chain_id:
447
        raise WrongChainIdError(
448
            expected=block_env.chain_id,
449
            actual=tx_chain_id,
450
        )
451
452
    sender_address = recover_sender(tx)
453
    sender_account = get_account(tx_state, sender_address)
454
455
    if isinstance(tx, FeeMarketCapableTransaction):
456
        if tx.max_fee_per_gas < tx.max_priority_fee_per_gas:
457
            raise PriorityFeeGreaterThanMaxFeeError(
458
                "priority fee greater than max fee"
459
            )
460
        if tx.max_fee_per_gas < block_env.base_fee_per_gas:
461
            raise InsufficientMaxFeePerGasError(
462
                tx.max_fee_per_gas, block_env.base_fee_per_gas
463
            )
464
465
        priority_fee_per_gas = min(
466
            tx.max_priority_fee_per_gas,
467
            tx.max_fee_per_gas - block_env.base_fee_per_gas,
468
        )
469
        effective_gas_price = priority_fee_per_gas + block_env.base_fee_per_gas
470
        max_gas_fee = tx.gas * tx.max_fee_per_gas
471
    else:
472
        if tx.gas_price < block_env.base_fee_per_gas:
473
            raise InvalidBlock
474
        effective_gas_price = tx.gas_price
475
        max_gas_fee = tx.gas * tx.gas_price
476
477
    if isinstance(tx, BlobTransaction):
478
        if not isinstance(tx.to, Address):
479
            raise TransactionTypeContractCreationError(tx)
480
        if len(tx.blob_versioned_hashes) == 0:
481
            raise NoBlobDataError("no blob data in transaction")
482
        for blob_versioned_hash in tx.blob_versioned_hashes:
483
            if blob_versioned_hash[0:1] != VERSIONED_HASH_VERSION_KZG:
484
                raise InvalidBlobVersionedHashError(
485
                    "invalid blob versioned hash"
486
                )
487
488
        blob_gas_price = calculate_blob_gas_price(block_env.excess_blob_gas)
489
        if Uint(tx.max_fee_per_blob_gas) < blob_gas_price:
490
            raise InsufficientMaxFeePerBlobGasError(
491
                "insufficient max fee per blob gas"
492
            )
493
494
        max_gas_fee += Uint(calculate_total_blob_gas(tx)) * Uint(
495
            tx.max_fee_per_blob_gas
496
        )
497
        blob_versioned_hashes = tx.blob_versioned_hashes
498
    else:
499
        blob_versioned_hashes = ()
500
    if sender_account.nonce > Uint(tx.nonce):
501
        raise NonceMismatchError("nonce too low")
502
    elif sender_account.nonce < Uint(tx.nonce):
503
        raise NonceMismatchError("nonce too high")
504
    if Uint(sender_account.balance) < max_gas_fee + Uint(tx.value):
505
        raise InsufficientBalanceError("insufficient sender balance")
506
    if sender_account.code_hash != EMPTY_CODE_HASH:
507
        raise InvalidSenderError("not EOA")
508
509
    return (
510
        sender_address,
511
        effective_gas_price,
512
        blob_versioned_hashes,
513
        tx_blob_gas_used,
514
    )

make_receipt

Make the receipt for a transaction that was executed.

Parameters

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

Returns

receipt : The receipt for the transaction.

def make_receipt(tx: Transaction, ​​error: Optional[EthereumException], ​​cumulative_gas_used: Uint, ​​logs: Tuple[Log, ...]) -> Bytes | Receipt:
523
    <snip>
544
    receipt = Receipt(
545
        succeeded=error is None,
546
        cumulative_gas_used=cumulative_gas_used,
547
        bloom=logs_bloom(logs),
548
        logs=logs,
549
    )
550
551
    return encode_receipt(tx, receipt)

process_unchecked_system_transaction

Process a system transaction without checking if the contract contains code or if the transaction fails.

Parameters

block_env : The block scoped environment. target_address : Address of the contract to call. data : Data to pass to the contract.

Returns

system_tx_output : MessageCallOutput Output of processing the system transaction.

def process_unchecked_system_transaction(block_env: ethereum.forks.cancun.vm.BlockEnvironment, ​​target_address: Address, ​​data: Bytes) -> MessageCallOutput:
559
    <snip>
578
    system_tx_state = TransactionState(parent=block_env.state)
579
    system_contract_code = get_code(
580
        system_tx_state,
581
        get_account(system_tx_state, target_address).code_hash,
582
    )
583
584
    tx_env = vm.TransactionEnvironment(
585
        origin=SYSTEM_ADDRESS,
586
        gas_price=block_env.base_fee_per_gas,
587
        gas=SYSTEM_TRANSACTION_GAS,
588
        access_list_addresses=set(),
589
        access_list_storage_keys=set(),
590
        state=system_tx_state,
591
        blob_versioned_hashes=(),
592
        index_in_block=None,
593
        tx_hash=None,
594
    )
595
596
    system_tx_message = Message(
597
        block_env=block_env,
598
        tx_env=tx_env,
599
        caller=SYSTEM_ADDRESS,
600
        target=target_address,
601
        gas=SYSTEM_TRANSACTION_GAS,
602
        value=U256(0),
603
        data=data,
604
        code=system_contract_code,
605
        depth=Uint(0),
606
        current_target=target_address,
607
        code_address=target_address,
608
        should_transfer_value=False,
609
        is_static=False,
610
        accessed_addresses=set(),
611
        accessed_storage_keys=set(),
612
        parent_evm=None,
613
    )
614
615
    system_tx_output = process_message_call(system_tx_message)
616
617
    incorporate_tx_into_block(system_tx_state)
618
619
    return system_tx_output

apply_body

Executes a block.

Many of the contents of a block are stored in data structures called tries. There is a transactions trie which is similar to a ledger of the transactions stored in the current block. There is also a receipts trie which stores the results of executing a transaction, like the post state and gas used. This function creates and executes the block that is to be added to the chain.

Parameters

block_env : The block scoped environment. transactions : Transactions included in the block. withdrawals : Withdrawals to be processed in the current block.

Returns

block_output : The block output for the current block.

def apply_body(block_env: ethereum.forks.cancun.vm.BlockEnvironment, ​​transactions: Tuple[LegacyTransaction | Bytes, ...], ​​withdrawals: Tuple[Withdrawal, ...]) -> ethereum.forks.cancun.vm.BlockOutput:
627
    <snip>
652
    block_output = vm.BlockOutput()
653
654
    process_unchecked_system_transaction(
655
        block_env=block_env,
656
        target_address=BEACON_ROOTS_ADDRESS,
657
        data=block_env.parent_beacon_block_root,
658
    )
659
660
    for i, tx in enumerate(map(decode_transaction, transactions)):
661
        process_transaction(block_env, block_output, tx, Uint(i))
662
663
    process_withdrawals(block_env, block_output, withdrawals)
664
665
    return block_output

process_transaction

Execute a transaction against the provided environment.

This function processes the actions needed to execute a transaction. It decrements the sender's account balance after calculating the gas fee and refunds them the proper amount after execution. Calling contracts, deploying code, and incrementing nonces are all examples of actions that happen within this function or from a call made within this function.

Accounts that are marked for deletion are processed and destroyed after execution.

Parameters

block_env : Environment for the Ethereum Virtual Machine. block_output : The block output for the current block. tx : Transaction to execute. index: Index of the transaction in the block.

def process_transaction(block_env: ethereum.forks.cancun.vm.BlockEnvironment, ​​block_output: ethereum.forks.cancun.vm.BlockOutput, ​​tx: Transaction, ​​index: Uint) -> None:
674
    <snip>
698
    tx_state = TransactionState(parent=block_env.state)
699
700
    trie_set(
701
        block_output.transactions_trie,
702
        rlp.encode(index),
703
        encode_transaction(tx),
704
    )
705
706
    intrinsic_gas = validate_transaction(tx)
707
708
    (
709
        sender,
710
        effective_gas_price,
711
        blob_versioned_hashes,
712
        tx_blob_gas_used,
713
    ) = check_transaction(
714
        block_env=block_env,
715
        block_output=block_output,
716
        tx=tx,
717
        tx_state=tx_state,
718
    )
719
720
    sender_account = get_account(tx_state, sender)
721
722
    if isinstance(tx, BlobTransaction):
723
        blob_gas_fee = calculate_data_fee(block_env.excess_blob_gas, tx)
724
    else:
725
        blob_gas_fee = Uint(0)
726
727
    effective_gas_fee = tx.gas * effective_gas_price
728
729
    gas = tx.gas - intrinsic_gas
730
    increment_nonce(tx_state, sender)
731
732
    sender_balance_after_gas_fee = (
733
        Uint(sender_account.balance) - effective_gas_fee - blob_gas_fee
734
    )
735
    set_account_balance(tx_state, sender, U256(sender_balance_after_gas_fee))
736
737
    access_list_addresses = set()
738
    access_list_storage_keys = set()
739
    access_list_addresses.add(block_env.coinbase)
740
    if isinstance(
741
        tx, (AccessListTransaction, FeeMarketTransaction, BlobTransaction)
742
    ):
743
        for access in tx.access_list:
744
            access_list_addresses.add(access.account)
745
            for slot in access.slots:
746
                access_list_storage_keys.add((access.account, slot))
747
748
    tx_env = vm.TransactionEnvironment(
749
        origin=sender,
750
        gas_price=effective_gas_price,
751
        gas=gas,
752
        access_list_addresses=access_list_addresses,
753
        access_list_storage_keys=access_list_storage_keys,
754
        state=tx_state,
755
        blob_versioned_hashes=blob_versioned_hashes,
756
        index_in_block=index,
757
        tx_hash=get_transaction_hash(encode_transaction(tx)),
758
    )
759
760
    message = prepare_message(block_env, tx_env, tx)
761
762
    tx_output = process_message_call(message)
763
764
    tx_gas_used_before_refund = tx.gas - tx_output.gas_left
765
    tx_gas_refund = min(
766
        tx_gas_used_before_refund // Uint(5), Uint(tx_output.refund_counter)
767
    )
768
    tx_gas_used_after_refund = tx_gas_used_before_refund - tx_gas_refund
769
    tx_gas_left = tx.gas - tx_gas_used_after_refund
770
    gas_refund_amount = tx_gas_left * effective_gas_price
771
772
    # For non-1559 transactions effective_gas_price == tx.gas_price
773
    priority_fee_per_gas = effective_gas_price - block_env.base_fee_per_gas
774
    transaction_fee = tx_gas_used_after_refund * priority_fee_per_gas
775
776
    # refund gas
777
    create_ether(tx_state, sender, U256(gas_refund_amount))
778
779
    # transfer miner fees
780
    create_ether(tx_state, block_env.coinbase, U256(transaction_fee))
781
782
    for address in tx_output.accounts_to_delete:
783
        destroy_account(tx_state, address)
784
785
    block_output.block_gas_used += tx_gas_used_after_refund
786
    block_output.blob_gas_used += tx_blob_gas_used
787
788
    receipt = make_receipt(
789
        tx, tx_output.error, block_output.block_gas_used, tx_output.logs
790
    )
791
792
    receipt_key = rlp.encode(Uint(index))
793
    block_output.receipt_keys += (receipt_key,)
794
795
    trie_set(
796
        block_output.receipts_trie,
797
        receipt_key,
798
        receipt,
799
    )
800
801
    block_output.block_logs += tx_output.logs
802
803
    incorporate_tx_into_block(tx_state)

process_withdrawals

Increase the balance of the withdrawing account.

def process_withdrawals(block_env: ethereum.forks.cancun.vm.BlockEnvironment, ​​block_output: ethereum.forks.cancun.vm.BlockOutput, ​​withdrawals: Tuple[Withdrawal, ...]) -> None:
811
    <snip>
814
    wd_state = TransactionState(parent=block_env.state)
815
816
    for i, wd in enumerate(withdrawals):
817
        trie_set(
818
            block_output.withdrawals_trie,
819
            rlp.encode(Uint(i)),
820
            rlp.encode(wd),
821
        )
822
823
        create_ether(wd_state, wd.address, wd.amount * U256(10**9))
824
825
    incorporate_tx_into_block(wd_state)

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 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 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:
829
    <snip>
857
    max_adjustment_delta = parent_gas_limit // GasCosts.LIMIT_ADJUSTMENT_FACTOR
858
    if gas_limit >= parent_gas_limit + max_adjustment_delta:
859
        return False
860
    if gas_limit <= parent_gas_limit - max_adjustment_delta:
861
        return False
862
    if gas_limit < GasCosts.LIMIT_MINIMUM:
863
        return False
864
865
    return True