Merge pull request #1178 from ethereum/decode-with-pyssz
Fuzzing utilities package / SSZ decoding for spec
This commit is contained in:
commit
25a16bd26a
|
@ -0,0 +1,84 @@
|
||||||
|
from eth2spec.utils.ssz import ssz_typing as spec_ssz
|
||||||
|
import ssz
|
||||||
|
|
||||||
|
|
||||||
|
def translate_typ(typ) -> ssz.BaseSedes:
|
||||||
|
"""
|
||||||
|
Translates a spec type to a Py-SSZ type description (sedes).
|
||||||
|
:param typ: The spec type, a class.
|
||||||
|
:return: The Py-SSZ equivalent.
|
||||||
|
"""
|
||||||
|
if spec_ssz.is_container_type(typ):
|
||||||
|
return ssz.Container(
|
||||||
|
[translate_typ(field_typ) for (field_name, field_typ) in typ.get_fields()])
|
||||||
|
elif spec_ssz.is_bytesn_type(typ):
|
||||||
|
return ssz.ByteVector(typ.length)
|
||||||
|
elif spec_ssz.is_bytes_type(typ):
|
||||||
|
return ssz.ByteList()
|
||||||
|
elif spec_ssz.is_vector_type(typ):
|
||||||
|
return ssz.Vector(translate_typ(spec_ssz.read_vector_elem_type(typ)), typ.length)
|
||||||
|
elif spec_ssz.is_list_type(typ):
|
||||||
|
return ssz.List(translate_typ(spec_ssz.read_list_elem_type(typ)))
|
||||||
|
elif spec_ssz.is_bool_type(typ):
|
||||||
|
return ssz.boolean
|
||||||
|
elif spec_ssz.is_uint_type(typ):
|
||||||
|
size = spec_ssz.uint_byte_size(typ)
|
||||||
|
if size == 1:
|
||||||
|
return ssz.uint8
|
||||||
|
elif size == 2:
|
||||||
|
return ssz.uint16
|
||||||
|
elif size == 4:
|
||||||
|
return ssz.uint32
|
||||||
|
elif size == 8:
|
||||||
|
return ssz.uint64
|
||||||
|
elif size == 16:
|
||||||
|
return ssz.uint128
|
||||||
|
elif size == 32:
|
||||||
|
return ssz.uint256
|
||||||
|
else:
|
||||||
|
raise TypeError("invalid uint size")
|
||||||
|
else:
|
||||||
|
raise TypeError("Type not supported: {}".format(typ))
|
||||||
|
|
||||||
|
|
||||||
|
def translate_value(value, typ):
|
||||||
|
"""
|
||||||
|
Translate a value output from Py-SSZ deserialization into the given spec type.
|
||||||
|
:param value: The PySSZ value
|
||||||
|
:param typ: The type from the spec to translate into
|
||||||
|
:return: the translated value
|
||||||
|
"""
|
||||||
|
if spec_ssz.is_uint_type(typ):
|
||||||
|
size = spec_ssz.uint_byte_size(typ)
|
||||||
|
if size == 1:
|
||||||
|
return spec_ssz.uint8(value)
|
||||||
|
elif size == 2:
|
||||||
|
return spec_ssz.uint16(value)
|
||||||
|
elif size == 4:
|
||||||
|
return spec_ssz.uint32(value)
|
||||||
|
elif size == 8:
|
||||||
|
# uint64 is default (TODO this is changing soon)
|
||||||
|
return value
|
||||||
|
elif size == 16:
|
||||||
|
return spec_ssz.uint128(value)
|
||||||
|
elif size == 32:
|
||||||
|
return spec_ssz.uint256(value)
|
||||||
|
else:
|
||||||
|
raise TypeError("invalid uint size")
|
||||||
|
elif spec_ssz.is_list_type(typ):
|
||||||
|
elem_typ = spec_ssz.read_elem_type(typ)
|
||||||
|
return [translate_value(elem, elem_typ) for elem in value]
|
||||||
|
elif spec_ssz.is_bool_type(typ):
|
||||||
|
return value
|
||||||
|
elif spec_ssz.is_vector_type(typ):
|
||||||
|
elem_typ = spec_ssz.read_elem_type(typ)
|
||||||
|
return typ(*(translate_value(elem, elem_typ) for elem in value))
|
||||||
|
elif spec_ssz.is_bytesn_type(typ):
|
||||||
|
return typ(value)
|
||||||
|
elif spec_ssz.is_bytes_type(typ):
|
||||||
|
return value
|
||||||
|
elif spec_ssz.is_container_type(typ):
|
||||||
|
return typ(**{f_name: translate_value(f_val, f_typ) for (f_name, f_val, f_typ)
|
||||||
|
in zip(typ.get_field_names(), value, typ.get_field_types())})
|
||||||
|
else:
|
||||||
|
raise TypeError("Type not supported: {}".format(typ))
|
|
@ -0,0 +1,33 @@
|
||||||
|
from eth2spec.fuzzing.decoder import translate_typ, translate_value
|
||||||
|
from eth2spec.phase0 import spec
|
||||||
|
from eth2spec.utils.ssz import ssz_impl as spec_ssz_impl
|
||||||
|
from random import Random
|
||||||
|
from eth2spec.debug import random_value
|
||||||
|
|
||||||
|
|
||||||
|
def test_decoder():
|
||||||
|
rng = Random(123)
|
||||||
|
|
||||||
|
# check these types only, Block covers a lot of operation types already.
|
||||||
|
for typ in [spec.BeaconBlock, spec.BeaconState, spec.IndexedAttestation, spec.AttestationDataAndCustodyBit]:
|
||||||
|
# create a random pyspec value
|
||||||
|
original = random_value.get_random_ssz_object(rng, typ, 100, 10,
|
||||||
|
mode=random_value.RandomizationMode.mode_random,
|
||||||
|
chaos=True)
|
||||||
|
# serialize it, using pyspec
|
||||||
|
pyspec_data = spec_ssz_impl.serialize(original)
|
||||||
|
# get the py-ssz type for it
|
||||||
|
block_sedes = translate_typ(typ)
|
||||||
|
# try decoding using the py-ssz type
|
||||||
|
raw_value = block_sedes.deserialize(pyspec_data)
|
||||||
|
|
||||||
|
# serialize it using py-ssz
|
||||||
|
pyssz_data = block_sedes.serialize(raw_value)
|
||||||
|
# now check if the serialized form is equal. If so, we confirmed decoding and encoding to work.
|
||||||
|
assert pyspec_data == pyssz_data
|
||||||
|
|
||||||
|
# now translate the py-ssz value in a pyspec-value
|
||||||
|
block = translate_value(raw_value, typ)
|
||||||
|
|
||||||
|
# and see if the hash-tree-root of the original matches the hash-tree-root of the decoded & translated value.
|
||||||
|
assert spec_ssz_impl.hash_tree_root(original) == spec_ssz_impl.hash_tree_root(block)
|
|
@ -3,3 +3,4 @@ eth-typing>=2.1.0,<3.0.0
|
||||||
pycryptodome==3.7.3
|
pycryptodome==3.7.3
|
||||||
py_ecc>=1.6.0
|
py_ecc>=1.6.0
|
||||||
typing_inspect==0.4.0
|
typing_inspect==0.4.0
|
||||||
|
ssz==0.1.0a10
|
||||||
|
|
|
@ -9,6 +9,7 @@ setup(
|
||||||
"eth-typing>=2.1.0,<3.0.0",
|
"eth-typing>=2.1.0,<3.0.0",
|
||||||
"pycryptodome==3.7.3",
|
"pycryptodome==3.7.3",
|
||||||
"py_ecc>=1.6.0",
|
"py_ecc>=1.6.0",
|
||||||
"typing_inspect==0.4.0"
|
"typing_inspect==0.4.0",
|
||||||
|
"ssz==0.1.0a10"
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue