Skip to content

Commit 77e91da

Browse files
committed
fixup
1 parent 77e4efd commit 77e91da

File tree

4 files changed

+100
-14
lines changed

4 files changed

+100
-14
lines changed

src/ethereum/prague/blocks.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,16 @@
99
chain.
1010
"""
1111
from dataclasses import dataclass
12-
from typing import Tuple, Union
12+
from typing import Annotated, Optional, Tuple, Union
1313

1414
from ethereum_rlp import rlp
1515
from ethereum_types.bytes import Bytes, Bytes8, Bytes32
1616
from ethereum_types.frozen import slotted_freezable
1717
from ethereum_types.numeric import U64, U256, Uint
18+
from typing_extensions import TypeAlias
19+
20+
from ethereum.cancun import blocks as previous_blocks
21+
from ethereum.exceptions import InvalidBlock
1822

1923
from ..crypto.hash import Hash32
2024
from .fork_types import Address, Bloom, Root
@@ -71,6 +75,49 @@ class Header:
7175
requests_hash: Hash32
7276

7377

78+
AnyHeader: TypeAlias = Union[previous_blocks.AnyHeader, Header]
79+
"""
80+
Represents all headers that may have appeared in the blockchain before or in
81+
the current fork.
82+
"""
83+
84+
85+
def decode_header(raw_header: rlp.Simple) -> AnyHeader:
86+
"""
87+
Convert `raw_header` from raw sequences and bytes to a structured block
88+
header.
89+
90+
Checks `raw_header` against this fork's `FORK_CRITERIA`, and if it belongs
91+
to this fork, decodes it accordingly. If not, this function forwards to the
92+
preceding fork where the process is repeated.
93+
"""
94+
from . import FORK_CRITERIA
95+
96+
# First, ensure that `raw_header` is not `bytes` (and is therefore a
97+
# sequence.)
98+
if isinstance(raw_header, bytes):
99+
raise InvalidBlock("header is bytes, expected sequence")
100+
101+
# Next, extract the block number and timestamp (which are always at index 8
102+
# and 11 respectively.)
103+
raw_number = raw_header[8]
104+
if not isinstance(raw_number, bytes):
105+
raise InvalidBlock("header number is sequence, expected bytes")
106+
number = Uint.from_be_bytes(raw_number)
107+
108+
raw_timestamp = raw_header[11]
109+
if not isinstance(raw_timestamp, bytes):
110+
raise InvalidBlock("header timestamp is sequence, expected bytes")
111+
timestamp = U256.from_be_bytes(raw_timestamp)
112+
113+
# Finally, check if this header belongs to this fork.
114+
if FORK_CRITERIA.check(number, timestamp):
115+
return rlp.deserialize_to(Header, raw_header)
116+
117+
# If it doesn't, forward to the preceding fork.
118+
return previous_blocks.decode_header(raw_header)
119+
120+
74121
@slotted_freezable
75122
@dataclass
76123
class Block:
@@ -80,10 +127,17 @@ class Block:
80127

81128
header: Header
82129
transactions: Tuple[Union[Bytes, LegacyTransaction], ...]
83-
ommers: Tuple[Header, ...]
130+
ommers: Tuple[Annotated[AnyHeader, rlp.With(decode_header)], ...]
84131
withdrawals: Tuple[Withdrawal, ...]
85132

86133

134+
AnyBlock: TypeAlias = Union[previous_blocks.AnyBlock, Block]
135+
"""
136+
Represents all blocks that may have appeared in the blockchain before or in the
137+
current fork.
138+
"""
139+
140+
87141
@slotted_freezable
88142
@dataclass
89143
class Log:
@@ -134,3 +188,13 @@ def decode_receipt(receipt: Union[Bytes, Receipt]) -> Receipt:
134188
return rlp.decode_to(Receipt, receipt[1:])
135189
else:
136190
return receipt
191+
192+
193+
def header_base_fee_per_gas(header: AnyHeader) -> Optional[Uint]:
194+
"""
195+
Returns the `base_fee_per_gas` of the given header, or `None` for headers
196+
without that field.
197+
"""
198+
if isinstance(header, Header):
199+
return header.base_fee_per_gas
200+
return previous_blocks.header_base_fee_per_gas(header)

src/ethereum/prague/fork.py

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
from ethereum_types.bytes import Bytes
2020
from ethereum_types.numeric import U64, U256, Uint
2121

22+
from ethereum.cancun import fork as previous_fork
2223
from ethereum.crypto.hash import Hash32, keccak256
2324
from ethereum.exceptions import (
2425
EthereumException,
@@ -27,7 +28,17 @@
2728
)
2829

2930
from . import vm
30-
from .blocks import Block, Header, Log, Receipt, Withdrawal, encode_receipt
31+
from .blocks import (
32+
AnyBlock,
33+
AnyHeader,
34+
Block,
35+
Header,
36+
Log,
37+
Receipt,
38+
Withdrawal,
39+
encode_receipt,
40+
header_base_fee_per_gas,
41+
)
3142
from .bloom import logs_bloom
3243
from .fork_types import Account, Address, Authorization, VersionedHash
3344
from .requests import (
@@ -79,6 +90,7 @@
7990
ELASTICITY_MULTIPLIER = Uint(2)
8091
GAS_LIMIT_ADJUSTMENT_FACTOR = Uint(1024)
8192
GAS_LIMIT_MINIMUM = Uint(5000)
93+
INITIAL_BASE_FEE = Uint(1000000000)
8294
EMPTY_OMMER_HASH = keccak256(rlp.encode([]))
8395
SYSTEM_ADDRESS = hex_to_address("0xfffffffffffffffffffffffffffffffffffffffe")
8496
BEACON_ROOTS_ADDRESS = hex_to_address(
@@ -106,7 +118,7 @@ class BlockChain:
106118
History and current state of the block chain.
107119
"""
108120

