From 34da452a6f5d8cdf75050b6735d863daced596a0 Mon Sep 17 00:00:00 2001 From: Juliya Smith Date: Fri, 15 Dec 2023 16:17:52 -0600 Subject: [PATCH 1/2] fix: rm leading zeroes if needed --- eth_pydantic_types/validators.py | 12 ++++++------ tests/test_hash.py | 17 +++++++++++++++++ tests/test_hex.py | 14 ++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/eth_pydantic_types/validators.py b/eth_pydantic_types/validators.py index a80d607..7d846aa 100644 --- a/eth_pydantic_types/validators.py +++ b/eth_pydantic_types/validators.py @@ -26,7 +26,7 @@ def validate_size(value: __SIZED_T, size: int, coerce: Optional[Callable] = None def validate_bytes_size(value: bytes, size: int) -> bytes: - return validate_size(value, size, coerce=lambda v: _left_pad_bytes(v, size)) + return validate_size(value, size, coerce=lambda v: _coerce_hexbytes_size(v, size)) def validate_address_size(value: str) -> str: @@ -34,12 +34,12 @@ def validate_address_size(value: str) -> str: def validate_str_size(value: str, size: int) -> str: - return validate_size(value, size, coerce=lambda v: _left_pad_str(v, size)) + return validate_size(value, size, coerce=lambda v: _coerce_hexstr_size(v, size)) -def _left_pad_str(val: str, length: int) -> str: - return "0" * (length - len(val)) + val if len(val) < length else val +def _coerce_hexstr_size(val: str, length: int) -> str: + return "0" * (length - len(val)) + val.lstrip("0") if len(val) != length else val -def _left_pad_bytes(val: bytes, num_bytes: int) -> bytes: - return b"\x00" * (num_bytes - len(val)) + val if len(val) < num_bytes else val +def _coerce_hexbytes_size(val: bytes, num_bytes: int) -> bytes: + return b"\x00" * (num_bytes - len(val)) + val.lstrip(b"\x00") if len(val) != num_bytes else val diff --git a/tests/test_hash.py b/tests/test_hash.py index 167fe57..11121e5 100644 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -4,6 +4,7 @@ from eth_pydantic_types.hash import ( HashBytes8, HashBytes16, + HashBytes20, HashBytes32, HashBytes64, HashStr8, @@ -17,6 +18,7 @@ class Model(BaseModel): valuebytes8: HashBytes8 valuebytes16: HashBytes16 + valuebytes20: HashBytes20 valuebytes32: HashBytes32 valuebytes64: HashBytes64 valuestr8: HashStr8 @@ -29,6 +31,7 @@ def from_single(cls, value): return cls( valuebytes8=value, valuebytes16=value, + valuebytes20=value, valuebytes32=value, valuebytes64=value, valuestr8=value, @@ -54,6 +57,7 @@ def test_hash(value): model = Model.from_single(value) assert len(model.valuebytes8) == 8 assert len(model.valuebytes16) == 16 + assert len(model.valuebytes20) == 20 assert len(model.valuebytes32) == 32 assert len(model.valuebytes64) == 64 assert len(model.valuestr8) == 18 @@ -76,6 +80,19 @@ def test_invalid_hash(value): Model.from_single(value) +def test_hash_removes_leading_zeroes_if_needed(): + address = "0x000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + + class MyModel(BaseModel): + my_address: HashBytes20 + + # Test both str and bytes for input. + for addr in (address, HexBytes(address)): + model = MyModel(my_address=addr) + assert len(model.my_address) == 20 + assert model.my_address == HexBytes("0xcafac3dd18ac6c6e92c921884f9e4176737c052c") + + def test_schema(): actual = Model.model_json_schema() for name, prop in actual["properties"].items(): diff --git a/tests/test_hex.py b/tests/test_hex.py index c263741..4c55bd8 100644 --- a/tests/test_hex.py +++ b/tests/test_hex.py @@ -2,6 +2,7 @@ from hexbytes import HexBytes as BaseHexBytes from pydantic import BaseModel, ValidationError +from eth_pydantic_types import HashStr20 from eth_pydantic_types.hex import HexBytes, HexStr @@ -115,3 +116,16 @@ def test_from_bytes(): value = b"\xb7\xfc\xef\x7f\xe7E\xf2\xa9U`\xff_U\x0e;\x8f" actual = HexStr.from_bytes(value) assert actual.startswith("0x") + + +def test_hex_removes_leading_zeroes_if_needed(): + address = "0x000000000000000000000000cafac3dd18ac6c6e92c921884f9e4176737c052c" + + class MyModel(BaseModel): + my_address: HashStr20 + + # Test both str and bytes for input. + for addr in (address, HexBytes(address)): + model = MyModel(my_address=addr) + assert len(model.my_address) == 42 + assert model.my_address == "0xcafac3dd18ac6c6e92c921884f9e4176737c052c" From 2011eea93e2e0ff4326d75eecbe4210081f0ab78 Mon Sep 17 00:00:00 2001 From: Juliya Smith Date: Fri, 15 Dec 2023 16:33:57 -0600 Subject: [PATCH 2/2] fix: breakage found in tests --- eth_pydantic_types/validators.py | 17 +++++++++++++++-- tests/test_hash.py | 1 + 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/eth_pydantic_types/validators.py b/eth_pydantic_types/validators.py index 7d846aa..4f01b5a 100644 --- a/eth_pydantic_types/validators.py +++ b/eth_pydantic_types/validators.py @@ -38,8 +38,21 @@ def validate_str_size(value: str, size: int) -> str: def _coerce_hexstr_size(val: str, length: int) -> str: - return "0" * (length - len(val)) + val.lstrip("0") if len(val) != length else val + val = val.replace("0x", "") if val.startswith("0x") else val + if len(val) == length: + return val + + val_stripped = val.lstrip("0") + num_zeroes = max(0, length - len(val_stripped)) + zeroes = "0" * num_zeroes + return f"{zeroes}{val_stripped}" def _coerce_hexbytes_size(val: bytes, num_bytes: int) -> bytes: - return b"\x00" * (num_bytes - len(val)) + val.lstrip(b"\x00") if len(val) != num_bytes else val + if len(val) == num_bytes: + return val + + val_stripped = val.lstrip(b"\x00") + num_zeroes = max(0, num_bytes - len(val_stripped)) + zeroes = b"\x00" * num_zeroes + return zeroes + val_stripped diff --git a/tests/test_hash.py b/tests/test_hash.py index 11121e5..0abe877 100644 --- a/tests/test_hash.py +++ b/tests/test_hash.py @@ -125,6 +125,7 @@ def test_model_dump(bytes32str): "valuebytes8": "0x0000000000000005", "valuestr8": "0x0000000000000005", "valuebytes16": "0x00000000000000000000000000000005", + "valuebytes20": "0x0000000000000000000000000000000000000005", "valuestr16": "0x00000000000000000000000000000005", "valuebytes32": "0x0000000000000000000000000000000000000000000000000000000000000005", "valuestr32": "0x0000000000000000000000000000000000000000000000000000000000000005",