Skip to content

Commit aae2d9a

Browse files
authored
feat: make SSZVector of Fp serializable (#223)
* feat: make Fp and its vector json serializable * fix: remove unneeded Fp.__get_pydantic_core_schema() * fix: remove no longer needed imports * fix: align naming * test: Fp list and vector * fix: linting
1 parent efbe44a commit aae2d9a

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

src/lean_spec/types/collections.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,25 @@ class Uint16Vector2(SSZVector):
4545
data: Tuple[SSZType, ...] = Field(default_factory=tuple)
4646
"""The immutable data stored in the vector."""
4747

48+
@field_serializer("data", when_used="json")
49+
def _serialize_data(self, value: Tuple[SSZType, ...]) -> list[Any]:
50+
"""Serialize vector elements to JSON, preserving custom type serialization."""
51+
from lean_spec.subspecs.koalabear import Fp
52+
53+
result: list[Any] = []
54+
for item in value:
55+
# For BaseBytes subclasses, manually add 0x prefix
56+
if isinstance(item, BaseBytes):
57+
result.append("0x" + item.hex())
58+
# For Fp field elements, extract the value attribute
59+
elif isinstance(item, Fp):
60+
result.append(item.value)
61+
else:
62+
# For other types (Uint, etc.), convert to int
63+
# BaseUint inherits from int, so this cast is safe
64+
result.append(item)
65+
return result
66+
4867
@field_validator("data", mode="before")
4968
@classmethod
5069
def _validate_vector_data(cls, v: Any) -> Tuple[SSZType, ...]:
@@ -188,11 +207,16 @@ class Uint64List32(SSZList):
188207
@field_serializer("data", when_used="json")
189208
def _serialize_data(self, value: Tuple[SSZType, ...]) -> list[Any]:
190209
"""Serialize list elements to JSON, preserving custom type serialization."""
210+
from lean_spec.subspecs.koalabear import Fp
211+
191212
result: list[Any] = []
192213
for item in value:
193214
# For BaseBytes subclasses, manually add 0x prefix
194215
if isinstance(item, BaseBytes):
195216
result.append("0x" + item.hex())
217+
# For Fp field elements, extract the value attribute
218+
elif isinstance(item, Fp):
219+
result.append(item.value)
196220
else:
197221
# For other types (Uint, etc.), convert to int
198222
# BaseUint inherits from int, so this cast is safe

tests/lean_spec/types/test_collections.py

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from pydantic import ValidationError, create_model
77
from typing_extensions import Type
88

9+
from lean_spec.subspecs.koalabear import Fp
910
from lean_spec.types.boolean import Boolean
1011
from lean_spec.types.collections import SSZList, SSZVector
1112
from lean_spec.types.container import Container
@@ -149,6 +150,13 @@ class Uint8Vector2(SSZVector):
149150
LENGTH = 2
150151

151152

153+
class FpVector8(SSZVector):
154+
"""A vector of exactly 8 Fp values."""
155+
156+
ELEMENT_TYPE = Fp
157+
LENGTH = 8
158+
159+
152160
# Additional List classes for tests
153161
class Uint8List32(SSZList):
154162
"""A list with up to 32 Uint8 values."""
@@ -178,6 +186,13 @@ class BooleanList4(SSZList):
178186
LIMIT = 4
179187

180188

189+
class FpList8(SSZList):
190+
"""A list with up to 8 Fp values."""
191+
192+
ELEMENT_TYPE = Fp
193+
LIMIT = 8
194+
195+
181196
# Test data for the 'sig' vector test case
182197
sig_test_data_list = [0] * 96
183198
for i, v in {0: 1, 32: 2, 64: 3, 95: 0xFF}.items():
@@ -335,6 +350,11 @@ class TestSSZVectorSerialization:
335350
(FixedContainer(a=Uint8(1), b=Uint16(2)), FixedContainer(a=Uint8(3), b=Uint16(4))),
336351
"010200030400", # 010200 for first element, 030400 for second
337352
),
353+
(
354+
FpVector8,
355+
(10, 20, 30, 40, 50, 60, 70, 80),
356+
"0a000000140000001e00000028000000320000003c0000004600000050000000",
357+
),
338358
],
339359
)
340360
def test_fixed_size_element_vector_serialization(
@@ -372,7 +392,7 @@ def test_variable_size_element_vector_serialization(self) -> None:
372392
assert decoded == instance
373393

374394

375-
class TestListSerialization:
395+
class TestSSZListSerialization:
376396
"""Tests SSZ serialization and deserialization for the List type."""
377397

378398
@pytest.mark.parametrize(
@@ -397,6 +417,11 @@ class TestListSerialization:
397417
tuple(range(1, 20)),
398418
"".join(i.to_bytes(32, "little").hex() for i in range(1, 20)),
399419
),
420+
(
421+
FpList8,
422+
(10, 20, 30),
423+
"0a000000140000001e000000",
424+
),
400425
],
401426
)
402427
def test_fixed_size_element_list_serialization(

0 commit comments

Comments
 (0)