138 lines
4.7 KiB
Python
138 lines
4.7 KiB
Python
from random import Random
|
|
from typing import Any
|
|
from enum import Enum
|
|
|
|
|
|
UINT_SIZES = [8, 16, 32, 64, 128, 256]
|
|
|
|
basic_types = ["uint%d" % v for v in UINT_SIZES] + ['bool', 'byte']
|
|
|
|
random_mode_names = ["random", "zero", "max", "nil", "one", "lengthy"]
|
|
|
|
|
|
class RandomizationMode(Enum):
|
|
# random content / length
|
|
mode_random = 0
|
|
# Zero-value
|
|
mode_zero = 1
|
|
# Maximum value, limited to count 1 however
|
|
mode_max = 2
|
|
# Return 0 values, i.e. empty
|
|
mode_nil_count = 3
|
|
# Return 1 value, random content
|
|
mode_one_count = 4
|
|
# Return max amount of values, random content
|
|
mode_max_count = 5
|
|
|
|
def to_name(self):
|
|
return random_mode_names[self.value]
|
|
|
|
def is_changing(self):
|
|
return self.value in [0, 4, 5]
|
|
|
|
|
|
def get_random_ssz_object(rng: Random, typ: Any, max_bytes_length: int, max_list_length: int, mode: RandomizationMode, chaos: bool) -> Any:
|
|
"""
|
|
Create an object for a given type, filled with random data.
|
|
:param rng: The random number generator to use.
|
|
:param typ: The type to instantiate
|
|
:param max_bytes_length: the max. length for a random bytes array
|
|
:param max_list_length: the max. length for a random list
|
|
:param mode: how to randomize
|
|
:param chaos: if true, the randomization-mode will be randomly changed
|
|
:return: the random object instance, of the given type.
|
|
"""
|
|
if chaos:
|
|
mode = rng.choice(list(RandomizationMode))
|
|
if isinstance(typ, str):
|
|
# Bytes array
|
|
if typ == 'bytes':
|
|
if mode == RandomizationMode.mode_nil_count:
|
|
return b''
|
|
if mode == RandomizationMode.mode_max_count:
|
|
return get_random_bytes_list(rng, max_bytes_length)
|
|
if mode == RandomizationMode.mode_one_count:
|
|
return get_random_bytes_list(rng, 1)
|
|
if mode == RandomizationMode.mode_zero:
|
|
return b'\x00'
|
|
if mode == RandomizationMode.mode_max:
|
|
return b'\xff'
|
|
return get_random_bytes_list(rng, rng.randint(0, max_bytes_length))
|
|
elif typ[:5] == 'bytes' and len(typ) > 5:
|
|
length = int(typ[5:])
|
|
# Sanity, don't generate absurdly big random values
|
|
# If a client is aiming to performance-test, they should create a benchmark suite.
|
|
assert length <= max_bytes_length
|
|
if mode == RandomizationMode.mode_zero:
|
|
return b'\x00' * length
|
|
if mode == RandomizationMode.mode_max:
|
|
return b'\xff' * length
|
|
return get_random_bytes_list(rng, length)
|
|
# Basic types
|
|
else:
|
|
if mode == RandomizationMode.mode_zero:
|
|
return get_min_basic_value(typ)
|
|
if mode == RandomizationMode.mode_max:
|
|
return get_max_basic_value(typ)
|
|
return get_random_basic_value(rng, typ)
|
|
# Vector:
|
|
elif isinstance(typ, list) and len(typ) == 2:
|
|
return [get_random_ssz_object(rng, typ[0], max_bytes_length, max_list_length, mode, chaos) for _ in range(typ[1])]
|
|
# List:
|
|
elif isinstance(typ, list) and len(typ) == 1:
|
|
length = rng.randint(0, max_list_length)
|
|
if mode == RandomizationMode.mode_one_count:
|
|
length = 1
|
|
if mode == RandomizationMode.mode_max_count:
|
|
length = max_list_length
|
|
return [get_random_ssz_object(rng, typ[0], max_bytes_length, max_list_length, mode, chaos) for _ in range(length)]
|
|
# Container:
|
|
elif hasattr(typ, 'fields'):
|
|
return typ(**{field: get_random_ssz_object(rng, subtype, max_bytes_length, max_list_length, mode, chaos) for field, subtype in typ.fields.items()})
|
|
else:
|
|
print(typ)
|
|
raise Exception("Type not recognized")
|
|
|
|
|
|
def get_random_bytes_list(rng: Random, length: int) -> bytes:
|
|
return bytes(rng.getrandbits(8) for _ in range(length))
|
|
|
|
|
|
def get_random_basic_value(rng: Random, typ: str) -> Any:
|
|
if typ == 'bool':
|
|
return rng.choice((True, False))
|
|
if typ[:4] == 'uint':
|
|
size = int(typ[4:])
|
|
assert size in UINT_SIZES
|
|
return rng.randint(0, 2**size - 1)
|
|
if typ == 'byte':
|
|
return rng.randint(0, 8)
|
|
else:
|
|
raise ValueError("Not a basic type")
|
|
|
|
|
|
def get_min_basic_value(typ: str) -> Any:
|
|
if typ == 'bool':
|
|
return False
|
|
if typ[:4] == 'uint':
|
|
size = int(typ[4:])
|
|
assert size in UINT_SIZES
|
|
return 0
|
|
if typ == 'byte':
|
|
return 0x00
|
|
else:
|
|
raise ValueError("Not a basic type")
|
|
|
|
|
|
def get_max_basic_value(typ: str) -> Any:
|
|
if typ == 'bool':
|
|
return True
|
|
if typ[:4] == 'uint':
|
|
size = int(typ[4:])
|
|
assert size in UINT_SIZES
|
|
return 2**size - 1
|
|
if typ == 'byte':
|
|
return 0xff
|
|
else:
|
|
raise ValueError("Not a basic type")
|