/ test_libs / pyspec / eth2spec / debug / random_value.py
random_value.py
  1  from random import Random
  2  from typing import Any
  3  from enum import Enum
  4  
  5  
  6  UINT_SIZES = [8, 16, 32, 64, 128, 256]
  7  
  8  basic_types = ["uint%d" % v for v in UINT_SIZES] + ['bool', 'byte']
  9  
 10  random_mode_names = ["random", "zero", "max", "nil", "one", "lengthy"]
 11  
 12  
 13  class RandomizationMode(Enum):
 14      # random content / length
 15      mode_random = 0
 16      # Zero-value
 17      mode_zero = 1
 18      # Maximum value, limited to count 1 however
 19      mode_max = 2
 20      # Return 0 values, i.e. empty
 21      mode_nil_count = 3
 22      # Return 1 value, random content
 23      mode_one_count = 4
 24      # Return max amount of values, random content
 25      mode_max_count = 5
 26  
 27      def to_name(self):
 28          return random_mode_names[self.value]
 29  
 30      def is_changing(self):
 31          return self.value in [0, 4, 5]
 32  
 33  
 34  def get_random_ssz_object(rng: Random, typ: Any, max_bytes_length: int, max_list_length: int, mode: RandomizationMode, chaos: bool) -> Any:
 35      """
 36      Create an object for a given type, filled with random data.
 37      :param rng: The random number generator to use.
 38      :param typ: The type to instantiate
 39      :param max_bytes_length: the max. length for a random bytes array
 40      :param max_list_length: the max. length for a random list
 41      :param mode: how to randomize
 42      :param chaos: if true, the randomization-mode will be randomly changed
 43      :return: the random object instance, of the given type.
 44      """
 45      if chaos:
 46          mode = rng.choice(list(RandomizationMode))
 47      if isinstance(typ, str):
 48          # Bytes array
 49          if typ == 'bytes':
 50              if mode == RandomizationMode.mode_nil_count:
 51                  return b''
 52              if mode == RandomizationMode.mode_max_count:
 53                  return get_random_bytes_list(rng, max_bytes_length)
 54              if mode == RandomizationMode.mode_one_count:
 55                  return get_random_bytes_list(rng, 1)
 56              if mode == RandomizationMode.mode_zero:
 57                  return b'\x00'
 58              if mode == RandomizationMode.mode_max:
 59                  return b'\xff'
 60              return get_random_bytes_list(rng, rng.randint(0, max_bytes_length))
 61          elif typ[:5] == 'bytes' and len(typ) > 5:
 62              length = int(typ[5:])
 63              # Sanity, don't generate absurdly big random values
 64              # If a client is aiming to performance-test, they should create a benchmark suite.
 65              assert length <= max_bytes_length
 66              if mode == RandomizationMode.mode_zero:
 67                  return b'\x00' * length
 68              if mode == RandomizationMode.mode_max:
 69                  return b'\xff' * length
 70              return get_random_bytes_list(rng, length)
 71          # Basic types
 72          else:
 73              if mode == RandomizationMode.mode_zero:
 74                  return get_min_basic_value(typ)
 75              if mode == RandomizationMode.mode_max:
 76                  return get_max_basic_value(typ)
 77              return get_random_basic_value(rng, typ)
 78      # Vector:
 79      elif isinstance(typ, list) and len(typ) == 2:
 80          return [get_random_ssz_object(rng, typ[0], max_bytes_length, max_list_length, mode, chaos) for _ in range(typ[1])]
 81      # List:
 82      elif isinstance(typ, list) and len(typ) == 1:
 83          length = rng.randint(0, max_list_length)
 84          if mode == RandomizationMode.mode_one_count:
 85              length = 1
 86          if mode == RandomizationMode.mode_max_count:
 87              length = max_list_length
 88          return [get_random_ssz_object(rng, typ[0], max_bytes_length, max_list_length, mode, chaos) for _ in range(length)]
 89      # Container:
 90      elif hasattr(typ, 'fields'):
 91          return typ(**{field: get_random_ssz_object(rng, subtype, max_bytes_length, max_list_length, mode, chaos) for field, subtype in typ.fields.items()})
 92      else:
 93          print(typ)
 94          raise Exception("Type not recognized")
 95  
 96  
 97  def get_random_bytes_list(rng: Random, length: int) -> bytes:
 98      return bytes(rng.getrandbits(8) for _ in range(length))
 99  
100  
101  def get_random_basic_value(rng: Random, typ: str) -> Any:
102      if typ == 'bool':
103          return rng.choice((True, False))
104      if typ[:4] == 'uint':
105          size = int(typ[4:])
106          assert size in UINT_SIZES
107          return rng.randint(0, 2**size - 1)
108      if typ == 'byte':
109          return rng.randint(0, 8)
110      else:
111          raise ValueError("Not a basic type")
112  
113  
114  def get_min_basic_value(typ: str) -> Any:
115      if typ == 'bool':
116          return False
117      if typ[:4] == 'uint':
118          size = int(typ[4:])
119          assert size in UINT_SIZES
120          return 0
121      if typ == 'byte':
122          return 0x00
123      else:
124          raise ValueError("Not a basic type")
125  
126  
127  def get_max_basic_value(typ: str) -> Any:
128      if typ == 'bool':
129          return True
130      if typ[:4] == 'uint':
131          size = int(typ[4:])
132          assert size in UINT_SIZES
133          return 2**size - 1
134      if typ == 'byte':
135          return 0xff
136      else:
137          raise ValueError("Not a basic type")