Skip to content

Commit 78156d7

Browse files
committed
More strict bounds on ommer types
1 parent 30a309d commit 78156d7

File tree

25 files changed

+601
-40
lines changed

25 files changed

+601
-40
lines changed

src/ethereum/arrow_glacier/blocks.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
chain.
1010
"""
1111
from dataclasses import dataclass
12-
from typing import Optional, 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 U256, Uint
1818
from typing_extensions import TypeAlias
1919

20+
from ethereum.exceptions import InvalidBlock
2021
from ethereum.london import blocks as previous_blocks
2122

2223
from ..crypto.hash import Hash32
@@ -61,6 +62,42 @@ class Header:
6162
"""
6263

6364

65+
def decode_header(raw_header: rlp.Simple) -> AnyHeader:
66+
"""
67+
Convert `raw_header` from raw sequences and bytes to a structured block
68+
header.
69+
70+
Checks `raw_header` against this fork's `FORK_CRITERIA`, and if it belongs
71+
to this fork, decodes it accordingly. If not, this function forwards to the
72+
preceding fork where the process is repeated.
73+
"""
74+
from . import FORK_CRITERIA
75+
76+
# First, ensure that `raw_header` is not `bytes` (and is therefore a
77+
# sequence.)
78+
if isinstance(raw_header, bytes):
79+
raise InvalidBlock("header is bytes, expected sequence")
80+
81+
# Next, extract the block number and timestamp (which are always at index 8
82+
# and 11 respectively.)
83+
raw_number = raw_header[8]
84+
if not isinstance(raw_number, bytes):
85+
raise InvalidBlock("header number is sequence, expected bytes")
86+
number = Uint.from_be_bytes(raw_number)
87+
88+
raw_timestamp = raw_header[11]
89+
if not isinstance(raw_timestamp, bytes):
90+
raise InvalidBlock("header timestamp is sequence, expected bytes")
91+
timestamp = U256.from_be_bytes(raw_timestamp)
92+
93+
# Finally, check if this header belongs to this fork.
94+
if FORK_CRITERIA.check(number, timestamp):
95+
return rlp.deserialize_to(Header, raw_header)
96+
97+
# If it doesn't, forward to the preceding fork.
98+
return previous_blocks.decode_header(raw_header)
99+
100+
64101
@slotted_freezable
65102
@dataclass
66103
class Block:
@@ -70,7 +107,7 @@ class Block:
70107

71108
header: Header
72109
transactions: Tuple[Union[Bytes, LegacyTransaction], ...]
73-
ommers: Tuple[AnyHeader, ...]
110+
ommers: Tuple[Annotated[AnyHeader, rlp.With(decode_header)], ...]
74111

75112

76113
AnyBlock: TypeAlias = Union[previous_blocks.AnyBlock, Block]

src/ethereum/berlin/blocks.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
chain.
1010
"""
1111
from dataclasses import dataclass
12-
from typing import Tuple, Union
12+
from typing import Annotated, 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 U256, Uint
1818
from typing_extensions import TypeAlias
1919

20+
from ethereum.exceptions import InvalidBlock
2021
from ethereum.muir_glacier import blocks as previous_blocks
2122

2223
from ..crypto.hash import Hash32
@@ -55,6 +56,42 @@ class Header:
5556
"""
5657

5758

59+
def decode_header(raw_header: rlp.Simple) -> AnyHeader:
60+
"""
61+
Convert `raw_header` from raw sequences and bytes to a structured block
62+
header.
63+
64+
Checks `raw_header` against this fork's `FORK_CRITERIA`, and if it belongs
65+
to this fork, decodes it accordingly. If not, this function forwards to the
66+
preceding fork where the process is repeated.
67+
"""
68+
from . import FORK_CRITERIA
69+
70+
# First, ensure that `raw_header` is not `bytes` (and is therefore a
71+
# sequence.)
72+
if isinstance(raw_header, bytes):
73+
raise InvalidBlock("header is bytes, expected sequence")
74+
75+
# Next, extract the block number and timestamp (which are always at index 8
76+
# and 11 respectively.)
77+
raw_number = raw_header[8]
78+
if not isinstance(raw_number, bytes):
79+
raise InvalidBlock("header number is sequence, expected bytes")
80+
number = Uint.from_be_bytes(raw_number)
81+
82+
raw_timestamp = raw_header[11]
83+
if not isinstance(raw_timestamp, bytes):
84+
raise InvalidBlock("header timestamp is sequence, expected bytes")
85+
timestamp = U256.from_be_bytes(raw_timestamp)
86+
87+
# Finally, check if this header belongs to this fork.
88+
if FORK_CRITERIA.check(number, timestamp):
89+
return rlp.deserialize_to(Header, raw_header)
90+
91+
# If it doesn't, forward to the preceding fork.
92+
return previous_blocks.decode_header(raw_header)
93+
94+
5895
@slotted_freezable
5996
@dataclass
6097
class Block:
@@ -64,7 +101,7 @@ class Block:
64101

