Repository: ethereum/consensus-specs

Issue:
BytesN SSZ Alias is Misleading
Hi all, currently the SSZ spec [alias section](https://github.com/ethereum/eth2.0-specs/blob/dev/specs/simple-serialize.md#aliases) mentions:

```
BytesN to Vector[byte, N] (this is not a basic type)
```
and we also have the type `Byte[N]` in some other places across the repo including the test generators for ssz generic containers [here](https://github.com/ethereum/eth2.0-specs/blob/40cb72ec112903a28cbfc9e310e14844680476e5/test_generators/ssz_generic/ssz_container.py#L35). This tricky nomenclature led us to some painful debugging and quite some time spent fixing broken tests in Prysm. The alias doesn't add much utility and we at @prylabs would just prefer to stick to the suggested naming conventions for lists vs. vectors:

```
List[byte, N]
Vector[byte, N]
```


Discussion:
How is it misleading? Other than that we should probably add ```Byte[N]``` to our list of aliases?
Why need the alias and more complexity? Why not just stick to the basic definitions which are clear to follow:
```go
List[byte, N]
Vector[byte, N]
```
It was just a point of frustration when trying to pass spec tests and got confused between the types. Would have been just easier if we didn't introduce those custom aliases. Spent a good chunk of time fixing broken tests due to confusion, unfortunately.


Resolving patch:
diff --git a/scripts/build_spec.py b/scripts/build_spec.py
--- a/scripts/build_spec.py
+++ b/scripts/build_spec.py
@@ -56,7 +56,7 @@
 )
 from eth2spec.utils.ssz.ssz_typing import (
     BasicValue, Elements, BaseBytes, BaseList, SSZType,
-    Container, List, Vector, Bytes, BytesN, Bitlist, Bitvector, Bits,
+    Container, List, Vector, ByteList, ByteVector, Bitlist, Bitvector, Bits,
     Bytes1, Bytes4, Bytes8, Bytes32, Bytes48, Bytes96,
     uint64, bit, boolean, byte,
 )
@@ -211,10 +211,10 @@ def combine_constants(old_constants: Dict[str, str], new_constants: Dict[str, st
 
 
 ignored_dependencies = [
-    'bit', 'boolean', 'Vector', 'List', 'Container', 'Hash', 'BLSPubkey', 'BLSSignature', 'Bytes', 'BytesN'
+    'bit', 'boolean', 'Vector', 'List', 'Container', 'Hash', 'BLSPubkey', 'BLSSignature', 'ByteList', 'ByteVector'
     'Bytes1', 'Bytes4', 'Bytes32', 'Bytes48', 'Bytes96', 'Bitlist', 'Bitvector',
     'uint8', 'uint16', 'uint32', 'uint64', 'uint128', 'uint256',
-    'bytes', 'byte', 'BytesN'  # to be removed after updating spec doc
+    'bytes', 'byte', 'ByteVector'  # to be removed after updating spec doc
 ]
 
 


Test patch:
diff --git a/test_libs/pyspec/eth2spec/debug/decode.py b/test_libs/pyspec/eth2spec/debug/decode.py
--- a/test_libs/pyspec/eth2spec/debug/decode.py
+++ b/test_libs/pyspec/eth2spec/debug/decode.py
@@ -1,8 +1,8 @@
 from typing import Any
 from eth2spec.utils.ssz.ssz_impl import hash_tree_root
 from eth2spec.utils.ssz.ssz_typing import (
-    SSZType, SSZValue, uint, Container, Bytes, List, boolean,
-    Vector, BytesN
+    SSZType, SSZValue, uint, Container, ByteList, List, boolean,
+    Vector, ByteVector
 )
 
 
@@ -11,7 +11,7 @@ def decode(data: Any, typ: SSZType) -> SSZValue:
         return typ(data)
     elif issubclass(typ, (List, Vector)):
         return typ(decode(element, typ.elem_type) for element in data)
-    elif issubclass(typ, (Bytes, BytesN)):
+    elif issubclass(typ, (ByteList, ByteVector)):
         return typ(bytes.fromhex(data[2:]))
     elif issubclass(typ, Container):
         temp = {}
diff --git a/test_libs/pyspec/eth2spec/debug/encode.py b/test_libs/pyspec/eth2spec/debug/encode.py
--- a/test_libs/pyspec/eth2spec/debug/encode.py
+++ b/test_libs/pyspec/eth2spec/debug/encode.py
@@ -17,7 +17,7 @@ def encode(value, include_hash_tree_roots=False):
         return '0x' + serialize(value).hex()
     elif isinstance(value, list):  # normal python lists, ssz-List, Vector
         return [encode(element, include_hash_tree_roots) for element in value]
-    elif isinstance(value, bytes):  # both bytes and BytesN
+    elif isinstance(value, bytes):  # both bytes and ByteVector
         return '0x' + value.hex()
     elif isinstance(value, Container):
         ret = {}
diff --git a/test_libs/pyspec/eth2spec/debug/random_value.py b/test_libs/pyspec/eth2spec/debug/random_value.py
--- a/test_libs/pyspec/eth2spec/debug/random_value.py
+++ b/test_libs/pyspec/eth2spec/debug/random_value.py
@@ -2,8 +2,8 @@
 from enum import Enum
 
 from eth2spec.utils.ssz.ssz_typing import (
-    SSZType, SSZValue, BasicValue, BasicType, uint, Container, Bytes, List, boolean,
-    Vector, BytesN, Bitlist, Bitvector
+    SSZType, SSZValue, BasicValue, BasicType, uint, Container, ByteList, List, boolean,
+    Vector, ByteVector, Bitlist, Bitvector
 )
 
 # in bytes
@@ -51,8 +51,8 @@ def get_random_ssz_object(rng: Random,
     """
     if chaos:
         mode = rng.choice(list(RandomizationMode))
-    if issubclass(typ, Bytes):
-        # Bytes array
+    if issubclass(typ, ByteList):
+        # ByteList array
         if mode == RandomizationMode.mode_nil_count:
             return typ(b'')
         elif mode == RandomizationMode.mode_max_count:
@@ -65,7 +65,7 @@ def get_random_ssz_object(rng: Random,
             return typ(b'\xff' * min(1, typ.length))
         else:
             return typ(get_random_bytes_list(rng, rng.randint(0, min(max_bytes_length, typ.length))))
-    elif issubclass(typ, BytesN):
+    elif issubclass(typ, ByteVector):
         # Sanity, don't generate absurdly big random values
         # If a client is aiming to performance-test, they should create a benchmark suite.
         assert typ.length <= max_bytes_length
diff --git a/test_libs/pyspec/eth2spec/fuzzing/decoder.py b/test_libs/pyspec/eth2spec/fuzzing/decoder.py
--- a/test_libs/pyspec/eth2spec/fuzzing/decoder.py
+++ b/test_libs/pyspec/eth2spec/fuzzing/decoder.py
@@ -11,9 +11,9 @@ def translate_typ(typ) -> ssz.BaseSedes:
     if issubclass(typ, spec_ssz.Container):
         return ssz.Container(
             [translate_typ(field_typ) for field_name, field_typ in typ.get_fields().items()])
-    elif issubclass(typ, spec_ssz.BytesN):
+    elif issubclass(typ, spec_ssz.ByteVector):
         return ssz.ByteVector(typ.length)
-    elif issubclass(typ, spec_ssz.Bytes):
+    elif issubclass(typ, spec_ssz.ByteList):
         return ssz.ByteList()
     elif issubclass(typ, spec_ssz.Vector):
         return ssz.Vector(translate_typ(typ.elem_type), typ.length)
@@ -76,9 +76,9 @@ def translate_value(value, typ):
         return typ(value)
     elif issubclass(typ, spec_ssz.Bitvector):
         return typ(value)
-    elif issubclass(typ, spec_ssz.BytesN):
+    elif issubclass(typ, spec_ssz.ByteVector):
         return typ(value)
-    elif issubclass(typ, spec_ssz.Bytes):
+    elif issubclass(typ, spec_ssz.ByteList):
         return value
     if issubclass(typ, spec_ssz.Container):
         return typ(**{f_name: translate_value(f_val, f_typ) for (f_val, (f_name, f_typ))
diff --git a/test_libs/pyspec/eth2spec/test/helpers/custody.py b/test_libs/pyspec/eth2spec/test/helpers/custody.py
--- a/test_libs/pyspec/eth2spec/test/helpers/custody.py
+++ b/test_libs/pyspec/eth2spec/test/helpers/custody.py
@@ -1,7 +1,7 @@
 from eth2spec.test.helpers.keys import privkeys
 from eth2spec.utils.bls import bls_sign, bls_aggregate_signatures
 from eth2spec.utils.hash_function import hash
-from eth2spec.utils.ssz.ssz_typing import Bitlist, BytesN, Bitvector
+from eth2spec.utils.ssz.ssz_typing import Bitlist, ByteVector, Bitvector
 from eth2spec.utils.ssz.ssz_impl import chunkify, pack, hash_tree_root
 from eth2spec.utils.merkle_minimal import get_merkle_tree, get_merkle_proof
 
@@ -136,9 +136,9 @@ def get_valid_custody_response(spec, state, bit_challenge, custody_data, challen
         chunk_index -= 1
         chunk_bit = spec.get_custody_chunk_bit(bit_challenge.responder_key, chunks[chunk_index])
 
-    chunks_hash_tree_roots = [hash_tree_root(BytesN[spec.BYTES_PER_CUSTODY_CHUNK](chunk)) for chunk in chunks]
+    chunks_hash_tree_roots = [hash_tree_root(ByteVector[spec.BYTES_PER_CUSTODY_CHUNK](chunk)) for chunk in chunks]
     chunks_hash_tree_roots += [
-        hash_tree_root(BytesN[spec.BYTES_PER_CUSTODY_CHUNK](b"\0" * spec.BYTES_PER_CUSTODY_CHUNK))
+        hash_tree_root(ByteVector[spec.BYTES_PER_CUSTODY_CHUNK](b"\0" * spec.BYTES_PER_CUSTODY_CHUNK))
         for i in range(2 ** spec.ceillog2(len(chunks)) - len(chunks))]
     data_tree = get_merkle_tree(chunks_hash_tree_roots)
 
@@ -158,7 +158,7 @@ def get_valid_custody_response(spec, state, bit_challenge, custody_data, challen
     return spec.CustodyResponse(
         challenge_index=challenge_index,
         chunk_index=chunk_index,
-        chunk=BytesN[spec.BYTES_PER_CUSTODY_CHUNK](chunks[chunk_index]),
+        chunk=ByteVector[spec.BYTES_PER_CUSTODY_CHUNK](chunks[chunk_index]),
         data_branch=data_branch,
         chunk_bits_branch=bitlist_chunk_branch,
         chunk_bits_leaf=chunk_bits_leaf,
diff --git a/test_libs/pyspec/eth2spec/utils/ssz/ssz_impl.py b/test_libs/pyspec/eth2spec/utils/ssz/ssz_impl.py
--- a/test_libs/pyspec/eth2spec/utils/ssz/ssz_impl.py
+++ b/test_libs/pyspec/eth2spec/utils/ssz/ssz_impl.py
@@ -1,7 +1,7 @@
 from ..merkle_minimal import merkleize_chunks
 from ..hash_function import hash
 from .ssz_typing import (
-    SSZValue, SSZType, BasicValue, BasicType, Series, Elements, Bits, boolean, Container, List, Bytes,
+    SSZValue, SSZType, BasicValue, BasicType, Series, Elements, Bits, boolean, Container, List, ByteList,
     Bitlist, Bitvector, uint,
 )
 
@@ -56,7 +56,7 @@ def serialize(obj: SSZValue):
 
 
 def encode_series(values: Series):
-    if isinstance(values, bytes):  # Bytes and BytesN are already like serialized output
+    if isinstance(values, bytes):  # ByteList and ByteVector are already like serialized output
         return values
 
     # Recursively serialize
@@ -93,7 +93,7 @@ def encode_series(values: Series):
 
 
 def pack(values: Series):
-    if isinstance(values, bytes):  # Bytes and BytesN are already packed
+    if isinstance(values, bytes):  # ByteList and ByteVector are already packed
         return values
     elif isinstance(values, Bits):
         # packs the bits in bytes, left-aligned.
@@ -151,7 +151,7 @@ def hash_tree_root(obj: SSZValue):
     else:
         raise Exception(f"Type not supported: {type(obj)}")
 
-    if isinstance(obj, (List, Bytes, Bitlist)):
+    if isinstance(obj, (List, ByteList, Bitlist)):
         return mix_in_length(merkleize_chunks(leaves, limit=chunk_count(obj.type())), len(obj))
     else:
         return merkleize_chunks(leaves)
diff --git a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py
--- a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py
+++ b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py
@@ -115,7 +115,7 @@ def coerce_type_maybe(v, typ: SSZType, strict: bool = False):
             return typ(v)
     elif isinstance(v, (list, tuple)):
         return typ(*v)
-    elif isinstance(v, (bytes, BytesN, Bytes)):
+    elif isinstance(v, (bytes, ByteVector, ByteList)):
         return typ(v)
     elif isinstance(v, GeneratorType):
         return typ(v)
@@ -472,7 +472,7 @@ def __str__(self):
         return f"{cls.__name__}[{cls.length}]: {self.hex()}"
 
 
-class Bytes(BaseBytes):
+class ByteList(BaseBytes):
 
     @classmethod
     def default(cls):
@@ -483,7 +483,7 @@ def is_fixed_size(cls):
         return False
 
 
-class BytesN(BaseBytes):
+class ByteVector(BaseBytes):
 
     @classmethod
     def extract_args(cls, *args):
@@ -506,10 +506,10 @@ def is_fixed_size(cls):
         return True
 
 
-# Helpers for common BytesN types
-Bytes1: BytesType = BytesN[1]
-Bytes4: BytesType = BytesN[4]
-Bytes8: BytesType = BytesN[8]
-Bytes32: BytesType = BytesN[32]
-Bytes48: BytesType = BytesN[48]
-Bytes96: BytesType = BytesN[96]
+# Helpers for common ByteVector types
+Bytes1: BytesType = ByteVector[1]
+Bytes4: BytesType = ByteVector[4]
+Bytes8: BytesType = ByteVector[8]
+Bytes32: BytesType = ByteVector[32]
+Bytes48: BytesType = ByteVector[48]
+Bytes96: BytesType = ByteVector[96]
diff --git a/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_impl.py b/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_impl.py
--- a/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_impl.py
+++ b/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_impl.py
@@ -1,7 +1,7 @@
 from typing import Iterable
 from .ssz_impl import serialize, hash_tree_root
 from .ssz_typing import (
-    bit, boolean, Container, List, Vector, Bytes, BytesN,
+    bit, boolean, Container, List, Vector, ByteList, ByteVector,
     Bitlist, Bitvector,
     uint8, uint16, uint32, uint64, uint256, byte
 )
@@ -39,7 +39,7 @@ class ComplexTestStruct(Container):
     A: uint16
     B: List[uint16, 128]
     C: uint8
-    D: Bytes[256]
+    D: ByteList[256]
     E: VarTestStruct
     F: Vector[FixedTestStruct, 4]
     G: Vector[VarTestStruct, 2]
@@ -116,7 +116,7 @@ def merge(a: str, branch: Iterable[str]) -> str:
     ("uint32 01234567", uint32(0x01234567), "67452301", chunk("67452301")),
     ("uint64 0000000000000000", uint64(0x00000000), "0000000000000000", chunk("0000000000000000")),
     ("uint64 0123456789abcdef", uint64(0x0123456789abcdef), "efcdab8967452301", chunk("efcdab8967452301")),
-    ("sig", BytesN[96](*sig_test_data),
+    ("sig", ByteVector[96](*sig_test_data),
      "0100000000000000000000000000000000000000000000000000000000000000"
      "0200000000000000000000000000000000000000000000000000000000000000"
      "03000000000000000000000000000000000000000000000000000000000000ff",
@@ -187,7 +187,7 @@ def merge(a: str, branch: Iterable[str]) -> str:
          A=0xaabb,
          B=List[uint16, 128](0x1122, 0x3344),
          C=0xff,
-         D=Bytes[256](b"foobar"),
+         D=ByteList[256](b"foobar"),
          E=VarTestStruct(A=0xabcd, B=List[uint16, 1024](1, 2, 3), C=0xff),
          F=Vector[FixedTestStruct, 4](
              FixedTestStruct(A=0xcc, B=0x4242424242424242, C=0x13371337),
diff --git a/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_typing.py b/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_typing.py
--- a/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_typing.py
+++ b/test_libs/pyspec/eth2spec/utils/ssz/test_ssz_typing.py
@@ -1,6 +1,6 @@
 from .ssz_typing import (
     SSZValue, SSZType, BasicValue, BasicType, Series, ElementsType,
-    Elements, bit, boolean, Container, List, Vector, Bytes, BytesN,
+    Elements, bit, boolean, Container, List, Vector, ByteList, ByteVector,
     byte, uint, uint8, uint16, uint32, uint64, uint128, uint256,
     Bytes32, Bytes48
 )
@@ -25,14 +25,14 @@ def test_subclasses():
     assert issubclass(boolean, BasicValue)
     assert isinstance(boolean, BasicType)
 
-    for c in [Container, List, Vector, Bytes, BytesN]:
+    for c in [Container, List, Vector, ByteList, ByteVector]:
         assert issubclass(c, Series)
         assert issubclass(c, SSZValue)
         assert isinstance(c, SSZType)
         assert not issubclass(c, BasicValue)
         assert not isinstance(c, BasicType)
 
-    for c in [List, Vector, Bytes, BytesN]:
+    for c in [List, Vector, ByteList, ByteVector]:
         assert issubclass(c, Elements)
         assert isinstance(c, ElementsType)
 
@@ -203,10 +203,10 @@ def test_list():
 
 
 def test_bytesn_subclass():
-    assert isinstance(BytesN[32](b'\xab' * 32), Bytes32)
-    assert not isinstance(BytesN[32](b'\xab' * 32), Bytes48)
-    assert issubclass(BytesN[32](b'\xab' * 32).type(), Bytes32)
-    assert issubclass(BytesN[32], Bytes32)
+    assert isinstance(ByteVector[32](b'\xab' * 32), Bytes32)
+    assert not isinstance(ByteVector[32](b'\xab' * 32), Bytes48)
+    assert issubclass(ByteVector[32](b'\xab' * 32).type(), Bytes32)
+    assert issubclass(ByteVector[32], Bytes32)
 
     class Hash(Bytes32):
         pass