109-
blocks: List[Block]
121+
blocks: List[AnyBlock]
110122
state: State
111123
chain_id: U64
112124

@@ -313,7 +325,7 @@ def calculate_base_fee_per_gas(
313325
return Uint(expected_base_fee_per_gas)
314326

315327

316-
def validate_header(chain: BlockChain, header: Header) -> None:
328+
def validate_header(chain: BlockChain, header: AnyHeader) -> None:
317329
"""
318330
Verifies a block header.
319331
@@ -351,15 +363,25 @@ def validate_header(chain: BlockChain, header: Header) -> None:
351363
if header.excess_blob_gas != excess_blob_gas:
352364
raise InvalidBlock
353365

366+
if not isinstance(header, Header):
367+
assert not isinstance(parent_header, Header)
368+
return previous_fork.validate_header(chain, header)
369+
354370
if header.gas_used > header.gas_limit:
355371
raise InvalidBlock
356372

357-
expected_base_fee_per_gas = calculate_base_fee_per_gas(
358-
header.gas_limit,
359-
parent_header.gas_limit,
360-
parent_header.gas_used,
361-
parent_header.base_fee_per_gas,
362-
)
373+
expected_base_fee_per_gas = INITIAL_BASE_FEE
374+
parent_base_fee_per_gas = header_base_fee_per_gas(parent_header)
375+
if parent_base_fee_per_gas is not None:
376+
# For every block except the first, calculate the base fee per gas
377+
# based on the parent block.
378+
expected_base_fee_per_gas = calculate_base_fee_per_gas(
379+
header.gas_limit,
380+
parent_header.gas_limit,
381+
parent_header.gas_used,
382+
parent_base_fee_per_gas,
383+
)
384+
363385
if expected_base_fee_per_gas != header.base_fee_per_gas:
364386
raise InvalidBlock
365387
if header.timestamp <= parent_header.timestamp:

src/ethereum/prague/vm/gas.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
from ethereum.trace import GasAndRefund, evm_trace
2020
from ethereum.utils.numeric import ceil32, taylor_exponential
2121

22-
from ..blocks import Header
22+
from ..blocks import AnyHeader, Header
2323
from ..transactions import BlobTransaction, Transaction
2424
from . import Evm
2525
from .exceptions import OutOfGasError
@@ -276,7 +276,7 @@ def init_code_cost(init_code_length: Uint) -> Uint:
276276
return GAS_INIT_CODE_WORD_COST * ceil32(init_code_length) // Uint(32)
277277

278278

279-
def calculate_excess_blob_gas(parent_header: Header) -> U64:
279+
def calculate_excess_blob_gas(parent_header: AnyHeader) -> U64:
280280
"""
281281
Calculated the excess blob gas for the current block based
282282
on the gas used in the parent block.

tests/prague/test_rlp.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
receipt_root=hash5,
9595
bloom=bloom,
9696
difficulty=Uint(1),
97-
number=Uint(2),
97+
number=Uint(19426587),
9898
gas_limit=Uint(3),
9999
gas_used=Uint(4),
100100
timestamp=U256(5),

0 commit comments

Comments
 (0)