eth2.0-specs/test_libs/pyspec/eth2spec/debug/random_value.py

148 lines
5.3 KiB
Python
Raw Normal View History

from random import Random
from enum import Enum
from eth2spec.utils.ssz.ssz_typing import (
SSZType, SSZValue, BasicValue, BasicType, uint, Container, Bytes, List, boolean,
Vector, BytesN, Bitlist, Bitvector
)
# in bytes
2019-06-19 00:37:22 +00:00
UINT_BYTE_SIZES = (1, 2, 4, 8, 16, 32)
2019-06-04 09:55:05 +00:00
random_mode_names = ("random", "zero", "max", "nil", "one", "lengthy")
2019-04-17 03:49:29 +00:00
class RandomizationMode(Enum):
# random content / length
mode_random = 0
# Zero-value
2019-04-17 03:49:29 +00:00
mode_zero = 1
# Maximum value, limited to count 1 however
2019-04-17 03:49:29 +00:00
mode_max = 2
# Return 0 values, i.e. empty
2019-04-17 03:49:29 +00:00
mode_nil_count = 3
# Return 1 value, random content
2019-04-17 03:49:29 +00:00
mode_one_count = 4
# Return max amount of values, random content
2019-04-17 03:49:29 +00:00
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]
2019-05-09 06:29:21 +00:00
def get_random_ssz_object(rng: Random,
2019-06-19 00:37:22 +00:00
typ: SSZType,
2019-05-09 06:29:21 +00:00
max_bytes_length: int,
max_list_length: int,
mode: RandomizationMode,
2019-06-19 00:37:22 +00:00
chaos: bool) -> SSZValue:
"""
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))
2019-06-19 00:37:22 +00:00
if issubclass(typ, Bytes):
2019-06-04 09:42:21 +00:00
# Bytes array
if mode == RandomizationMode.mode_nil_count:
2019-06-19 00:37:22 +00:00
return typ(b'')
2019-06-04 09:42:21 +00:00
elif mode == RandomizationMode.mode_max_count:
return typ(get_random_bytes_list(rng, min(max_bytes_length, typ.length)))
2019-06-04 09:42:21 +00:00
elif mode == RandomizationMode.mode_one_count:
return typ(get_random_bytes_list(rng, min(1, typ.length)))
2019-06-04 09:42:21 +00:00
elif mode == RandomizationMode.mode_zero:
return typ(b'\x00' * min(1, typ.length))
2019-06-04 09:42:21 +00:00
elif mode == RandomizationMode.mode_max:
return typ(b'\xff' * min(1, typ.length))
2019-06-04 09:42:21 +00:00
else:
return typ(get_random_bytes_list(rng, rng.randint(0, min(max_bytes_length, typ.length))))
2019-06-19 00:37:22 +00:00
elif issubclass(typ, BytesN):
# Sanity, don't generate absurdly big random values
# If a client is aiming to performance-test, they should create a benchmark suite.
2019-06-19 00:37:22 +00:00
assert typ.length <= max_bytes_length
if mode == RandomizationMode.mode_zero:
2019-06-19 00:37:22 +00:00
return typ(b'\x00' * typ.length)
2019-06-04 09:42:21 +00:00
elif mode == RandomizationMode.mode_max:
2019-06-19 00:37:22 +00:00
return typ(b'\xff' * typ.length)
2019-06-04 09:42:21 +00:00
else:
2019-06-19 00:37:22 +00:00
return typ(get_random_bytes_list(rng, typ.length))
elif issubclass(typ, BasicValue):
2019-06-04 09:42:21 +00:00
# Basic types
if mode == RandomizationMode.mode_zero:
return get_min_basic_value(typ)
2019-06-04 09:42:21 +00:00
elif mode == RandomizationMode.mode_max:
return get_max_basic_value(typ)
2019-06-04 09:42:21 +00:00
else:
return get_random_basic_value(rng, typ)
elif issubclass(typ, Vector) or issubclass(typ, Bitvector):
2019-06-19 00:37:22 +00:00
return typ(
get_random_ssz_object(rng, typ.elem_type, max_bytes_length, max_list_length, mode, chaos)
for _ in range(typ.length)
2019-06-19 00:37:22 +00:00
)
elif issubclass(typ, List) or issubclass(typ, Bitlist):
2019-06-19 00:37:22 +00:00
length = rng.randint(0, min(typ.length, max_list_length))
if mode == RandomizationMode.mode_one_count:
length = 1
2019-06-04 09:42:21 +00:00
elif mode == RandomizationMode.mode_max_count:
length = max_list_length
2019-06-04 09:42:21 +00:00
if typ.length < length: # SSZ imposes a hard limit on lists, we can't put in more than that
length = typ.length
2019-06-19 00:37:22 +00:00
return typ(
get_random_ssz_object(rng, typ.elem_type, max_bytes_length, max_list_length, mode, chaos)
2019-05-09 06:29:21 +00:00
for _ in range(length)
2019-06-19 00:37:22 +00:00
)
elif issubclass(typ, Container):
2019-06-04 09:42:21 +00:00
# Container
2019-05-09 06:29:21 +00:00
return typ(**{
2019-06-19 00:37:22 +00:00
field_name:
get_random_ssz_object(rng, field_type, max_bytes_length, max_list_length, mode, chaos)
for field_name, field_type in typ.get_fields().items()
2019-05-09 06:29:21 +00:00
})
else:
2019-06-04 09:42:21 +00:00
raise Exception(f"Type not recognized: typ={typ}")
def get_random_bytes_list(rng: Random, length: int) -> bytes:
return bytes(rng.getrandbits(8) for _ in range(length))
2019-06-19 00:37:22 +00:00
def get_random_basic_value(rng: Random, typ: BasicType) -> BasicValue:
if issubclass(typ, boolean):
2019-06-19 00:37:22 +00:00
return typ(rng.choice((True, False)))
elif issubclass(typ, uint):
assert typ.byte_len in UINT_BYTE_SIZES
return typ(rng.randint(0, 256 ** typ.byte_len - 1))
else:
2019-06-04 09:42:21 +00:00
raise ValueError(f"Not a basic type: typ={typ}")
2019-06-19 00:37:22 +00:00
def get_min_basic_value(typ: BasicType) -> BasicValue:
if issubclass(typ, boolean):
2019-06-19 00:37:22 +00:00
return typ(False)
elif issubclass(typ, uint):
assert typ.byte_len in UINT_BYTE_SIZES
return typ(0)
else:
2019-06-04 09:42:21 +00:00
raise ValueError(f"Not a basic type: typ={typ}")
2019-06-19 00:37:22 +00:00
def get_max_basic_value(typ: BasicType) -> BasicValue:
if issubclass(typ, boolean):
2019-06-19 00:37:22 +00:00
return typ(True)
elif issubclass(typ, uint):
assert typ.byte_len in UINT_BYTE_SIZES
return typ(256 ** typ.byte_len - 1)
else:
2019-06-04 09:42:21 +00:00
raise ValueError(f"Not a basic type: typ={typ}")