From 7c0cc7f801259edfad890122ae61e5859548e923 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 13 Jun 2019 14:32:45 -0600 Subject: [PATCH 01/14] fix #1169 bytes type error --- test_libs/pyspec/eth2spec/test/sanity/test_blocks.py | 2 ++ test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py | 3 +++ 2 files changed, 5 insertions(+) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 2275161c8..1abda292a 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -33,6 +33,7 @@ def test_empty_block_transition(spec, state): assert len(state.eth1_data_votes) == pre_eth1_votes + 1 assert spec.get_block_root_at_slot(state, pre_slot) == block.parent_root + assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH @with_all_phases @@ -51,6 +52,7 @@ def test_skipped_slots(spec, state): yield 'post', state assert state.slot == block.slot + assert spec.get_randao_mix(state, spec.get_current_epoch(state)) != spec.ZERO_HASH for slot in range(pre_slot, state.slot): assert spec.get_block_root_at_slot(state, slot) == block.parent_root diff --git a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py index 55ced4ee2..bb98fb084 100644 --- a/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py +++ b/test_libs/pyspec/eth2spec/utils/ssz/ssz_typing.py @@ -1,3 +1,4 @@ +from types import GeneratorType from typing import List, Iterable, TypeVar, Type, NewType from typing import Union from typing_inspect import get_origin @@ -356,6 +357,8 @@ def parse_bytes(val): return val elif isinstance(val, int): return bytes([val]) + elif isinstance(val, (list, GeneratorType)): + return bytes(val) else: return None From 4a08abfa1d7cb51271b5399dfca68d1c4dc038a4 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 13 Jun 2019 14:47:53 -0600 Subject: [PATCH 02/14] ensure sanity tests run with bls --- test_libs/pyspec/eth2spec/test/sanity/test_blocks.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 2275161c8..f877d81d6 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -13,11 +13,10 @@ from eth2spec.test.helpers.proposer_slashings import get_valid_proposer_slashing from eth2spec.test.helpers.attestations import get_valid_attestation from eth2spec.test.helpers.deposits import prepare_state_and_deposit -from eth2spec.test.context import spec_state_test, never_bls, with_all_phases +from eth2spec.test.context import spec_state_test, with_all_phases @with_all_phases -@never_bls @spec_state_test def test_empty_block_transition(spec, state): pre_slot = state.slot @@ -36,7 +35,6 @@ def test_empty_block_transition(spec, state): @with_all_phases -@never_bls @spec_state_test def test_skipped_slots(spec, state): pre_slot = state.slot From e7bb9bf19bd387842158e8e852bd1df7d2f11e8f Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 14 Jun 2019 01:56:36 +0200 Subject: [PATCH 03/14] SSZ decoding through pyssz, with translation of types/values --- test_libs/fuzzing/decoder.py | 84 ++++++++++++++++++++++++++++++ test_libs/fuzzing/requirements.txt | 3 ++ 2 files changed, 87 insertions(+) create mode 100644 test_libs/fuzzing/decoder.py create mode 100644 test_libs/fuzzing/requirements.txt diff --git a/test_libs/fuzzing/decoder.py b/test_libs/fuzzing/decoder.py new file mode 100644 index 000000000..16d798f6f --- /dev/null +++ b/test_libs/fuzzing/decoder.py @@ -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( + [(field_name, 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 Exception("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 False + 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.values(), typ.get_field_types())}) + else: + raise Exception("Type not supported: {}".format(typ)) diff --git a/test_libs/fuzzing/requirements.txt b/test_libs/fuzzing/requirements.txt new file mode 100644 index 000000000..98dcd9564 --- /dev/null +++ b/test_libs/fuzzing/requirements.txt @@ -0,0 +1,3 @@ +../../test_libs/pyspec +../../test_libs/config_helpers +ssz==0.1.0a8 From c391017a0557bcb6f3060619fac48d7798b036eb Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 13 Jun 2019 17:57:29 -0600 Subject: [PATCH 04/14] address #1146 by inserting state root and re-signing blocks in tests --- .../pyspec/eth2spec/test/helpers/state.py | 13 ++++ .../test_process_crosslinks.py | 8 ++- .../test_process_registry_updates.py | 5 +- .../eth2spec/test/sanity/test_blocks.py | 62 +++++++++++-------- .../pyspec/eth2spec/test/test_finality.py | 4 +- 5 files changed, 58 insertions(+), 34 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/helpers/state.py b/test_libs/pyspec/eth2spec/test/helpers/state.py index 63aa27d70..8641d4c0d 100644 --- a/test_libs/pyspec/eth2spec/test/helpers/state.py +++ b/test_libs/pyspec/eth2spec/test/helpers/state.py @@ -1,3 +1,6 @@ +from eth2spec.test.helpers.block import sign_block + + def get_balance(state, index): return state.balances[index] @@ -23,3 +26,13 @@ def get_state_root(spec, state, slot) -> bytes: """ assert slot < state.slot <= slot + spec.SLOTS_PER_HISTORICAL_ROOT return state.latest_state_roots[slot % spec.SLOTS_PER_HISTORICAL_ROOT] + + +def state_transition_and_sign_block(spec, state, block): + """ + State transition via the provided ``block`` + then package the block with the state root and signature. + """ + spec.state_transition(state, block) + block.state_root = state.hash_tree_root() + sign_block(spec, state, block) diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py index 65d958678..d51191efb 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_crosslinks.py @@ -3,7 +3,8 @@ from copy import deepcopy from eth2spec.test.context import spec_state_test, with_all_phases from eth2spec.test.helpers.state import ( next_epoch, - next_slot + next_slot, + state_transition_and_sign_block, ) from eth2spec.test.helpers.block import apply_empty_block, sign_block from eth2spec.test.helpers.attestations import ( @@ -27,11 +28,14 @@ def run_process_crosslinks(spec, state, valid=True): block = build_empty_block_for_next_slot(spec, state) block.slot = slot sign_block(spec, state, block) - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) # cache state before epoch transition spec.process_slot(state) + # process components of epoch transition before processing crosslinks + spec.process_justification_and_finalization(state) + yield 'pre', state spec.process_crosslinks(state) yield 'post', state diff --git a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py index e6679f844..4f6d700b7 100644 --- a/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py +++ b/test_libs/pyspec/eth2spec/test/phase_0/epoch_processing/test_process_registry_updates.py @@ -1,6 +1,5 @@ -from eth2spec.phase0.spec import state_transition from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block -from eth2spec.test.helpers.state import next_epoch +from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block from eth2spec.test.context import spec_state_test, with_all_phases @@ -16,7 +15,7 @@ def run_process_registry_updates(spec, state, valid=True): block = build_empty_block_for_next_slot(spec, state) block.slot = slot sign_block(spec, state, block) - state_transition(state, block) + state_transition_and_sign_block(spec, state, block) # cache state before epoch transition spec.process_slot(state) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index 347c67a19..f333531e7 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -4,7 +4,7 @@ from typing import List from eth2spec.utils.ssz.ssz_impl import signing_root from eth2spec.utils.bls import bls_sign -from eth2spec.test.helpers.state import get_balance +from eth2spec.test.helpers.state import get_balance, state_transition_and_sign_block # from eth2spec.test.helpers.transfers import get_valid_transfer from eth2spec.test.helpers.block import build_empty_block_for_next_slot, sign_block from eth2spec.test.helpers.keys import privkeys, pubkeys @@ -16,6 +16,7 @@ from eth2spec.test.helpers.deposits import prepare_state_and_deposit from eth2spec.test.context import spec_state_test, with_all_phases + @with_all_phases @spec_state_test def test_empty_block_transition(spec, state): @@ -25,9 +26,10 @@ def test_empty_block_transition(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state, signed=True) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert len(state.eth1_data_votes) == pre_eth1_votes + 1 @@ -44,9 +46,10 @@ def test_skipped_slots(spec, state): block = build_empty_block_for_next_slot(spec, state) block.slot += 3 sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert state.slot == block.slot @@ -64,9 +67,10 @@ def test_empty_epoch_transition(spec, state): block = build_empty_block_for_next_slot(spec, state) block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert state.slot == block.slot @@ -84,9 +88,10 @@ def test_empty_epoch_transition(spec, state): # block = build_empty_block_for_next_slot(spec, state) # block.slot += spec.SLOTS_PER_EPOCH * 5 # sign_block(spec, state, block, proposer_index=0) -# yield 'blocks', [block], List[spec.BeaconBlock] -# spec.state_transition(state, block) +# state_transition_and_sign_block(spec, state, block) + +# yield 'blocks', [block], List[spec.BeaconBlock] # yield 'post', state # assert state.slot == block.slot @@ -113,9 +118,10 @@ def test_proposer_slashing(spec, state): block = build_empty_block_for_next_slot(spec, state) block.body.proposer_slashings.append(proposer_slashing) sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state # check if slashed @@ -147,9 +153,10 @@ def test_attester_slashing(spec, state): block = build_empty_block_for_next_slot(spec, state) block.body.attester_slashings.append(attester_slashing) sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) + + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state slashed_validator = state.validator_registry[validator_index] @@ -185,9 +192,9 @@ def test_deposit_in_block(spec, state): block.body.deposits.append(deposit) sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] + state_transition_and_sign_block(spec, state, block) - spec.state_transition(state, block) + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert len(state.validator_registry) == initial_registry_len + 1 @@ -213,9 +220,9 @@ def test_deposit_top_up(spec, state): block.body.deposits.append(deposit) sign_block(spec, state, block) - yield 'blocks', [block], List[spec.BeaconBlock] + state_transition_and_sign_block(spec, state, block) - spec.state_transition(state, block) + yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state assert len(state.validator_registry) == initial_registry_len @@ -238,7 +245,7 @@ def test_attestation(spec, state): attestation_block.slot += spec.MIN_ATTESTATION_INCLUSION_DELAY attestation_block.body.attestations.append(attestation) sign_block(spec, state, attestation_block) - spec.state_transition(state, attestation_block) + state_transition_and_sign_block(spec, state, attestation_block) assert len(state.current_epoch_attestations) == pre_current_attestations_len + 1 @@ -248,7 +255,8 @@ def test_attestation(spec, state): epoch_block = build_empty_block_for_next_slot(spec, state) epoch_block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, epoch_block) - spec.state_transition(state, epoch_block) + state_transition_and_sign_block(spec, state, epoch_block) + yield 'blocks', [attestation_block, epoch_block], List[spec.BeaconBlock] yield 'post', state @@ -287,7 +295,7 @@ def test_voluntary_exit(spec, state): initiate_exit_block = build_empty_block_for_next_slot(spec, state) initiate_exit_block.body.voluntary_exits.append(voluntary_exit) sign_block(spec, state, initiate_exit_block) - spec.state_transition(state, initiate_exit_block) + state_transition_and_sign_block(spec, state, initiate_exit_block) assert state.validator_registry[validator_index].exit_epoch < spec.FAR_FUTURE_EPOCH @@ -295,7 +303,7 @@ def test_voluntary_exit(spec, state): exit_block = build_empty_block_for_next_slot(spec, state) exit_block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, exit_block) - spec.state_transition(state, exit_block) + state_transition_and_sign_block(spec, state, exit_block) yield 'blocks', [initiate_exit_block, exit_block], List[spec.BeaconBlock] yield 'post', state @@ -326,9 +334,9 @@ def test_voluntary_exit(spec, state): # block.body.transfers.append(transfer) # sign_block(spec, state, block) - # yield 'blocks', [block], List[spec.BeaconBlock] + # state_transition_and_sign_block(spec, state, block) - # spec.state_transition(state, block) + # yield 'blocks', [block], List[spec.BeaconBlock] # yield 'post', state # sender_balance = get_balance(state, sender_index) @@ -354,7 +362,7 @@ def test_balance_driven_status_transitions(spec, state): block = build_empty_block_for_next_slot(spec, state) block.slot += spec.SLOTS_PER_EPOCH sign_block(spec, state, block) - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state @@ -371,7 +379,7 @@ def test_historical_batch(spec, state): yield 'pre', state block = build_empty_block_for_next_slot(spec, state, signed=True) - spec.state_transition(state, block) + state_transition_and_sign_block(spec, state, block) yield 'blocks', [block], List[spec.BeaconBlock] yield 'post', state @@ -392,7 +400,7 @@ def test_historical_batch(spec, state): # blocks = [] # for _ in range(spec.SLOTS_PER_ETH1_VOTING_PERIOD - 1): # block = build_empty_block_for_next_slot(spec, state) -# spec.state_transition(state, block) +# state_transition_and_sign_block(spec, state, block) # expected_votes += 1 # assert len(state.eth1_data_votes) == expected_votes # blocks.append(block) @@ -400,7 +408,7 @@ def test_historical_batch(spec, state): # block = build_empty_block_for_next_slot(spec, state) # blocks.append(block) -# spec.state_transition(state, block) +# state_transition_and_sign_block(spec, state, block) # yield 'blocks', [block], List[spec.BeaconBlock] # yield 'post', state diff --git a/test_libs/pyspec/eth2spec/test/test_finality.py b/test_libs/pyspec/eth2spec/test/test_finality.py index 801e8b4fd..5e81f52c8 100644 --- a/test_libs/pyspec/eth2spec/test/test_finality.py +++ b/test_libs/pyspec/eth2spec/test/test_finality.py @@ -2,7 +2,7 @@ from copy import deepcopy from typing import List from eth2spec.test.context import spec_state_test, never_bls, with_all_phases -from eth2spec.test.helpers.state import next_epoch +from eth2spec.test.helpers.state import next_epoch, state_transition_and_sign_block from eth2spec.test.helpers.block import build_empty_block_for_next_slot, apply_empty_block from eth2spec.test.helpers.attestations import get_valid_attestation @@ -54,7 +54,7 @@ def next_epoch_with_attestations(spec, prev_attestation = get_valid_attestation(spec, post_state, slot_to_attest) block.body.attestations.append(prev_attestation) - spec.state_transition(post_state, block) + state_transition_and_sign_block(spec, post_state, block) blocks.append(block) return state, blocks, post_state From f834f727fa210c442231f6aea6542a08e0e1e669 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Thu, 13 Jun 2019 18:03:20 -0600 Subject: [PATCH 05/14] lint --- test_libs/pyspec/eth2spec/test/sanity/test_blocks.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py index f333531e7..e19fbc97c 100644 --- a/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py +++ b/test_libs/pyspec/eth2spec/test/sanity/test_blocks.py @@ -16,7 +16,6 @@ from eth2spec.test.helpers.deposits import prepare_state_and_deposit from eth2spec.test.context import spec_state_test, with_all_phases - @with_all_phases @spec_state_test def test_empty_block_transition(spec, state): @@ -257,7 +256,6 @@ def test_attestation(spec, state): sign_block(spec, state, epoch_block) state_transition_and_sign_block(spec, state, epoch_block) - yield 'blocks', [attestation_block, epoch_block], List[spec.BeaconBlock] yield 'post', state From aed5db033a8c52f0263da7f0671c0c172d62d6e9 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Fri, 14 Jun 2019 10:15:54 -0600 Subject: [PATCH 06/14] enforce byte length for g2 values in test generators --- test_generators/bls/main.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/test_generators/bls/main.py b/test_generators/bls/main.py index ef80635de..8a6a7dafe 100644 --- a/test_generators/bls/main.py +++ b/test_generators/bls/main.py @@ -12,8 +12,15 @@ from gen_base import gen_runner, gen_suite, gen_typing from py_ecc import bls -def int_to_hex(n: int) -> str: - return '0x' + int_to_big_endian(n).hex() +F2Q_COEFF_LEN = 48 +G2_COMPRESSED_Z_LEN = 48 + + +def int_to_hex(n: int, byte_length: int=None) -> str: + byte_value = int_to_big_endian(n) + if byte_length: + byte_value = byte_value.rjust(byte_length, b'\x00') + return '0x' + byte_value.hex() def hex_to_int(x: str) -> int: @@ -58,8 +65,8 @@ def hash_message(msg: bytes, """ return [ [ - int_to_hex(fq2.coeffs[0]), - int_to_hex(fq2.coeffs[1]), + int_to_hex(fq2.coeffs[0], F2Q_COEFF_LEN), + int_to_hex(fq2.coeffs[1], F2Q_COEFF_LEN), ] for fq2 in bls.utils.hash_to_G2(msg, domain) ] @@ -75,8 +82,7 @@ def hash_message_compressed(msg: bytes, domain: int) -> Tuple[str, str]: - Message hash as a compressed G2 point """ z1, z2 = bls.utils.compress_G2(bls.utils.hash_to_G2(msg, domain)) - return [int_to_hex(z1), int_to_hex(z2)] - + return [int_to_hex(z1, G2_COMPRESSED_Z_LEN), int_to_hex(z2, G2_COMPRESSED_Z_LEN)] @to_tuple From 7b0ffc1ace8eefb61d459cddff94969a9601df52 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 14 Jun 2019 19:09:49 +0200 Subject: [PATCH 07/14] move decoder for fuzzing, minor fixes, update dependency to support SOS style offsets --- test_libs/fuzzing/requirements.txt | 3 --- test_libs/pyspec/eth2spec/fuzzing/__init__.py | 0 test_libs/{ => pyspec/eth2spec}/fuzzing/decoder.py | 8 ++++---- test_libs/pyspec/requirements.txt | 1 + test_libs/pyspec/setup.py | 3 ++- 5 files changed, 7 insertions(+), 8 deletions(-) delete mode 100644 test_libs/fuzzing/requirements.txt create mode 100644 test_libs/pyspec/eth2spec/fuzzing/__init__.py rename test_libs/{ => pyspec/eth2spec}/fuzzing/decoder.py (89%) diff --git a/test_libs/fuzzing/requirements.txt b/test_libs/fuzzing/requirements.txt deleted file mode 100644 index 98dcd9564..000000000 --- a/test_libs/fuzzing/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -../../test_libs/pyspec -../../test_libs/config_helpers -ssz==0.1.0a8 diff --git a/test_libs/pyspec/eth2spec/fuzzing/__init__.py b/test_libs/pyspec/eth2spec/fuzzing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/test_libs/fuzzing/decoder.py b/test_libs/pyspec/eth2spec/fuzzing/decoder.py similarity index 89% rename from test_libs/fuzzing/decoder.py rename to test_libs/pyspec/eth2spec/fuzzing/decoder.py index 16d798f6f..bb8aceb61 100644 --- a/test_libs/fuzzing/decoder.py +++ b/test_libs/pyspec/eth2spec/fuzzing/decoder.py @@ -10,7 +10,7 @@ def translate_typ(typ) -> ssz.BaseSedes: """ if spec_ssz.is_container_type(typ): return ssz.Container( - [(field_name, translate_typ(field_typ)) for (field_name, field_typ) in typ.get_fields()]) + [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): @@ -38,7 +38,7 @@ def translate_typ(typ) -> ssz.BaseSedes: else: raise TypeError("invalid uint size") else: - raise Exception("Type not supported: {}".format(typ)) + raise TypeError("Type not supported: {}".format(typ)) def translate_value(value, typ): @@ -79,6 +79,6 @@ def translate_value(value, 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.values(), typ.get_field_types())}) + in zip(typ.get_field_names(), value, typ.get_field_types())}) else: - raise Exception("Type not supported: {}".format(typ)) + raise TypeError("Type not supported: {}".format(typ)) diff --git a/test_libs/pyspec/requirements.txt b/test_libs/pyspec/requirements.txt index 3b38930bd..eed0d5a7d 100644 --- a/test_libs/pyspec/requirements.txt +++ b/test_libs/pyspec/requirements.txt @@ -3,3 +3,4 @@ eth-typing>=2.1.0,<3.0.0 pycryptodome==3.7.3 py_ecc>=1.6.0 typing_inspect==0.4.0 +ssz==0.1.0a9 diff --git a/test_libs/pyspec/setup.py b/test_libs/pyspec/setup.py index e99b911ee..49f3c84a1 100644 --- a/test_libs/pyspec/setup.py +++ b/test_libs/pyspec/setup.py @@ -9,6 +9,7 @@ setup( "eth-typing>=2.1.0,<3.0.0", "pycryptodome==3.7.3", "py_ecc>=1.6.0", - "typing_inspect==0.4.0" + "typing_inspect==0.4.0", + "ssz==0.1.0a9" ] ) From 01be8b7e656de4bfce86136dac79774eefeea3e5 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 14 Jun 2019 19:16:15 +0200 Subject: [PATCH 08/14] minor fix --- test_libs/pyspec/eth2spec/fuzzing/decoder.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test_libs/pyspec/eth2spec/fuzzing/decoder.py b/test_libs/pyspec/eth2spec/fuzzing/decoder.py index bb8aceb61..5722ed297 100644 --- a/test_libs/pyspec/eth2spec/fuzzing/decoder.py +++ b/test_libs/pyspec/eth2spec/fuzzing/decoder.py @@ -69,7 +69,7 @@ def translate_value(value, 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 False + 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) From 895ab67815866c2a3f78022fc8dc6ad575f4b1fc Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 14 Jun 2019 20:41:08 +0200 Subject: [PATCH 09/14] fix decoder, also fix bug in pyssz, see PR 74 --- test_libs/pyspec/eth2spec/fuzzing/decoder.py | 2 +- .../pyspec/eth2spec/fuzzing/test_decoder.py | 39 +++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 test_libs/pyspec/eth2spec/fuzzing/test_decoder.py diff --git a/test_libs/pyspec/eth2spec/fuzzing/decoder.py b/test_libs/pyspec/eth2spec/fuzzing/decoder.py index 5722ed297..a5d3dfd97 100644 --- a/test_libs/pyspec/eth2spec/fuzzing/decoder.py +++ b/test_libs/pyspec/eth2spec/fuzzing/decoder.py @@ -72,7 +72,7 @@ def translate_value(value, 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) + 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): diff --git a/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py b/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py new file mode 100644 index 000000000..3747b3535 --- /dev/null +++ b/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py @@ -0,0 +1,39 @@ +from eth2spec.fuzzing.decoder import translate_typ, translate_value +from eth2spec.phase0 import spec +from preset_loader import loader +from eth2spec.utils.ssz import ssz_impl as spec_ssz_impl +from random import Random +from eth2spec.debug import random_value + + +def test_decoder(): + configs_path = "../../../../configs/" + config_name = "minimal" + presets = loader.load_presets(configs_path, config_name) + spec.apply_constants_preset(presets) + + 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) From d4bf55e5a10f505f6e767e2864d9632f805c5763 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 14 Jun 2019 21:24:24 +0200 Subject: [PATCH 10/14] update pyssz to include deserialization bugfix --- test_libs/pyspec/requirements.txt | 2 +- test_libs/pyspec/setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test_libs/pyspec/requirements.txt b/test_libs/pyspec/requirements.txt index eed0d5a7d..2de2aa84b 100644 --- a/test_libs/pyspec/requirements.txt +++ b/test_libs/pyspec/requirements.txt @@ -3,4 +3,4 @@ eth-typing>=2.1.0,<3.0.0 pycryptodome==3.7.3 py_ecc>=1.6.0 typing_inspect==0.4.0 -ssz==0.1.0a9 +ssz==0.1.0a10 diff --git a/test_libs/pyspec/setup.py b/test_libs/pyspec/setup.py index 49f3c84a1..3856640ab 100644 --- a/test_libs/pyspec/setup.py +++ b/test_libs/pyspec/setup.py @@ -10,6 +10,6 @@ setup( "pycryptodome==3.7.3", "py_ecc>=1.6.0", "typing_inspect==0.4.0", - "ssz==0.1.0a9" + "ssz==0.1.0a10" ] ) From 367586d888ae5bc901a81588227926971b40585f Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 14 Jun 2019 21:31:33 +0200 Subject: [PATCH 11/14] remove need for presets loading, just test mainnet, not too many/large objects anyway --- test_libs/pyspec/eth2spec/fuzzing/test_decoder.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py b/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py index 3747b3535..26ee6e913 100644 --- a/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py +++ b/test_libs/pyspec/eth2spec/fuzzing/test_decoder.py @@ -1,17 +1,11 @@ from eth2spec.fuzzing.decoder import translate_typ, translate_value from eth2spec.phase0 import spec -from preset_loader import loader from eth2spec.utils.ssz import ssz_impl as spec_ssz_impl from random import Random from eth2spec.debug import random_value def test_decoder(): - configs_path = "../../../../configs/" - config_name = "minimal" - presets = loader.load_presets(configs_path, config_name) - spec.apply_constants_preset(presets) - rng = Random(123) # check these types only, Block covers a lot of operation types already. From 1c51982c6b5de8fd9dfb851cb85086fa83205d52 Mon Sep 17 00:00:00 2001 From: protolambda Date: Fri, 14 Jun 2019 23:12:04 +0200 Subject: [PATCH 12/14] generate coverage reports in make test, open as html site --- .gitignore | 7 +++++++ Makefile | 14 ++++++++++---- test_libs/pyspec/requirements-testing.txt | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index c6b39955f..16d39a434 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,10 @@ eth2.0-spec-tests/ # Dynamically built from Markdown spec test_libs/pyspec/eth2spec/phase0/spec.py test_libs/pyspec/eth2spec/phase1/spec.py + +# coverage reports +.htmlcov +.coverage + +# local CI testing output +test_libs/pyspec/test-reports diff --git a/Makefile b/Makefile index f79b89dad..9e658f35d 100644 --- a/Makefile +++ b/Makefile @@ -21,8 +21,10 @@ PY_SPEC_PHASE_1_DEPS = $(SPEC_DIR)/core/1_*.md PY_SPEC_ALL_TARGETS = $(PY_SPEC_PHASE_0_TARGETS) $(PY_SPEC_PHASE_1_TARGETS) +COV_INDEX_FILE=$(PY_SPEC_DIR)/.htmlcov/index.html -.PHONY: clean all test citest lint gen_yaml_tests pyspec phase0 phase1 install_test install_deposit_contract_test test_deposit_contract compile_deposit_contract +.PHONY: clean all test citest lint gen_yaml_tests pyspec phase0 phase1 install_test open_cov \ + install_deposit_contract_test test_deposit_contract compile_deposit_contract all: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_DIR) $(YAML_TEST_TARGETS) @@ -41,11 +43,15 @@ install_test: cd $(PY_SPEC_DIR); python3 -m venv venv; . venv/bin/activate; pip3 install -r requirements-testing.txt; test: $(PY_SPEC_ALL_TARGETS) - cd $(PY_SPEC_DIR); . venv/bin/activate; python -m pytest eth2spec + cd $(PY_SPEC_DIR); . venv/bin/activate; export PYTHONPATH="./"; \ + python -m pytest --cov=eth2spec.phase0.spec --cov=eth2spec.phase1.spec --cov-report="html:.htmlcov" --cov-branch eth2spec citest: $(PY_SPEC_ALL_TARGETS) - cd $(PY_SPEC_DIR); mkdir -p test-reports/eth2spec; . venv/bin/activate; \ - python -m pytest --junitxml=test-reports/eth2spec/test_results_phase0.xml eth2spec + cd $(PY_SPEC_DIR); mkdir -p test-reports/eth2spec; . venv/bin/activate; \ + python -m pytest --junitxml=test-reports/eth2spec/test_results.xml eth2spec + +open_cov: + ((open "$(COV_INDEX_FILE)" || xdg-open "$(COV_INDEX_FILE)") &> /dev/null) & lint: $(PY_SPEC_ALL_TARGETS) cd $(PY_SPEC_DIR); . venv/bin/activate; \ diff --git a/test_libs/pyspec/requirements-testing.txt b/test_libs/pyspec/requirements-testing.txt index 331d0fa28..edd141650 100644 --- a/test_libs/pyspec/requirements-testing.txt +++ b/test_libs/pyspec/requirements-testing.txt @@ -2,3 +2,4 @@ pytest>=3.6,<3.7 ../config_helpers flake8==3.7.7 +pytest-cov From 20aa539f4dc3903a7f73fac2a4d26e257b34ee26 Mon Sep 17 00:00:00 2001 From: protolambda Date: Sat, 15 Jun 2019 01:13:29 +0200 Subject: [PATCH 13/14] update clean command --- Makefile | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 9e658f35d..1b7155625 100644 --- a/Makefile +++ b/Makefile @@ -21,7 +21,8 @@ PY_SPEC_PHASE_1_DEPS = $(SPEC_DIR)/core/1_*.md PY_SPEC_ALL_TARGETS = $(PY_SPEC_PHASE_0_TARGETS) $(PY_SPEC_PHASE_1_TARGETS) -COV_INDEX_FILE=$(PY_SPEC_DIR)/.htmlcov/index.html +COV_HTML_OUT=.htmlcov +COV_INDEX_FILE=$(PY_SPEC_DIR)/$(COV_HTML_OUT)/index.html .PHONY: clean all test citest lint gen_yaml_tests pyspec phase0 phase1 install_test open_cov \ install_deposit_contract_test test_deposit_contract compile_deposit_contract @@ -34,6 +35,9 @@ clean: rm -rf $(PY_SPEC_DIR)/venv $(PY_SPEC_DIR)/.pytest_cache rm -rf $(PY_SPEC_ALL_TARGETS) rm -rf $(DEPOSIT_CONTRACT_DIR)/venv $(DEPOSIT_CONTRACT_DIR)/.pytest_cache + rm -rf $(PY_SPEC_DIR)/$(COV_HTML_OUT) + rm -rf $(PY_SPEC_DIR)/.coverage + rm -rf $(PY_SPEC_DIR)/test-reports # "make gen_yaml_tests" to run generators gen_yaml_tests: $(PY_SPEC_ALL_TARGETS) $(YAML_TEST_TARGETS) @@ -44,7 +48,7 @@ install_test: test: $(PY_SPEC_ALL_TARGETS) cd $(PY_SPEC_DIR); . venv/bin/activate; export PYTHONPATH="./"; \ - python -m pytest --cov=eth2spec.phase0.spec --cov=eth2spec.phase1.spec --cov-report="html:.htmlcov" --cov-branch eth2spec + python -m pytest --cov=eth2spec.phase0.spec --cov=eth2spec.phase1.spec --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec -k transfer citest: $(PY_SPEC_ALL_TARGETS) cd $(PY_SPEC_DIR); mkdir -p test-reports/eth2spec; . venv/bin/activate; \ From 2f9c554bf7370319bfc0d61feaaf2a34338583b9 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Sun, 16 Jun 2019 17:38:48 -0600 Subject: [PATCH 14/14] minor fix to makefile, add codecov instructiosn to readme --- Makefile | 2 +- test_libs/pyspec/README.md | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 1b7155625..4ee757f88 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ install_test: test: $(PY_SPEC_ALL_TARGETS) cd $(PY_SPEC_DIR); . venv/bin/activate; export PYTHONPATH="./"; \ - python -m pytest --cov=eth2spec.phase0.spec --cov=eth2spec.phase1.spec --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec -k transfer + python -m pytest --cov=eth2spec.phase0.spec --cov=eth2spec.phase1.spec --cov-report="html:$(COV_HTML_OUT)" --cov-branch eth2spec citest: $(PY_SPEC_ALL_TARGETS) cd $(PY_SPEC_DIR); mkdir -p test-reports/eth2spec; . venv/bin/activate; \ diff --git a/test_libs/pyspec/README.md b/test_libs/pyspec/README.md index 2c2226ee7..53750517d 100644 --- a/test_libs/pyspec/README.md +++ b/test_libs/pyspec/README.md @@ -28,7 +28,7 @@ These tests are sanity tests, to verify if the spec itself is consistent. #### Automated -Run `make test` from the root of the specs repository. +Run `make test` from the root of the specs repository (after running `make install_test` if have not before). #### Manual @@ -50,6 +50,10 @@ pytest --config=minimal eth2spec ``` Note the package-name, this is to locate the tests. +### How to view code coverage report + +Run `make open_cov` from the root of the specs repository after running `make test` to open the html code coverage report. + ## Contributing