65102
header: Header
66103
transactions: Tuple[Union[Bytes, LegacyTransaction], ...]
67-
ommers: Tuple[AnyHeader, ...]
104+
ommers: Tuple[Annotated[AnyHeader, rlp.With(decode_header)], ...]
68105

69106

70107
AnyBlock: TypeAlias = Union[previous_blocks.AnyBlock, Block]

src/ethereum/byzantium/blocks.py

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

14+
from ethereum_rlp import rlp
1415
from ethereum_types.bytes import Bytes, Bytes8, Bytes32
1516
from ethereum_types.frozen import slotted_freezable
1617
from ethereum_types.numeric import U256, Uint
1718
from typing_extensions import TypeAlias
1819

20+
from ethereum.exceptions import InvalidBlock
1921
from ethereum.spurious_dragon import blocks as previous_blocks
2022

2123
from ..crypto.hash import Hash32
@@ -54,6 +56,42 @@ class Header:
5456
"""
5557

5658

59+
def decode_header(raw_header: rlp.Simple) -> AnyHeader:
60+
"""
61+
Convert `raw_header` from raw sequences and bytes to a structured block
62+
header.
63+
64+
Checks `raw_header` against this fork's `FORK_CRITERIA`, and if it belongs
65+
to this fork, decodes it accordingly. If not, this function forwards to the
66+
preceding fork where the process is repeated.
67+
"""
68+
from . import FORK_CRITERIA
69+
70+
# First, ensure that `raw_header` is not `bytes` (and is therefore a
71+
# sequence.)
72+
if isinstance(raw_header, bytes):
73+
raise InvalidBlock("header is bytes, expected sequence")
74+
75+
# Next, extract the block number and timestamp (which are always at index 8
76+
# and 11 respectively.)
77+
raw_number = raw_header[8]
78+
if not isinstance(raw_number, bytes):
79+
raise InvalidBlock("header number is sequence, expected bytes")
80+
number = Uint.from_be_bytes(raw_number)
81+
82+
raw_timestamp = raw_header[11]
83+
if not isinstance(raw_timestamp, bytes):
84+
raise InvalidBlock("header timestamp is sequence, expected bytes")
85+
timestamp = U256.from_be_bytes(raw_timestamp)
86+
87+
# Finally, check if this header belongs to this fork.
88+
if FORK_CRITERIA.check(number, timestamp):
89+
return rlp.deserialize_to(Header, raw_header)
90+
91+
# If it doesn't, forward to the preceding fork.
92+
return previous_blocks.decode_header(raw_header)
93+
94+
5795
@slotted_freezable
5896
@dataclass
5997
class Block:
@@ -63,7 +101,7 @@ class Block:
63101

64102
header: Header
65103
transactions: Tuple[Transaction, ...]
66-
ommers: Tuple[AnyHeader, ...]
104+
ommers: Tuple[Annotated[AnyHeader, rlp.With(decode_header)], ...]
67105

68106

69107
AnyBlock: TypeAlias = Union[previous_blocks.AnyBlock, Block]

src/ethereum/cancun/blocks.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@
99
chain.
1010
"""
1111
from dataclasses import dataclass
12-
from typing import Optional, 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
1818
from typing_extensions import TypeAlias
1919

20+
from ethereum.exceptions import InvalidBlock
2021
from ethereum.shanghai import blocks as previous_blocks
2122

