update ssz testing/debug utils
This commit is contained in:
parent
5048b9e87a
commit
a33c67894e
|
@ -1,39 +1,29 @@
|
||||||
|
from typing import Any
|
||||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||||
from eth2spec.utils.ssz.ssz_typing import (
|
from eth2spec.utils.ssz.ssz_typing import (
|
||||||
is_uint_type, is_bool_type, is_list_type,
|
SSZType, SSZValue, uint, Container, Bytes, List, Bit,
|
||||||
is_vector_type, is_bytes_type, is_bytesn_type, is_container_type,
|
|
||||||
read_vector_elem_type, read_list_elem_type,
|
|
||||||
Vector, BytesN
|
Vector, BytesN
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def decode(data, typ):
|
def decode(data: Any, typ: SSZType) -> SSZValue:
|
||||||
if is_uint_type(typ):
|
if issubclass(typ, (uint, Bit)):
|
||||||
return data
|
return typ(data)
|
||||||
elif is_bool_type(typ):
|
elif issubclass(typ, (List, Vector)):
|
||||||
assert data in (True, False)
|
return typ(decode(element, typ.elem_type) for element in data)
|
||||||
return data
|
elif issubclass(typ, (Bytes, BytesN)):
|
||||||
elif is_list_type(typ):
|
return typ(bytes.fromhex(data[2:]))
|
||||||
elem_typ = read_list_elem_type(typ)
|
elif issubclass(typ, Container):
|
||||||
return [decode(element, elem_typ) for element in data]
|
|
||||||
elif is_vector_type(typ):
|
|
||||||
elem_typ = read_vector_elem_type(typ)
|
|
||||||
return Vector(decode(element, elem_typ) for element in data)
|
|
||||||
elif is_bytes_type(typ):
|
|
||||||
return bytes.fromhex(data[2:])
|
|
||||||
elif is_bytesn_type(typ):
|
|
||||||
return BytesN(bytes.fromhex(data[2:]))
|
|
||||||
elif is_container_type(typ):
|
|
||||||
temp = {}
|
temp = {}
|
||||||
for field, subtype in typ.get_fields():
|
for field_name, field_type in typ.get_fields().items():
|
||||||
temp[field] = decode(data[field], subtype)
|
temp[field_name] = decode(data[field_name], field_type)
|
||||||
if field + "_hash_tree_root" in data:
|
if field_name + "_hash_tree_root" in data:
|
||||||
assert(data[field + "_hash_tree_root"][2:] ==
|
assert (data[field_name + "_hash_tree_root"][2:] ==
|
||||||
hash_tree_root(temp[field], subtype).hex())
|
hash_tree_root(temp[field_name]).hex())
|
||||||
ret = typ(**temp)
|
ret = typ(**temp)
|
||||||
if "hash_tree_root" in data:
|
if "hash_tree_root" in data:
|
||||||
assert(data["hash_tree_root"][2:] ==
|
assert (data["hash_tree_root"][2:] ==
|
||||||
hash_tree_root(ret, typ).hex())
|
hash_tree_root(ret).hex())
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Type not recognized: data={data}, typ={typ}")
|
raise Exception(f"Type not recognized: data={data}, typ={typ}")
|
||||||
|
|
|
@ -1,36 +1,30 @@
|
||||||
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
from eth2spec.utils.ssz.ssz_impl import hash_tree_root
|
||||||
from eth2spec.utils.ssz.ssz_typing import (
|
from eth2spec.utils.ssz.ssz_typing import (
|
||||||
is_uint_type, is_bool_type, is_list_type, is_vector_type, is_container_type,
|
SSZValue, uint, Container, Bytes, BytesN, List, Vector, Bit
|
||||||
read_elem_type,
|
|
||||||
uint
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def encode(value, typ, include_hash_tree_roots=False):
|
def encode(value: SSZValue, include_hash_tree_roots=False):
|
||||||
if is_uint_type(typ):
|
if isinstance(value, uint):
|
||||||
if hasattr(typ, '__supertype__'):
|
|
||||||
typ = typ.__supertype__
|
|
||||||
# Larger uints are boxed and the class declares their byte length
|
# Larger uints are boxed and the class declares their byte length
|
||||||
if issubclass(typ, uint) and typ.byte_len > 8:
|
if value.type().byte_len > 8:
|
||||||
return str(value)
|
return str(value)
|
||||||
return value
|
return value
|
||||||
elif is_bool_type(typ):
|
elif isinstance(value, Bit):
|
||||||
assert value in (True, False)
|
assert value in (True, False)
|
||||||
return value
|
return value
|
||||||
elif is_list_type(typ) or is_vector_type(typ):
|
elif isinstance(value, (List, Vector)):
|
||||||
elem_typ = read_elem_type(typ)
|
return [encode(element, include_hash_tree_roots) for element in value]
|
||||||
return [encode(element, elem_typ, include_hash_tree_roots) for element in value]
|
elif isinstance(value, (Bytes, BytesN)): # both bytes and BytesN
|
||||||
elif isinstance(typ, type) and issubclass(typ, bytes): # both bytes and BytesN
|
|
||||||
return '0x' + value.hex()
|
return '0x' + value.hex()
|
||||||
elif is_container_type(typ):
|
elif isinstance(value, Container):
|
||||||
ret = {}
|
ret = {}
|
||||||
for field, subtype in typ.get_fields():
|
for field_value, field_name in zip(value, value.get_fields().keys()):
|
||||||
field_value = getattr(value, field)
|
ret[field_name] = encode(field_value, include_hash_tree_roots)
|
||||||
ret[field] = encode(field_value, subtype, include_hash_tree_roots)
|
|
||||||
if include_hash_tree_roots:
|
if include_hash_tree_roots:
|
||||||
ret[field + "_hash_tree_root"] = '0x' + hash_tree_root(field_value, subtype).hex()
|
ret[field_name + "_hash_tree_root"] = '0x' + hash_tree_root(field_value).hex()
|
||||||
if include_hash_tree_roots:
|
if include_hash_tree_roots:
|
||||||
ret["hash_tree_root"] = '0x' + hash_tree_root(value, typ).hex()
|
ret["hash_tree_root"] = '0x' + hash_tree_root(value).hex()
|
||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Type not recognized: value={value}, typ={typ}")
|
raise Exception(f"Type not recognized: value={value}, typ={value.type()}")
|
||||||
|
|
|
@ -1,18 +1,13 @@
|
||||||
from random import Random
|
from random import Random
|
||||||
from typing import Any
|
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
|
|
||||||
from eth2spec.utils.ssz.ssz_impl import is_basic_type
|
|
||||||
|
|
||||||
from eth2spec.utils.ssz.ssz_typing import (
|
from eth2spec.utils.ssz.ssz_typing import (
|
||||||
is_uint_type, is_bool_type, is_list_type,
|
SSZType, SSZValue, BasicValue, BasicType, uint, Container, Bytes, List, Bit,
|
||||||
is_vector_type, is_bytes_type, is_bytesn_type, is_container_type,
|
Vector, BytesN
|
||||||
read_vector_elem_type, read_list_elem_type,
|
|
||||||
uint_byte_size
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# in bytes
|
# in bytes
|
||||||
UINT_SIZES = (1, 2, 4, 8, 16, 32)
|
UINT_BYTE_SIZES = (1, 2, 4, 8, 16, 32)
|
||||||
|
|
||||||
random_mode_names = ("random", "zero", "max", "nil", "one", "lengthy")
|
random_mode_names = ("random", "zero", "max", "nil", "one", "lengthy")
|
||||||
|
|
||||||
|
@ -39,11 +34,11 @@ class RandomizationMode(Enum):
|
||||||
|
|
||||||
|
|
||||||
def get_random_ssz_object(rng: Random,
|
def get_random_ssz_object(rng: Random,
|
||||||
typ: Any,
|
typ: SSZType,
|
||||||
max_bytes_length: int,
|
max_bytes_length: int,
|
||||||
max_list_length: int,
|
max_list_length: int,
|
||||||
mode: RandomizationMode,
|
mode: RandomizationMode,
|
||||||
chaos: bool) -> Any:
|
chaos: bool) -> SSZValue:
|
||||||
"""
|
"""
|
||||||
Create an object for a given type, filled with random data.
|
Create an object for a given type, filled with random data.
|
||||||
:param rng: The random number generator to use.
|
:param rng: The random number generator to use.
|
||||||
|
@ -56,33 +51,31 @@ def get_random_ssz_object(rng: Random,
|
||||||
"""
|
"""
|
||||||
if chaos:
|
if chaos:
|
||||||
mode = rng.choice(list(RandomizationMode))
|
mode = rng.choice(list(RandomizationMode))
|
||||||
if is_bytes_type(typ):
|
if issubclass(typ, Bytes):
|
||||||
# Bytes array
|
# Bytes array
|
||||||
if mode == RandomizationMode.mode_nil_count:
|
if mode == RandomizationMode.mode_nil_count:
|
||||||
return b''
|
return typ(b'')
|
||||||
elif mode == RandomizationMode.mode_max_count:
|
elif mode == RandomizationMode.mode_max_count:
|
||||||
return get_random_bytes_list(rng, max_bytes_length)
|
return typ(get_random_bytes_list(rng, max_bytes_length))
|
||||||
elif mode == RandomizationMode.mode_one_count:
|
elif mode == RandomizationMode.mode_one_count:
|
||||||
return get_random_bytes_list(rng, 1)
|
return typ(get_random_bytes_list(rng, 1))
|
||||||
elif mode == RandomizationMode.mode_zero:
|
elif mode == RandomizationMode.mode_zero:
|
||||||
return b'\x00'
|
return typ(b'\x00')
|
||||||
elif mode == RandomizationMode.mode_max:
|
elif mode == RandomizationMode.mode_max:
|
||||||
return b'\xff'
|
return typ(b'\xff')
|
||||||
else:
|
else:
|
||||||
return get_random_bytes_list(rng, rng.randint(0, max_bytes_length))
|
return typ(get_random_bytes_list(rng, rng.randint(0, max_bytes_length)))
|
||||||
elif is_bytesn_type(typ):
|
elif issubclass(typ, BytesN):
|
||||||
# BytesN
|
|
||||||
length = typ.length
|
|
||||||
# Sanity, don't generate absurdly big random values
|
# Sanity, don't generate absurdly big random values
|
||||||
# If a client is aiming to performance-test, they should create a benchmark suite.
|
# If a client is aiming to performance-test, they should create a benchmark suite.
|
||||||
assert length <= max_bytes_length
|
assert typ.length <= max_bytes_length
|
||||||
if mode == RandomizationMode.mode_zero:
|
if mode == RandomizationMode.mode_zero:
|
||||||
return b'\x00' * length
|
return typ(b'\x00' * typ.length)
|
||||||
elif mode == RandomizationMode.mode_max:
|
elif mode == RandomizationMode.mode_max:
|
||||||
return b'\xff' * length
|
return typ(b'\xff' * typ.length)
|
||||||
else:
|
else:
|
||||||
return get_random_bytes_list(rng, length)
|
return typ(get_random_bytes_list(rng, typ.length))
|
||||||
elif is_basic_type(typ):
|
elif issubclass(typ, BasicValue):
|
||||||
# Basic types
|
# Basic types
|
||||||
if mode == RandomizationMode.mode_zero:
|
if mode == RandomizationMode.mode_zero:
|
||||||
return get_min_basic_value(typ)
|
return get_min_basic_value(typ)
|
||||||
|
@ -90,32 +83,28 @@ def get_random_ssz_object(rng: Random,
|
||||||
return get_max_basic_value(typ)
|
return get_max_basic_value(typ)
|
||||||
else:
|
else:
|
||||||
return get_random_basic_value(rng, typ)
|
return get_random_basic_value(rng, typ)
|
||||||
elif is_vector_type(typ):
|
elif issubclass(typ, Vector):
|
||||||
# Vector
|
return typ(
|
||||||
elem_typ = read_vector_elem_type(typ)
|
get_random_ssz_object(rng, typ.elem_type, max_bytes_length, max_list_length, mode, chaos)
|
||||||
return [
|
|
||||||
get_random_ssz_object(rng, elem_typ, max_bytes_length, max_list_length, mode, chaos)
|
|
||||||
for _ in range(typ.length)
|
for _ in range(typ.length)
|
||||||
]
|
)
|
||||||
elif is_list_type(typ):
|
elif issubclass(typ, List):
|
||||||
# List
|
length = rng.randint(0, min(typ.length, max_list_length))
|
||||||
elem_typ = read_list_elem_type(typ)
|
|
||||||
length = rng.randint(0, max_list_length)
|
|
||||||
if mode == RandomizationMode.mode_one_count:
|
if mode == RandomizationMode.mode_one_count:
|
||||||
length = 1
|
length = 1
|
||||||
elif mode == RandomizationMode.mode_max_count:
|
elif mode == RandomizationMode.mode_max_count:
|
||||||
length = max_list_length
|
length = max_list_length
|
||||||
|
|
||||||
return [
|
return typ(
|
||||||
get_random_ssz_object(rng, elem_typ, max_bytes_length, max_list_length, mode, chaos)
|
get_random_ssz_object(rng, typ.elem_type, max_bytes_length, max_list_length, mode, chaos)
|
||||||
for _ in range(length)
|
for _ in range(length)
|
||||||
]
|
)
|
||||||
elif is_container_type(typ):
|
elif issubclass(typ, Container):
|
||||||
# Container
|
# Container
|
||||||
return typ(**{
|
return typ(**{
|
||||||
field:
|
field_name:
|
||||||
get_random_ssz_object(rng, subtype, max_bytes_length, max_list_length, mode, chaos)
|
get_random_ssz_object(rng, field_type, max_bytes_length, max_list_length, mode, chaos)
|
||||||
for field, subtype in typ.get_fields()
|
for field_name, field_type in typ.get_fields().items()
|
||||||
})
|
})
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Type not recognized: typ={typ}")
|
raise Exception(f"Type not recognized: typ={typ}")
|
||||||
|
@ -125,34 +114,31 @@ def get_random_bytes_list(rng: Random, length: int) -> bytes:
|
||||||
return bytes(rng.getrandbits(8) for _ in range(length))
|
return bytes(rng.getrandbits(8) for _ in range(length))
|
||||||
|
|
||||||
|
|
||||||
def get_random_basic_value(rng: Random, typ) -> Any:
|
def get_random_basic_value(rng: Random, typ: BasicType) -> BasicValue:
|
||||||
if is_bool_type(typ):
|
if issubclass(typ, Bit):
|
||||||
return rng.choice((True, False))
|
return typ(rng.choice((True, False)))
|
||||||
elif is_uint_type(typ):
|
elif issubclass(typ, uint):
|
||||||
size = uint_byte_size(typ)
|
assert typ.byte_len in UINT_BYTE_SIZES
|
||||||
assert size in UINT_SIZES
|
return typ(rng.randint(0, 256 ** typ.byte_len - 1))
|
||||||
return rng.randint(0, 256**size - 1)
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Not a basic type: typ={typ}")
|
raise ValueError(f"Not a basic type: typ={typ}")
|
||||||
|
|
||||||
|
|
||||||
def get_min_basic_value(typ) -> Any:
|
def get_min_basic_value(typ: BasicType) -> BasicValue:
|
||||||
if is_bool_type(typ):
|
if issubclass(typ, Bit):
|
||||||
return False
|
return typ(False)
|
||||||
elif is_uint_type(typ):
|
elif issubclass(typ, uint):
|
||||||
size = uint_byte_size(typ)
|
assert typ.byte_len in UINT_BYTE_SIZES
|
||||||
assert size in UINT_SIZES
|
return typ(0)
|
||||||
return 0
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Not a basic type: typ={typ}")
|
raise ValueError(f"Not a basic type: typ={typ}")
|
||||||
|
|
||||||
|
|
||||||
def get_max_basic_value(typ) -> Any:
|
def get_max_basic_value(typ: BasicType) -> BasicValue:
|
||||||
if is_bool_type(typ):
|
if issubclass(typ, Bit):
|
||||||
return True
|
return typ(True)
|
||||||
elif is_uint_type(typ):
|
elif issubclass(typ, uint):
|
||||||
size = uint_byte_size(typ)
|
assert typ.byte_len in UINT_BYTE_SIZES
|
||||||
assert size in UINT_SIZES
|
return typ(256 ** typ.byte_len - 1)
|
||||||
return 256**size - 1
|
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Not a basic type: typ={typ}")
|
raise ValueError(f"Not a basic type: typ={typ}")
|
||||||
|
|
|
@ -364,7 +364,10 @@ class BytesLike(Elements, metaclass=BytesType):
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
cls = self.__class__
|
cls = self.__class__
|
||||||
return f"{cls.__name__}[{cls.length}]: {self.items.hex()}"
|
return f"{cls.__name__}[{cls.length}]: {self.hex()}"
|
||||||
|
|
||||||
|
def hex(self) -> str:
|
||||||
|
return self.items.hex()
|
||||||
|
|
||||||
|
|
||||||
class Bytes(BytesLike):
|
class Bytes(BytesLike):
|
||||||
|
|
Loading…
Reference in New Issue