2223
from ..crypto.hash import Hash32
@@ -79,6 +80,42 @@ class Header:
7980
"""
8081

8182

83+
def decode_header(raw_header: rlp.Simple) -> AnyHeader:
84+
"""
85+
Convert `raw_header` from raw sequences and bytes to a structured block
86+
header.
87+
88+
Checks `raw_header` against this fork's `FORK_CRITERIA`, and if it belongs
89+
to this fork, decodes it accordingly. If not, this function forwards to the
90+
preceding fork where the process is repeated.
91+
"""
92+
from . import FORK_CRITERIA
93+
94+
# First, ensure that `raw_header` is not `bytes` (and is therefore a
95+
# sequence.)
96+
if isinstance(raw_header, bytes):
97+
raise InvalidBlock("header is bytes, expected sequence")
98+
99+
# Next, extract the block number and timestamp (which are always at index 8
100+
# and 11 respectively.)
101+
raw_number = raw_header[8]
102+
if not isinstance(raw_number, bytes):
103+
raise InvalidBlock("header number is sequence, expected bytes")
104+
number = Uint.from_be_bytes(raw_number)
105+
106+
raw_timestamp = raw_header[11]
107+
if not isinstance(raw_timestamp, bytes):
108+
raise InvalidBlock("header timestamp is sequence, expected bytes")
109+
timestamp = U256.from_be_bytes(raw_timestamp)
110+
111+
# Finally, check if this header belongs to this fork.
112+
if FORK_CRITERIA.check(number, timestamp):
113+
return rlp.deserialize_to(Header, raw_header)
114+
115+
# If it doesn't, forward to the preceding fork.
116+
return previous_blocks.decode_header(raw_header)
117+
118+
82119
@slotted_freezable
83120
@dataclass
84121
class Block:
@@ -88,7 +125,7 @@ class Block:
88125

89126
header: Header
90127
transactions: Tuple[Union[Bytes, LegacyTransaction], ...]
91-
ommers: Tuple[AnyHeader, ...]
128+
ommers: Tuple[Annotated[AnyHeader, rlp.With(decode_header)], ...]
92129
withdrawals: Tuple[Withdrawal, ...]
93130

94131

src/ethereum/constantinople/blocks.py

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

14+
from ethereum_rlp import rlp
1415
from ethereum_types.bytes import Bytes, Bytes8, Bytes32
1516
from ethereum_types.frozen import slotted_freezable
1617
from ethereum_types.numeric import U256, Uint
1718
from typing_extensions import TypeAlias
1819

1920
from ethereum.byzantium import blocks as previous_blocks
21+
from ethereum.exceptions import InvalidBlock
2022

2123
from ..crypto.hash import Hash32
2224
from .fork_types import Address, Bloom, Root
@@ -54,6 +56,42 @@ class Header:
5456
"""
5557

5658

59+
def decode_header(raw_header: rlp.Simple) -> AnyHeader:
60+
"""
61+
Convert `raw_header` from raw sequences and bytes to a structured block
62+
header.
63+
64+
Checks `raw_header` against this fork's `FORK_CRITERIA`, and if it belongs
65+
to this fork, decodes it accordingly. If not, this function forwards to the
66+
preceding fork where the process is repeated.
67+
"""
68+
from . import FORK_CRITERIA
69+
70+
# First, ensure that `raw_header` is not `bytes` (and is therefore a
71+
# sequence.)
72+
if isinstance(raw_header, bytes):
73+
raise InvalidBlock("header is bytes, expected sequence")
74+
75+
# Next, extract the block number and timestamp (which are always at index 8
76+
# and 11 respectively.)
77+
raw_number = raw_header[8]
78+
if not isinstance(raw_number, bytes):
79+
raise InvalidBlock("header number is sequence, expected bytes")
80+
number = Uint.from_be_bytes(raw_number)
81+
82+
raw_timestamp = raw_header[11]
83+
if not isinstance(raw_timestamp, bytes):
84+
raise InvalidBlock("header timestamp is sequence, expected bytes")
85+
timestamp = U256.from_be_bytes(raw_timestamp)
86+
87+
# Finally, check if this header belongs to this fork.
88+
if FORK_CRITERIA.check(number, timestamp):
89+
return rlp.deserialize_to(Header, raw_header)
90+
91+
# If it doesn't, forward to the preceding fork.
92+
return previous_blocks.decode_header(raw_header)
93+
94+
5795
@slotted_freezable
5896
@dataclass
5997
class Block:
@@ -63,7 +101,7 @@ class Block:
63101

64102
header: Header
65103
transactions: Tuple[Transaction, ...]
66-
ommers: Tuple[AnyHeader, ...]
104+
ommers: Tuple[Annotated[AnyHeader, rlp.With(decode_header)], ...]
67105

68106

69107
AnyBlock: TypeAlias = Union[previous_blocks.AnyBlock, Block]

0 commit comments

Comments
 (0)