From b85afcab7a36b0d51ae9625192d6a0cd301d4868 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Thu, 11 Jun 2020 07:28:26 -0700 Subject: [PATCH 01/28] Added fix in store_target_checkpoint_state --- specs/phase0/fork-choice.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index b9d8ecd3c..614c21cc6 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -301,7 +301,8 @@ def store_target_checkpoint_state(store: Store, target: Checkpoint) -> None: # Store target checkpoint state if not yet seen if target not in store.checkpoint_states: base_state = store.block_states[target.root].copy() - process_slots(base_state, compute_start_slot_at_epoch(target.epoch)) + if base_state.slot < compute_start_slot_at_epoch(target.epoch): + process_slots(base_state, compute_start_slot_at_epoch(target.epoch)) store.checkpoint_states[target] = base_state ``` From 993ed5c2c6474af552326582153bebc2f210080d Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Fri, 12 Jun 2020 14:37:07 -0700 Subject: [PATCH 02/28] Resetting branch to dev and adding single commit --- specs/phase0/fork-choice.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/specs/phase0/fork-choice.md b/specs/phase0/fork-choice.md index 132477ee8..02143d0a4 100644 --- a/specs/phase0/fork-choice.md +++ b/specs/phase0/fork-choice.md @@ -301,7 +301,8 @@ def store_target_checkpoint_state(store: Store, target: Checkpoint) -> None: # Store target checkpoint state if not yet seen if target not in store.checkpoint_states: base_state = store.block_states[target.root].copy() - process_slots(base_state, compute_start_slot_at_epoch(target.epoch)) + if base_state.slot < compute_start_slot_at_epoch(target.epoch): + process_slots(base_state, compute_start_slot_at_epoch(target.epoch)) store.checkpoint_states[target] = base_state ``` From ea591931570a67f27647651635d8d291105026e9 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 18:11:28 +0800 Subject: [PATCH 03/28] Rename some `shard_blocks` to `shard_block_dict` --- .../eth2spec/test/fork_choice/test_on_shard_head.py | 2 +- .../core/pyspec/eth2spec/test/helpers/shard_block.py | 10 +++++++--- .../test_process_shard_transition.py | 4 ++-- .../eth2spec/test/phase1/sanity/test_blocks.py | 12 ++++++------ 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py index c84f073b2..f7b888a2e 100644 --- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py +++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py @@ -72,7 +72,7 @@ def apply_shard_and_beacon(spec, state, store, shard_store, shard_blocks_buffer) shard_transitions = get_shard_transitions( spec, state, - shard_blocks={shard: shard_blocks_buffer}, + shard_block_dict={shard: shard_blocks_buffer}, ) shard_transition = shard_transitions[shard] attestation = get_valid_on_time_attestation( diff --git a/tests/core/pyspec/eth2spec/test/helpers/shard_block.py b/tests/core/pyspec/eth2spec/test/helpers/shard_block.py index 6957f2283..1193f05e5 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/shard_block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/shard_block.py @@ -30,7 +30,7 @@ def build_shard_block(spec, slot = shard_parent_state.slot + 1 if body is None: - body = b'\x56' * 128 + body = get_sample_shard_block_body() beacon_state, beacon_parent_root = get_state_and_beacon_parent_root_at_slot(spec, beacon_state, slot) proposer_index = spec.get_shard_proposer_index(beacon_state, slot, shard) @@ -52,10 +52,10 @@ def build_shard_block(spec, return signed_block -def get_shard_transitions(spec, parent_beacon_state, shard_blocks): +def get_shard_transitions(spec, parent_beacon_state, shard_block_dict): shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS on_time_slot = parent_beacon_state.slot + 1 - for shard, blocks in shard_blocks.items(): + for shard, blocks in shard_block_dict.items(): shard_transition = spec.get_shard_transition(parent_beacon_state, shard, blocks) offset_slots = spec.compute_offset_slots( spec.get_latest_slot_for_shard(parent_beacon_state, shard), @@ -81,3 +81,7 @@ def get_committee_index_of_shard(spec, state, slot, shard): # Optional[Committe if (start_shard + committee_index) % active_shard_count == shard: return committee_index return None + + +def get_sample_shard_block_body(): + return b'\x56' * 128 diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index 6d8878177..0c9293b74 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -16,7 +16,7 @@ def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True): state = transition_to_valid_shard_slot(spec, state) committee_index = spec.CommitteeIndex(0) init_slot = state.slot - shard_slot = state.slot + target_len_offset_slot - 1 + shard_slot = init_slot + target_len_offset_slot - 1 shard = spec.compute_shard_from_committee_index(state, committee_index, shard_slot) assert state.shard_states[shard].slot == init_slot - 1 @@ -31,7 +31,7 @@ def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True): shard_transitions = get_shard_transitions( spec, state, - shard_blocks={shard: shard_blocks}, + shard_block_dict={shard: shard_blocks}, ) shard_transition = shard_transitions[shard] attestation = get_valid_on_time_attestation( diff --git a/tests/core/pyspec/eth2spec/test/phase1/sanity/test_blocks.py b/tests/core/pyspec/eth2spec/test/phase1/sanity/test_blocks.py index 33b0beac7..aa24bfc1e 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/sanity/test_blocks.py +++ b/tests/core/pyspec/eth2spec/test/phase1/sanity/test_blocks.py @@ -19,9 +19,9 @@ def run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, comm body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE shard_block = build_shard_block(spec, state, shard, body=body, slot=state.slot, signed=True) - shard_blocks: Dict[spec.Shard, Sequence[spec.SignedShardBlock]] = {shard: [shard_block]} + shard_block_dict: Dict[spec.Shard, Sequence[spec.SignedShardBlock]] = {shard: [shard_block]} - shard_transitions = get_shard_transitions(spec, state, shard_blocks) + shard_transitions = get_shard_transitions(spec, state, shard_block_dict) attestations = [ get_valid_on_time_attestation( spec, @@ -30,7 +30,7 @@ def run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, comm shard_transition=shard_transitions[shard], signed=True, ) - for shard in shard_blocks.keys() + for shard in shard_block_dict.keys() ] beacon_block = build_empty_block(spec, state, slot=state.slot + 1) @@ -50,16 +50,16 @@ def run_beacon_block_with_shard_blocks(spec, state, target_len_offset_slot, comm for shard in range(spec.get_active_shard_count(state)): post_shard_state = state.shard_states[shard] - if shard in shard_blocks: + if shard in shard_block_dict: # Shard state has been changed to state_transition result assert post_shard_state == shard_transitions[shard].shard_states[ len(shard_transitions[shard].shard_states) - 1 ] assert post_shard_state.slot == state.slot - 1 - if len(shard_blocks[shard]) == 0: + if len((shard_block_dict[shard])) == 0: # `latest_block_root` is the same assert post_shard_state.latest_block_root == pre_shard_states[shard].latest_block_root - if target_len_offset_slot == 1 and len(shard_blocks) > 0: + if target_len_offset_slot == 1 and len(shard_block_dict[shard]) > 0: assert post_shard_state.gasprice > pre_gasprice From 3117cf3140c400c706e2f5de082b5c13771dc58a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 19:09:11 +0800 Subject: [PATCH 04/28] Refactor --- .../eth2spec/test/helpers/shard_block.py | 7 +- .../test_process_shard_transition.py | 72 ++++++++++++------- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/helpers/shard_block.py b/tests/core/pyspec/eth2spec/test/helpers/shard_block.py index 1193f05e5..c8d60938b 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/shard_block.py +++ b/tests/core/pyspec/eth2spec/test/helpers/shard_block.py @@ -30,7 +30,7 @@ def build_shard_block(spec, slot = shard_parent_state.slot + 1 if body is None: - body = get_sample_shard_block_body() + body = get_sample_shard_block_body(spec) beacon_state, beacon_parent_root = get_state_and_beacon_parent_root_at_slot(spec, beacon_state, slot) proposer_index = spec.get_shard_proposer_index(beacon_state, slot, shard) @@ -83,5 +83,6 @@ def get_committee_index_of_shard(spec, state, slot, shard): # Optional[Committe return None -def get_sample_shard_block_body(): - return b'\x56' * 128 +def get_sample_shard_block_body(spec, is_max=False): + size = spec.MAX_SHARD_BLOCK_SIZE if is_max else 128 + return b'\x56' * size diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index 0c9293b74..afc4828f8 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -8,50 +8,63 @@ from eth2spec.test.helpers.shard_transitions import run_shard_transitions_proces from eth2spec.test.helpers.shard_block import ( build_shard_block, get_shard_transitions, + get_sample_shard_block_body, + get_committee_index_of_shard, ) from eth2spec.test.helpers.state import transition_to, transition_to_valid_shard_slot, next_slot -def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True): +def get_initial_env(spec, state, target_len_offset_slot): state = transition_to_valid_shard_slot(spec, state) committee_index = spec.CommitteeIndex(0) + target_shard_slot = state.slot + target_len_offset_slot - 1 + shard = spec.compute_shard_from_committee_index(state, committee_index, target_shard_slot) + return state, shard, target_shard_slot + + +def get_attestations_and_shard_transitions(spec, state, shard_block_dict): + shard_transitions = get_shard_transitions(spec, state, shard_block_dict) + attestations = [ + get_valid_on_time_attestation( + spec, state, + index=get_committee_index_of_shard(spec, state, state.slot, shard), + shard_transition=shard_transition, + signed=False, + ) + for shard, shard_transition in enumerate(shard_transitions) + if shard_transition != spec.ShardTransition() + ] + return attestations, shard_transitions + + +def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True): + state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot) init_slot = state.slot - shard_slot = init_slot + target_len_offset_slot - 1 - shard = spec.compute_shard_from_committee_index(state, committee_index, shard_slot) assert state.shard_states[shard].slot == init_slot - 1 - # Create SignedShardBlock - body = b'\x56' * spec.MAX_SHARD_BLOCK_SIZE - shard_block = build_shard_block(spec, state, shard, body=body, slot=state.slot, signed=True) - shard_blocks = [shard_block] + # Create SignedShardBlock at init_slot + shard_block = build_shard_block( + spec, state, shard, + slot=init_slot, body=get_sample_shard_block_body(spec, is_max=True), signed=True + ) + + # Transition state to target shard slot + transition_to(spec, state, target_shard_slot) + + # Create a shard_transitions that would be included at beacon block `target_shard_slot + 1` + shard_block_dict = {shard: [shard_block]} + attestations, shard_transitions = get_attestations_and_shard_transitions(spec, state, shard_block_dict) - # Transition state latest shard slot - transition_to(spec, state, shard_slot) - # Create a shard_transitions that would be included at beacon block `state.slot + target_len_offset_slot` - shard_transitions = get_shard_transitions( - spec, - state, - shard_block_dict={shard: shard_blocks}, - ) - shard_transition = shard_transitions[shard] - attestation = get_valid_on_time_attestation( - spec, - state, - index=committee_index, - shard_transition=shard_transition, - signed=False, - ) next_slot(spec, state) pre_gasprice = state.shard_states[shard].gasprice - transition_to(spec, state, init_slot + target_len_offset_slot) pre_shard_state = state.shard_states[shard] - yield from run_shard_transitions_processing(spec, state, shard_transitions, [attestation], valid=valid) + yield from run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=valid) if valid: shard_state = state.shard_states[shard] assert shard_state != pre_shard_state - assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1] + assert shard_state == shard_transitions[shard].shard_states[len(shard_transitions[shard].shard_states) - 1] assert shard_state.latest_block_root == shard_block.message.hash_tree_root() if target_len_offset_slot == 1: assert shard_state.gasprice > pre_gasprice @@ -69,3 +82,10 @@ def test_basic_crosslinks(spec, state): def test_multiple_offset_slots(spec, state): # NOTE: this test is only for full crosslink (minimal config), not for mainnet yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=2, valid=True) + + +@with_all_phases_except([PHASE0]) +@spec_state_test +def test_no_winning_root(spec, state): + # NOTE: this test is only for full crosslink (minimal config), not for mainnet + yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=1, valid=True) From c2c2b4c4441590da1e7c2286176d883f1578638a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 22:19:21 +0800 Subject: [PATCH 05/28] Crosslink bugfix 1. Fix `is_winning_attestation` condition 2. Fix `process_crosslink_for_shard`: we can only check `shard_data_roots` if `shard_transition != ShardTransition()` --- specs/phase1/beacon-chain.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index a1a940adf..aa250f9c4 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -671,7 +671,7 @@ def is_winning_attestation(state: BeaconState, ``winning_root`` formed by ``committee_index`` committee at the current slot. """ return ( - attestation.data.slot == state.slot + is_on_time_attestation(state, attestation) and attestation.data.index == committee_index and attestation.data.shard_transition_root == winning_root ) @@ -886,9 +886,10 @@ def process_crosslink_for_shard(state: BeaconState, for attestation in transition_attestations: participants = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) transition_participants = transition_participants.union(participants) - assert attestation.data.shard_head_root == shard_transition.shard_data_roots[ - len(shard_transition.shard_data_roots) - 1 - ] + if len(shard_transition.shard_data_roots) > 0: + # Is the `shard_transition` candidate + last_offset_index = len(shard_transition.shard_data_roots) - 1 + assert attestation.data.shard_head_root == shard_transition.shard_data_roots[last_offset_index] enough_online_stake = ( get_total_balance(state, online_indices.intersection(transition_participants)) * 3 >= From 43605eebe8f24a5509c78cac47547e3fb5c518b1 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 22:21:29 +0800 Subject: [PATCH 06/28] Add `test_no_winning_root` --- .../test_process_shard_transition.py | 73 ++++++++++++++++--- 1 file changed, 64 insertions(+), 9 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index afc4828f8..ead555098 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -3,7 +3,12 @@ from eth2spec.test.context import ( with_all_phases_except, spec_state_test, ) -from eth2spec.test.helpers.attestations import get_valid_on_time_attestation +from eth2spec.test.helpers.attestations import ( + get_valid_attestation, + get_valid_on_time_attestation, + run_attestation_processing, + sign_aggregate_attestation, +) from eth2spec.test.helpers.shard_transitions import run_shard_transitions_processing from eth2spec.test.helpers.shard_block import ( build_shard_block, @@ -19,6 +24,7 @@ def get_initial_env(spec, state, target_len_offset_slot): committee_index = spec.CommitteeIndex(0) target_shard_slot = state.slot + target_len_offset_slot - 1 shard = spec.compute_shard_from_committee_index(state, committee_index, target_shard_slot) + assert state.shard_states[shard].slot == state.slot - 1 return state, shard, target_shard_slot @@ -29,7 +35,7 @@ def get_attestations_and_shard_transitions(spec, state, shard_block_dict): spec, state, index=get_committee_index_of_shard(spec, state, state.slot, shard), shard_transition=shard_transition, - signed=False, + signed=True, ) for shard, shard_transition in enumerate(shard_transitions) if shard_transition != spec.ShardTransition() @@ -37,10 +43,9 @@ def get_attestations_and_shard_transitions(spec, state, shard_block_dict): return attestations, shard_transitions -def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True): +def run_successful_crosslink_tests(spec, state, target_len_offset_slot, valid=True): state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot) init_slot = state.slot - assert state.shard_states[shard].slot == init_slot - 1 # Create SignedShardBlock at init_slot shard_block = build_shard_block( @@ -56,36 +61,86 @@ def run_basic_crosslink_tests(spec, state, target_len_offset_slot, valid=True): attestations, shard_transitions = get_attestations_and_shard_transitions(spec, state, shard_block_dict) next_slot(spec, state) - pre_gasprice = state.shard_states[shard].gasprice + for attestation in attestations: + _, _, _ = run_attestation_processing(spec, state, attestation) + + pre_gasprice = state.shard_states[shard].gasprice pre_shard_state = state.shard_states[shard] yield from run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=valid) if valid: shard_state = state.shard_states[shard] + shard_transition = shard_transitions[shard] assert shard_state != pre_shard_state - assert shard_state == shard_transitions[shard].shard_states[len(shard_transitions[shard].shard_states) - 1] + assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1] assert shard_state.latest_block_root == shard_block.message.hash_tree_root() if target_len_offset_slot == 1: assert shard_state.gasprice > pre_gasprice + for pending_attestation in state.current_epoch_attestations: + assert bool(pending_attestation.crosslink_success) is True + @with_all_phases_except([PHASE0]) @spec_state_test def test_basic_crosslinks(spec, state): # NOTE: this test is only for full crosslink (minimal config), not for mainnet - yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=1, valid=True) + yield from run_successful_crosslink_tests(spec, state, target_len_offset_slot=1) @with_all_phases_except([PHASE0]) @spec_state_test def test_multiple_offset_slots(spec, state): # NOTE: this test is only for full crosslink (minimal config), not for mainnet - yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=2, valid=True) + yield from run_successful_crosslink_tests(spec, state, target_len_offset_slot=2) @with_all_phases_except([PHASE0]) @spec_state_test def test_no_winning_root(spec, state): # NOTE: this test is only for full crosslink (minimal config), not for mainnet - yield from run_basic_crosslink_tests(spec, state, target_len_offset_slot=1, valid=True) + state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot=1) + init_slot = state.slot + + # Create SignedShardBlock at init_slot + shard_block = build_shard_block( + spec, state, shard, + slot=init_slot, body=get_sample_shard_block_body(spec, is_max=True), signed=True + ) + + # Transition state to target shard slot + transition_to(spec, state, target_shard_slot) + + # Create a shard_transitions that would be included at beacon block `target_shard_slot + 1` + shard_transitions = get_shard_transitions(spec, state, {shard: [shard_block]}) + shard_transition = shard_transitions[shard] + committee_index = get_committee_index_of_shard(spec, state, state.slot, shard) + attestation = get_valid_attestation( + spec, state, + index=committee_index, + shard_transition=shard_transition, + signed=True, + on_time=True, + ) + + # Decrease attested participants to 1/3 committee + beacon_committee = spec.get_beacon_committee(state, state.slot, committee_index) + attested_participants = beacon_committee[:len(beacon_committee) // 3] + for i in range(len(beacon_committee)): + attestation.aggregation_bits[i] = beacon_committee[i] in attested_participants + attestation.signature = sign_aggregate_attestation(spec, state, attestation.data, attested_participants) + + next_slot(spec, state) + + _, _, _ = run_attestation_processing(spec, state, attestation) + + # No winning root, shard_transitions[shard] is empty + shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS + pre_shard_state = state.shard_states[shard] + yield from run_shard_transitions_processing(spec, state, shard_transitions, [attestation]) + + for pending_attestation in state.current_epoch_attestations: + assert bool(pending_attestation.crosslink_success) is False + + assert state.shard_states[shard] == pre_shard_state From 37d1a0750db7f01c3781638f8a35f36a1a78dc3f Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 22:29:49 +0800 Subject: [PATCH 07/28] Use `get_shard_winning_roots` to verify --- .../block_processing/test_process_shard_transition.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index ead555098..0a0722b9c 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -65,6 +65,10 @@ def run_successful_crosslink_tests(spec, state, target_len_offset_slot, valid=Tr for attestation in attestations: _, _, _ = run_attestation_processing(spec, state, attestation) + _, winning_roots = spec.get_shard_winning_roots(state, attestations) + assert len(winning_roots) == 1 + assert winning_roots[0] == shard_transitions[shard].hash_tree_root() + pre_gasprice = state.shard_states[shard].gasprice pre_shard_state = state.shard_states[shard] yield from run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=valid) @@ -135,6 +139,9 @@ def test_no_winning_root(spec, state): _, _, _ = run_attestation_processing(spec, state, attestation) + _, winning_roots = spec.get_shard_winning_roots(state, [attestation]) + assert len(winning_roots) == 0 + # No winning root, shard_transitions[shard] is empty shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS pre_shard_state = state.shard_states[shard] From a9f8411a51ea55f1506ec538d47ac8d56fe5e74d Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 22:42:18 +0800 Subject: [PATCH 08/28] Verify all shard states --- .../test_process_shard_transition.py | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index 0a0722b9c..8d89e3911 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -67,20 +67,23 @@ def run_successful_crosslink_tests(spec, state, target_len_offset_slot, valid=Tr _, winning_roots = spec.get_shard_winning_roots(state, attestations) assert len(winning_roots) == 1 - assert winning_roots[0] == shard_transitions[shard].hash_tree_root() + shard_transition = shard_transitions[shard] + assert winning_roots[0] == shard_transition.hash_tree_root() pre_gasprice = state.shard_states[shard].gasprice - pre_shard_state = state.shard_states[shard] + pre_shard_states = state.shard_states.copy() yield from run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=valid) if valid: - shard_state = state.shard_states[shard] - shard_transition = shard_transitions[shard] - assert shard_state != pre_shard_state - assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1] - assert shard_state.latest_block_root == shard_block.message.hash_tree_root() - if target_len_offset_slot == 1: - assert shard_state.gasprice > pre_gasprice + for index, shard_state in enumerate(state.shard_states): + if index == shard: + assert shard_state != pre_shard_states[index] + assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1] + assert shard_state.latest_block_root == shard_block.message.hash_tree_root() + if target_len_offset_slot == 1: + assert shard_state.gasprice > pre_gasprice + else: + assert shard_state == pre_shard_states[index] for pending_attestation in state.current_epoch_attestations: assert bool(pending_attestation.crosslink_success) is True @@ -144,10 +147,10 @@ def test_no_winning_root(spec, state): # No winning root, shard_transitions[shard] is empty shard_transitions = [spec.ShardTransition()] * spec.MAX_SHARDS - pre_shard_state = state.shard_states[shard] + pre_shard_states = state.shard_states.copy() yield from run_shard_transitions_processing(spec, state, shard_transitions, [attestation]) for pending_attestation in state.current_epoch_attestations: assert bool(pending_attestation.crosslink_success) is False - assert state.shard_states[shard] == pre_shard_state + assert state.shard_states == pre_shard_states From b8caa6ee85c53a717cd394479134961e01be359a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 22:52:38 +0800 Subject: [PATCH 09/28] Add `test_wrong_shard_transition_root` --- .../test_process_shard_transition.py | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index 8d89e3911..ad8e7a633 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -154,3 +154,46 @@ def test_no_winning_root(spec, state): assert bool(pending_attestation.crosslink_success) is False assert state.shard_states == pre_shard_states + + +@with_all_phases_except([PHASE0]) +@spec_state_test +def test_wrong_shard_transition_root(spec, state): + state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot=1) + init_slot = state.slot + + # Create SignedShardBlock at init_slot + shard_block = build_shard_block( + spec, state, shard, + slot=init_slot, body=get_sample_shard_block_body(spec, is_max=True), signed=True + ) + + # Transition state to target shard slot + transition_to(spec, state, target_shard_slot) + + # Create a shard_transitions that would be included at beacon block `target_shard_slot + 1` + shard_transitions = get_shard_transitions(spec, state, {shard: [shard_block]}) + shard_transition = shard_transitions[shard] + wrong_shard_transition = shard_transition.copy() + wrong_shard_transition.shard_states[shard].gasprice = shard_transition.shard_states[shard].gasprice + 1 + committee_index = get_committee_index_of_shard(spec, state, state.slot, shard) + attestation = get_valid_attestation( + spec, state, + index=committee_index, + shard_transition=wrong_shard_transition, + signed=True, + on_time=True, + ) + attestations = [attestation] + + next_slot(spec, state) + + run_attestation_processing(spec, state, attestation) + + # Check if winning root != shard_transition.hash_tree_root() + _, winning_roots = spec.get_shard_winning_roots(state, attestations) + assert len(winning_roots) == 1 + shard_transition = shard_transitions[shard] + assert winning_roots[0] != shard_transition.hash_tree_root() + + yield from run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=False) From 80c6c0b5f1ff62b74d7fc6850a453494f00d4e69 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 23:07:39 +0800 Subject: [PATCH 10/28] valid==True for `run_successful_crosslink_tests` helper --- .../test_process_shard_transition.py | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index ad8e7a633..caae1a1c7 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -43,7 +43,7 @@ def get_attestations_and_shard_transitions(spec, state, shard_block_dict): return attestations, shard_transitions -def run_successful_crosslink_tests(spec, state, target_len_offset_slot, valid=True): +def run_successful_crosslink_tests(spec, state, target_len_offset_slot): state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot) init_slot = state.slot @@ -72,21 +72,20 @@ def run_successful_crosslink_tests(spec, state, target_len_offset_slot, valid=Tr pre_gasprice = state.shard_states[shard].gasprice pre_shard_states = state.shard_states.copy() - yield from run_shard_transitions_processing(spec, state, shard_transitions, attestations, valid=valid) + yield from run_shard_transitions_processing(spec, state, shard_transitions, attestations) - if valid: - for index, shard_state in enumerate(state.shard_states): - if index == shard: - assert shard_state != pre_shard_states[index] - assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1] - assert shard_state.latest_block_root == shard_block.message.hash_tree_root() - if target_len_offset_slot == 1: - assert shard_state.gasprice > pre_gasprice - else: - assert shard_state == pre_shard_states[index] + for index, shard_state in enumerate(state.shard_states): + if index == shard: + assert shard_state != pre_shard_states[index] + assert shard_state == shard_transition.shard_states[len(shard_transition.shard_states) - 1] + assert shard_state.latest_block_root == shard_block.message.hash_tree_root() + if target_len_offset_slot == 1: + assert shard_state.gasprice > pre_gasprice + else: + assert shard_state == pre_shard_states[index] - for pending_attestation in state.current_epoch_attestations: - assert bool(pending_attestation.crosslink_success) is True + for pending_attestation in state.current_epoch_attestations: + assert bool(pending_attestation.crosslink_success) is True @with_all_phases_except([PHASE0]) From c28857e4e1f7226cb177f60c94364398428d5b77 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 23:34:51 +0800 Subject: [PATCH 11/28] Fix `attestation.data.shard_head_root` bug --- specs/phase1/beacon-chain.md | 7 ++++--- tests/core/pyspec/eth2spec/test/helpers/attestations.py | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index aa250f9c4..f90d61f26 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -886,10 +886,11 @@ def process_crosslink_for_shard(state: BeaconState, for attestation in transition_attestations: participants = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) transition_participants = transition_participants.union(participants) - if len(shard_transition.shard_data_roots) > 0: + if len(shard_transition.shard_states) > 0: # Is the `shard_transition` candidate - last_offset_index = len(shard_transition.shard_data_roots) - 1 - assert attestation.data.shard_head_root == shard_transition.shard_data_roots[last_offset_index] + last_offset_index = len(shard_transition.shard_states) - 1 + shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root + assert attestation.data.shard_head_root == shard_head_root enough_online_stake = ( get_total_balance(state, online_indices.intersection(transition_participants)) * 3 >= diff --git a/tests/core/pyspec/eth2spec/test/helpers/attestations.py b/tests/core/pyspec/eth2spec/test/helpers/attestations.py index 0c3f012f5..3445f9ff6 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/attestations.py +++ b/tests/core/pyspec/eth2spec/test/helpers/attestations.py @@ -83,14 +83,14 @@ def build_attestation_data(spec, state, slot, index, shard=None, shard_transitio attestation_data.shard = shard if shard_transition is not None: - lastest_shard_data_root_index = len(shard_transition.shard_data_roots) - 1 - attestation_data.shard_head_root = shard_transition.shard_data_roots[lastest_shard_data_root_index] + last_offset_index = len(shard_transition.shard_data_roots) - 1 + attestation_data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root attestation_data.shard_transition_root = shard_transition.hash_tree_root() else: if on_time: shard_transition = spec.get_shard_transition(state, shard, shard_blocks=[]) - lastest_shard_data_root_index = len(shard_transition.shard_data_roots) - 1 - attestation_data.shard_head_root = shard_transition.shard_data_roots[lastest_shard_data_root_index] + last_offset_index = len(shard_transition.shard_data_roots) - 1 + attestation_data.shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root attestation_data.shard_transition_root = shard_transition.hash_tree_root() else: attestation_data.shard_head_root = state.shard_states[shard].latest_block_root From 1a5016157a24f059afcab33a309adc2f21e47543 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Fri, 19 Jun 2020 23:46:01 +0800 Subject: [PATCH 12/28] Fix 1. To make it more compatible, update `is_on_time_attestation` argument: replace `attestation: Attestation` with `attestation_data: AttestationData` 2. Fix `get_sample_shard_transition` --- specs/phase1/beacon-chain.md | 16 ++++++++-------- specs/phase1/validator.md | 2 +- .../core/pyspec/eth2spec/test/helpers/custody.py | 2 +- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index f90d61f26..f9b123d3b 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -652,11 +652,11 @@ def get_offset_slots(state: BeaconState, shard: Shard) -> Sequence[Slot]: ```python def is_on_time_attestation(state: BeaconState, - attestation: Attestation) -> bool: + attestation_data: AttestationData) -> bool: """ - Check if the given attestation is on-time. + Check if the given ``attestation_data`` is on-time. """ - return attestation.data.slot == compute_previous_slot(state.slot) + return attestation_data.slot == compute_previous_slot(state.slot) ``` #### `is_winning_attestation` @@ -667,11 +667,11 @@ def is_winning_attestation(state: BeaconState, committee_index: CommitteeIndex, winning_root: Root) -> bool: """ - Check if ``attestation`` helped contribute to the successful crosslink of - ``winning_root`` formed by ``committee_index`` committee at the current slot. + Check if on-time ``attestation`` helped contribute to the successful crosslink of + ``winning_root`` formed by ``committee_index`` committee. """ return ( - is_on_time_attestation(state, attestation) + is_on_time_attestation(state, attestation.data) and attestation.data.index == committee_index and attestation.data.shard_transition_root == winning_root ) @@ -766,7 +766,7 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None: assert attestation.data.source == state.previous_justified_checkpoint # Type 1: on-time attestations - if is_on_time_attestation(state, attestation): + if is_on_time_attestation(state, attestation.data): # Correct parent block root assert data.beacon_block_root == get_block_root_at_slot(state, compute_previous_slot(state.slot)) # Correct shard number @@ -941,7 +941,7 @@ def process_crosslinks(state: BeaconState, # Since the attestations are validated, all `shard_attestations` satisfy `attestation.data.shard == shard` shard_attestations = [ attestation for attestation in attestations - if is_on_time_attestation(state, attestation) and attestation.data.index == committee_index + if is_on_time_attestation(state, attestation.data) and attestation.data.index == committee_index ] winning_root = process_crosslink_for_shard( state, committee_index, shard_transitions[shard], shard_attestations diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index f347f8757..fd3b7fef6 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -157,7 +157,7 @@ def get_shard_winning_roots(state: BeaconState, # All attestations in the block for this committee/shard and are "on time" shard_attestations = [ attestation for attestation in attestations - if is_on_time_attestation(state, attestation) and attestation.data.index == committee_index + if is_on_time_attestation(state, attestation.data) and attestation.data.index == committee_index ] committee = get_beacon_committee(state, on_time_attestation_slot, committee_index) diff --git a/tests/core/pyspec/eth2spec/test/helpers/custody.py b/tests/core/pyspec/eth2spec/test/helpers/custody.py index f63a07099..d25b91a41 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/custody.py +++ b/tests/core/pyspec/eth2spec/test/helpers/custody.py @@ -172,7 +172,7 @@ def get_sample_shard_transition(spec, start_slot, block_lengths): start_slot=start_slot, shard_block_lengths=block_lengths, shard_data_roots=b, - shard_states=[spec.Root() for x in block_lengths], + shard_states=[spec.ShardState() for x in block_lengths], proposer_signature_aggregate=spec.BLSSignature(), ) return shard_transition From 769e1d0339479bf6cdab9f8fbdd2a49001928bd6 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Fri, 19 Jun 2020 09:06:37 -0700 Subject: [PATCH 13/28] Update shard-transition.md --- specs/phase1/shard-transition.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/specs/phase1/shard-transition.md b/specs/phase1/shard-transition.md index 50694ce68..bb3e76a4f 100644 --- a/specs/phase1/shard-transition.md +++ b/specs/phase1/shard-transition.md @@ -42,8 +42,6 @@ def verify_shard_block_message(beacon_parent_state: BeaconState, next_slot = Slot(block.slot + 1) offset_slots = compute_offset_slots(get_latest_slot_for_shard(beacon_parent_state, shard), next_slot) assert block.slot in offset_slots - # Check `shard` field - assert block.shard == shard # Check `proposer_index` field assert block.proposer_index == get_shard_proposer_index(beacon_parent_state, block.slot, shard) # Check `body` field From 40b397f53f3643b7ae9ad26859b0e9286127a269 Mon Sep 17 00:00:00 2001 From: ericsson Date: Sat, 20 Jun 2020 00:09:59 +0300 Subject: [PATCH 14/28] use [[]] * num instead of just [] * num --- specs/phase1/phase1-fork.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase1/phase1-fork.md b/specs/phase1/phase1-fork.md index 496191e07..6e466b16d 100644 --- a/specs/phase1/phase1-fork.md +++ b/specs/phase1/phase1-fork.md @@ -111,7 +111,7 @@ def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState: current_light_committee=CompactCommittee(), # computed after state creation next_light_committee=CompactCommittee(), # Custody game - exposed_derived_secrets=[] * EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS, + exposed_derived_secrets=[[]] * EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS, # exposed_derived_secrets will fully default to zeroes ) next_epoch = Epoch(epoch + 1) From 723784b40813aa5478e23cffc8925eaa7a1c06e4 Mon Sep 17 00:00:00 2001 From: ericsson Date: Sat, 20 Jun 2020 01:02:19 +0300 Subject: [PATCH 15/28] replace `[[]]*num` with `[()]*num` to avoild problems with mutability, as [] or () is copied --- specs/phase1/phase1-fork.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase1/phase1-fork.md b/specs/phase1/phase1-fork.md index 6e466b16d..e83e9ef4a 100644 --- a/specs/phase1/phase1-fork.md +++ b/specs/phase1/phase1-fork.md @@ -111,7 +111,7 @@ def upgrade_to_phase1(pre: phase0.BeaconState) -> BeaconState: current_light_committee=CompactCommittee(), # computed after state creation next_light_committee=CompactCommittee(), # Custody game - exposed_derived_secrets=[[]] * EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS, + exposed_derived_secrets=[()] * EARLY_DERIVED_SECRET_PENALTY_MAX_FUTURE_EPOCHS, # exposed_derived_secrets will fully default to zeroes ) next_epoch = Epoch(epoch + 1) From 4f618fc62d4aefb1418f1834710669e80bd79105 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 22 Jun 2020 23:11:21 +0800 Subject: [PATCH 16/28] Rework `shard_state_transition` interface To make `shard_state_transition` similar to phase 0 `state_transition` function 1. Rename old `shard_state_transition` to `process_shard_block` 2. Add `shard_state_transition` with `validate_message` flag, we only validate it in shard fork choice --- specs/phase1/shard-fork-choice.md | 8 +++----- specs/phase1/shard-transition.md | 33 ++++++++++++++----------------- specs/phase1/validator.md | 4 ++-- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/specs/phase1/shard-fork-choice.md b/specs/phase1/shard-fork-choice.md index 411ad9b6b..1a449e3e9 100644 --- a/specs/phase1/shard-fork-choice.md +++ b/specs/phase1/shard-fork-choice.md @@ -169,14 +169,12 @@ def on_shard_block(store: Store, shard_store: ShardStore, signed_shard_block: Si ) # Check the block is valid and compute the post-state - assert verify_shard_block_message(beacon_parent_state, shard_parent_state, shard_block) - assert verify_shard_block_signature(beacon_parent_state, signed_shard_block) - - post_state = get_post_shard_state(shard_parent_state, shard_block) + shard_state = shard_parent_state.copy() + shard_state_transition(beacon_parent_state, shard_state, shard_block) # Add new block to the store shard_store.blocks[hash_tree_root(shard_block)] = shard_block # Add new state for this block to the store - shard_store.block_states[hash_tree_root(shard_block)] = post_state + shard_store.block_states[hash_tree_root(shard_block)] = shard_state ``` diff --git a/specs/phase1/shard-transition.md b/specs/phase1/shard-transition.md index 50694ce68..1433b0837 100644 --- a/specs/phase1/shard-transition.md +++ b/specs/phase1/shard-transition.md @@ -42,8 +42,6 @@ def verify_shard_block_message(beacon_parent_state: BeaconState, next_slot = Slot(block.slot + 1) offset_slots = compute_offset_slots(get_latest_slot_for_shard(beacon_parent_state, shard), next_slot) assert block.slot in offset_slots - # Check `shard` field - assert block.shard == shard # Check `proposer_index` field assert block.proposer_index == get_shard_proposer_index(beacon_parent_state, block.slot, shard) # Check `body` field @@ -63,8 +61,20 @@ def verify_shard_block_signature(beacon_state: BeaconState, ## Shard state transition ```python -def shard_state_transition(shard_state: ShardState, - block: ShardBlock) -> None: +def shard_state_transition(beacon_parent_state: BeaconState, + shard_state: ShardState, + block: ShardBlock, + validate_message: bool = True) -> bool: + if validate_message: + assert verify_shard_block_message(beacon_parent_state, shard_state, block) + + process_shard_block(shard_state, block) + return shard_state +``` + +```python +def process_shard_block(shard_state: ShardState, + block: ShardBlock) -> None: """ Update ``shard_state`` with shard ``block``. """ @@ -79,19 +89,6 @@ def shard_state_transition(shard_state: ShardState, shard_state.latest_block_root = latest_block_root ``` -We have a pure function `get_post_shard_state` for describing the fraud proof verification and honest validator behavior. - -```python -def get_post_shard_state(shard_state: ShardState, - block: ShardBlock) -> ShardState: - """ - A pure function that returns a new post ShardState instead of modifying the given `shard_state`. - """ - post_state = shard_state.copy() - shard_state_transition(post_state, block) - return post_state -``` - ## Fraud proofs ### Verifying the proof @@ -129,7 +126,7 @@ def is_valid_fraud_proof(beacon_state: BeaconState, else: shard_state = transition.shard_states[offset_index - 1] # Not doing the actual state updates here. - shard_state = get_post_shard_state(shard_state, block) + shard_state_transition(beacon_state, shard_state, block, validate_message=False) if shard_state != transition.shard_states[offset_index]: return True diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index f347f8757..39ada7891 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -286,7 +286,7 @@ def get_shard_transition_fields( shard_data_roots = [] shard_block_lengths = [] - shard_state = beacon_state.shard_states[shard] + shard_state = beacon_state.shard_states[shard].copy() shard_block_slots = [shard_block.message.slot for shard_block in shard_blocks] offset_slots = compute_offset_slots( get_latest_slot_for_shard(beacon_state, shard), @@ -299,7 +299,7 @@ def get_shard_transition_fields( else: shard_block = SignedShardBlock(message=ShardBlock(slot=slot, shard=shard)) shard_data_roots.append(Root()) - shard_state = get_post_shard_state(shard_state, shard_block.message) + shard_state_transition(beacon_state, shard_state, shard_block.message, validate_message=False) shard_states.append(shard_state) shard_block_lengths.append(len(shard_block.message.body)) From eec14424170f1afd6a3220d534dac3aa26dfebde Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Mon, 22 Jun 2020 23:41:02 +0800 Subject: [PATCH 17/28] Reorg `shard_state_transition` argument and fix `get_shard_transition_fields` --- specs/phase1/shard-fork-choice.md | 2 +- specs/phase1/shard-transition.md | 9 +++++---- specs/phase1/validator.md | 6 +++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/specs/phase1/shard-fork-choice.md b/specs/phase1/shard-fork-choice.md index 1a449e3e9..b6d5c5ad4 100644 --- a/specs/phase1/shard-fork-choice.md +++ b/specs/phase1/shard-fork-choice.md @@ -170,7 +170,7 @@ def on_shard_block(store: Store, shard_store: ShardStore, signed_shard_block: Si # Check the block is valid and compute the post-state shard_state = shard_parent_state.copy() - shard_state_transition(beacon_parent_state, shard_state, shard_block) + shard_state_transition(shard_state, shard_block, validate_message=True, beacon_parent_state=beacon_parent_state) # Add new block to the store shard_store.blocks[hash_tree_root(shard_block)] = shard_block diff --git a/specs/phase1/shard-transition.md b/specs/phase1/shard-transition.md index 1433b0837..d725cd8bf 100644 --- a/specs/phase1/shard-transition.md +++ b/specs/phase1/shard-transition.md @@ -61,11 +61,12 @@ def verify_shard_block_signature(beacon_state: BeaconState, ## Shard state transition ```python -def shard_state_transition(beacon_parent_state: BeaconState, - shard_state: ShardState, +def shard_state_transition(shard_state: ShardState, block: ShardBlock, - validate_message: bool = True) -> bool: + validate_message: bool = True, + beacon_parent_state: Optional[BeaconState] = None) -> bool: if validate_message: + assert beacon_parent_state is not None assert verify_shard_block_message(beacon_parent_state, shard_state, block) process_shard_block(shard_state, block) @@ -126,7 +127,7 @@ def is_valid_fraud_proof(beacon_state: BeaconState, else: shard_state = transition.shard_states[offset_index - 1] # Not doing the actual state updates here. - shard_state_transition(beacon_state, shard_state, block, validate_message=False) + shard_state_transition(shard_state, block, validate_message=False) if shard_state != transition.shard_states[offset_index]: return True diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index 39ada7891..d78726bc2 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -280,13 +280,12 @@ def get_shard_transition_fields( beacon_state: BeaconState, shard: Shard, shard_blocks: Sequence[SignedShardBlock], - validate_signature: bool=True, ) -> Tuple[Sequence[uint64], Sequence[Root], Sequence[ShardState]]: shard_states = [] shard_data_roots = [] shard_block_lengths = [] - shard_state = beacon_state.shard_states[shard].copy() + shard_state = beacon_state.shard_states[shard] shard_block_slots = [shard_block.message.slot for shard_block in shard_blocks] offset_slots = compute_offset_slots( get_latest_slot_for_shard(beacon_state, shard), @@ -299,7 +298,8 @@ def get_shard_transition_fields( else: shard_block = SignedShardBlock(message=ShardBlock(slot=slot, shard=shard)) shard_data_roots.append(Root()) - shard_state_transition(beacon_state, shard_state, shard_block.message, validate_message=False) + shard_state = shard_state.copy() + shard_state_transition(shard_state, shard_block.message, validate_message=False) shard_states.append(shard_state) shard_block_lengths.append(len(shard_block.message.body)) From a1e3392d2f0b62209577174a79e87c09da9149b2 Mon Sep 17 00:00:00 2001 From: Danny Ryan Date: Mon, 22 Jun 2020 09:47:22 -0600 Subject: [PATCH 18/28] add tests for on_attestation ceckpont state fix --- .../test/fork_choice/test_on_attestation.py | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py index a5334c5c7..d8885a014 100644 --- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py +++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_attestation.py @@ -163,9 +163,9 @@ def test_on_attestation_inconsistent_target_and_head(spec, state): @with_all_phases @spec_state_test -def test_on_attestation_target_not_in_store(spec, state): +def test_on_attestation_target_block_not_in_store(spec, state): store = spec.get_forkchoice_store(state) - time = store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH + time = store.time + spec.SECONDS_PER_SLOT * (spec.SLOTS_PER_EPOCH + 1) spec.on_tick(store, time) # move to immediately before next epoch to make block new target @@ -183,11 +183,63 @@ def test_on_attestation_target_not_in_store(spec, state): run_on_attestation(spec, state, store, attestation, False) +@with_all_phases +@spec_state_test +def test_on_attestation_target_checkpoint_not_in_store(spec, state): + store = spec.get_forkchoice_store(state) + time = store.time + spec.SECONDS_PER_SLOT * (spec.SLOTS_PER_EPOCH + 1) + spec.on_tick(store, time) + + # move to immediately before next epoch to make block new target + next_epoch = spec.get_current_epoch(state) + 1 + transition_to(spec, state, spec.compute_start_slot_at_epoch(next_epoch) - 1) + + target_block = build_empty_block_for_next_slot(spec, state) + signed_target_block = state_transition_and_sign_block(spec, state, target_block) + + # add target block to store + spec.on_block(store, signed_target_block) + + # target checkpoint state is not yet in store + + attestation = get_valid_attestation(spec, state, slot=target_block.slot, signed=True) + assert attestation.data.target.root == target_block.hash_tree_root() + + run_on_attestation(spec, state, store, attestation) + + +@with_all_phases +@spec_state_test +def test_on_attestation_target_checkpoint_not_in_store_diff_slot(spec, state): + store = spec.get_forkchoice_store(state) + time = store.time + spec.SECONDS_PER_SLOT * (spec.SLOTS_PER_EPOCH + 1) + spec.on_tick(store, time) + + # move to two slots before next epoch to make target block one before an empty slot + next_epoch = spec.get_current_epoch(state) + 1 + transition_to(spec, state, spec.compute_start_slot_at_epoch(next_epoch) - 2) + + target_block = build_empty_block_for_next_slot(spec, state) + signed_target_block = state_transition_and_sign_block(spec, state, target_block) + + # add target block to store + spec.on_block(store, signed_target_block) + + # target checkpoint state is not yet in store + + attestation_slot = target_block.slot + 1 + transition_to(spec, state, attestation_slot) + attestation = get_valid_attestation(spec, state, slot=attestation_slot, signed=True) + assert attestation.data.target.root == target_block.hash_tree_root() + + run_on_attestation(spec, state, store, attestation) + + @with_all_phases @spec_state_test def test_on_attestation_beacon_block_not_in_store(spec, state): store = spec.get_forkchoice_store(state) - time = store.time + spec.SECONDS_PER_SLOT * spec.SLOTS_PER_EPOCH + time = store.time + spec.SECONDS_PER_SLOT * (spec.SLOTS_PER_EPOCH + 1) spec.on_tick(store, time) # move to immediately before next epoch to make block new target From 97d0048eaa86cebcd2d4f89c511753bde474af2a Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 Jun 2020 00:06:35 +0800 Subject: [PATCH 19/28] PR feedback: use condition to determine if the test should be skipped --- .../test_process_shard_transition.py | 24 ++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index caae1a1c7..0f610c161 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -43,6 +43,11 @@ def get_attestations_and_shard_transitions(spec, state, shard_block_dict): return attestations, shard_transitions +def is_full_crosslink(spec, state): + epoch = spec.compute_epoch_at_slot(state.slot) + return spec.get_committee_count_per_slot(state, epoch) >= spec.get_active_shard_count(state) + + def run_successful_crosslink_tests(spec, state, target_len_offset_slot): state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot) init_slot = state.slot @@ -91,21 +96,30 @@ def run_successful_crosslink_tests(spec, state, target_len_offset_slot): @with_all_phases_except([PHASE0]) @spec_state_test def test_basic_crosslinks(spec, state): - # NOTE: this test is only for full crosslink (minimal config), not for mainnet + if not is_full_crosslink(spec, state): + # Skip this test + return + yield from run_successful_crosslink_tests(spec, state, target_len_offset_slot=1) @with_all_phases_except([PHASE0]) @spec_state_test def test_multiple_offset_slots(spec, state): - # NOTE: this test is only for full crosslink (minimal config), not for mainnet + if not is_full_crosslink(spec, state): + # Skip this test + return + yield from run_successful_crosslink_tests(spec, state, target_len_offset_slot=2) @with_all_phases_except([PHASE0]) @spec_state_test def test_no_winning_root(spec, state): - # NOTE: this test is only for full crosslink (minimal config), not for mainnet + if not is_full_crosslink(spec, state): + # Skip this test + return + state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot=1) init_slot = state.slot @@ -158,6 +172,10 @@ def test_no_winning_root(spec, state): @with_all_phases_except([PHASE0]) @spec_state_test def test_wrong_shard_transition_root(spec, state): + if not is_full_crosslink(spec, state): + # Skip this test + return + state, shard, target_shard_slot = get_initial_env(spec, state, target_len_offset_slot=1) init_slot = state.slot From 5357bddcf8e49ff8d3a13938f71aa8fb14e5691c Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 Jun 2020 00:13:48 +0800 Subject: [PATCH 20/28] PR feedback: use `filter_participant_set` --- .../block_processing/test_process_shard_transition.py | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py index 0f610c161..b14b836c0 100644 --- a/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py +++ b/tests/core/pyspec/eth2spec/test/phase1/block_processing/test_process_shard_transition.py @@ -7,7 +7,6 @@ from eth2spec.test.helpers.attestations import ( get_valid_attestation, get_valid_on_time_attestation, run_attestation_processing, - sign_aggregate_attestation, ) from eth2spec.test.helpers.shard_transitions import run_shard_transitions_processing from eth2spec.test.helpers.shard_block import ( @@ -140,17 +139,12 @@ def test_no_winning_root(spec, state): spec, state, index=committee_index, shard_transition=shard_transition, + # Decrease attested participants to 1/3 committee + filter_participant_set=lambda committee: set(list(committee)[:len(committee) // 3]), signed=True, on_time=True, ) - # Decrease attested participants to 1/3 committee - beacon_committee = spec.get_beacon_committee(state, state.slot, committee_index) - attested_participants = beacon_committee[:len(beacon_committee) // 3] - for i in range(len(beacon_committee)): - attestation.aggregation_bits[i] = beacon_committee[i] in attested_participants - attestation.signature = sign_aggregate_attestation(spec, state, attestation.data, attested_participants) - next_slot(spec, state) _, _, _ = run_attestation_processing(spec, state, attestation) From ddddc4ba9958833aad4e2724c33a94de5c49d93c Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 23 Jun 2020 02:06:27 +0200 Subject: [PATCH 21/28] attester slashing with 0 indices and out of bounds indices --- .../test_process_attester_slashing.py | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py index 063514498..724e5bf6f 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py @@ -197,6 +197,74 @@ def test_participants_already_slashed(spec, state): # Some of the following tests are phase0 only: phase 1 lists participants with bitfields instead of index list. +@with_phases([PHASE0]) +@spec_state_test +@always_bls +def test_att1_high_index(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) + + indices = get_indexed_attestation_participants(spec, attester_slashing.attestation_1) + indices.append(spec.ValidatorIndex(len(state.validators))) # off by 1 + attester_slashing.attestation_1.attesting_indices = indices + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@with_phases([PHASE0]) +@spec_state_test +@always_bls +def test_att2_high_index(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=True) + + indices = get_indexed_attestation_participants(spec, attester_slashing.attestation_2) + indices.append(spec.ValidatorIndex(len(state.validators))) # off by 1 + attester_slashing.attestation_2.attesting_indices = indices + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +EMPTY_SIGNATURE = b'\xc0' + (b'\x00' * 95) + + +@with_phases([PHASE0]) +@spec_state_test +@always_bls +def test_att1_empty_indices(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) + + attester_slashing.attestation_1.attesting_indices = [] + attester_slashing.attestation_1.signature = EMPTY_SIGNATURE + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@with_phases([PHASE0]) +@spec_state_test +@always_bls +def test_att2_empty_indices(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False) + + attester_slashing.attestation_2.attesting_indices = [] + attester_slashing.attestation_2.signature = EMPTY_SIGNATURE + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + +@with_phases([PHASE0]) +@spec_state_test +@always_bls +def test_all_empty_indices(spec, state): + attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=False) + + attester_slashing.attestation_1.attesting_indices = [] + attester_slashing.attestation_1.signature = EMPTY_SIGNATURE + + attester_slashing.attestation_2.attesting_indices = [] + attester_slashing.attestation_2.signature = EMPTY_SIGNATURE + + yield from run_attester_slashing_processing(spec, state, attester_slashing, False) + + @with_phases([PHASE0]) @spec_state_test @always_bls From db5da9dc973c0fac9e1b9d1d3e19f755b91a8ac8 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 Jun 2020 10:50:41 +0800 Subject: [PATCH 22/28] Fix return type Co-authored-by: Danny Ryan --- specs/phase1/shard-transition.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase1/shard-transition.md b/specs/phase1/shard-transition.md index d725cd8bf..e9982b370 100644 --- a/specs/phase1/shard-transition.md +++ b/specs/phase1/shard-transition.md @@ -64,7 +64,7 @@ def verify_shard_block_signature(beacon_state: BeaconState, def shard_state_transition(shard_state: ShardState, block: ShardBlock, validate_message: bool = True, - beacon_parent_state: Optional[BeaconState] = None) -> bool: + beacon_parent_state: Optional[BeaconState] = None) -> ShardState: if validate_message: assert beacon_parent_state is not None assert verify_shard_block_message(beacon_parent_state, shard_state, block) From e52c19896da33751ad85cbfed3315c6f4f607f04 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Tue, 23 Jun 2020 18:22:18 +0800 Subject: [PATCH 23/28] Update 1. Check on-time attestations fit `attestation.data.shard_transition_root != hash_tree_root(ShardTransition())` 2. Move `attestation.data.shard_head_root` check to after winning root --- specs/phase1/beacon-chain.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index f9b123d3b..5a5200c9e 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -772,6 +772,8 @@ def validate_attestation(state: BeaconState, attestation: Attestation) -> None: # Correct shard number shard = compute_shard_from_committee_index(state, attestation.data.index, attestation.data.slot) assert attestation.data.shard == shard + # On-time attestations should have a non-empty shard transition root + assert attestation.data.shard_transition_root != hash_tree_root(ShardTransition()) # Type 2: no shard transition else: # Ensure delayed attestation @@ -886,11 +888,6 @@ def process_crosslink_for_shard(state: BeaconState, for attestation in transition_attestations: participants = get_attesting_indices(state, attestation.data, attestation.aggregation_bits) transition_participants = transition_participants.union(participants) - if len(shard_transition.shard_states) > 0: - # Is the `shard_transition` candidate - last_offset_index = len(shard_transition.shard_states) - 1 - shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root - assert attestation.data.shard_head_root == shard_head_root enough_online_stake = ( get_total_balance(state, online_indices.intersection(transition_participants)) * 3 >= @@ -903,6 +900,12 @@ def process_crosslink_for_shard(state: BeaconState, # Attestation <-> shard transition consistency assert shard_transition_root == hash_tree_root(shard_transition) + # Check `shard_head_root` of the winning root + last_offset_index = len(shard_transition.shard_states) - 1 + shard_head_root = shard_transition.shard_states[last_offset_index].latest_block_root + for attestation in transition_attestations: + assert attestation.data.shard_head_root == shard_head_root + # Apply transition apply_shard_transition(state, shard, shard_transition) # Apply proposer reward and cost From 4bf10be4ffd8a8b344240918fee69b4c21507338 Mon Sep 17 00:00:00 2001 From: protolambda Date: Tue, 23 Jun 2020 21:14:43 +0200 Subject: [PATCH 24/28] use BLS constant for special signature --- .../test_process_attester_slashing.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py index 724e5bf6f..192e0390d 100644 --- a/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py +++ b/tests/core/pyspec/eth2spec/test/phase0/block_processing/test_process_attester_slashing.py @@ -223,9 +223,6 @@ def test_att2_high_index(spec, state): yield from run_attester_slashing_processing(spec, state, attester_slashing, False) -EMPTY_SIGNATURE = b'\xc0' + (b'\x00' * 95) - - @with_phases([PHASE0]) @spec_state_test @always_bls @@ -233,7 +230,7 @@ def test_att1_empty_indices(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=True) attester_slashing.attestation_1.attesting_indices = [] - attester_slashing.attestation_1.signature = EMPTY_SIGNATURE + attester_slashing.attestation_1.signature = spec.bls.Z2_SIGNATURE yield from run_attester_slashing_processing(spec, state, attester_slashing, False) @@ -245,7 +242,7 @@ def test_att2_empty_indices(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=True, signed_2=False) attester_slashing.attestation_2.attesting_indices = [] - attester_slashing.attestation_2.signature = EMPTY_SIGNATURE + attester_slashing.attestation_2.signature = spec.bls.Z2_SIGNATURE yield from run_attester_slashing_processing(spec, state, attester_slashing, False) @@ -257,10 +254,10 @@ def test_all_empty_indices(spec, state): attester_slashing = get_valid_attester_slashing(spec, state, signed_1=False, signed_2=False) attester_slashing.attestation_1.attesting_indices = [] - attester_slashing.attestation_1.signature = EMPTY_SIGNATURE + attester_slashing.attestation_1.signature = spec.bls.Z2_SIGNATURE attester_slashing.attestation_2.attesting_indices = [] - attester_slashing.attestation_2.signature = EMPTY_SIGNATURE + attester_slashing.attestation_2.signature = spec.bls.Z2_SIGNATURE yield from run_attester_slashing_processing(spec, state, attester_slashing, False) From dbd1d4e5896926109dc8244b651fbd37bab2b3e6 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 24 Jun 2020 12:14:22 +0800 Subject: [PATCH 25/28] PR feedback: Enable `verify_shard_block_signature` --- specs/phase1/shard-fork-choice.md | 4 +++- specs/phase1/shard-transition.md | 19 ++++++++++--------- specs/phase1/validator.md | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/specs/phase1/shard-fork-choice.md b/specs/phase1/shard-fork-choice.md index b6d5c5ad4..8d64eb842 100644 --- a/specs/phase1/shard-fork-choice.md +++ b/specs/phase1/shard-fork-choice.md @@ -170,7 +170,9 @@ def on_shard_block(store: Store, shard_store: ShardStore, signed_shard_block: Si # Check the block is valid and compute the post-state shard_state = shard_parent_state.copy() - shard_state_transition(shard_state, shard_block, validate_message=True, beacon_parent_state=beacon_parent_state) + shard_state_transition( + shard_state, signed_shard_block, + validate=True, beacon_parent_state=beacon_parent_state) # Add new block to the store shard_store.blocks[hash_tree_root(shard_block)] = shard_block diff --git a/specs/phase1/shard-transition.md b/specs/phase1/shard-transition.md index e9982b370..05f986000 100644 --- a/specs/phase1/shard-transition.md +++ b/specs/phase1/shard-transition.md @@ -50,10 +50,10 @@ def verify_shard_block_message(beacon_parent_state: BeaconState, ``` ```python -def verify_shard_block_signature(beacon_state: BeaconState, +def verify_shard_block_signature(beacon_parent_state: BeaconState, signed_block: SignedShardBlock) -> bool: - proposer = beacon_state.validators[signed_block.message.proposer_index] - domain = get_domain(beacon_state, DOMAIN_SHARD_PROPOSAL, compute_epoch_at_slot(signed_block.message.slot)) + proposer = beacon_parent_state.validators[signed_block.message.proposer_index] + domain = get_domain(beacon_parent_state, DOMAIN_SHARD_PROPOSAL, compute_epoch_at_slot(signed_block.message.slot)) signing_root = compute_signing_root(signed_block.message, domain) return bls.Verify(proposer.pubkey, signing_root, signed_block.signature) ``` @@ -62,14 +62,15 @@ def verify_shard_block_signature(beacon_state: BeaconState, ```python def shard_state_transition(shard_state: ShardState, - block: ShardBlock, - validate_message: bool = True, + signed_block: SignedShardBlock, + validate: bool = True, beacon_parent_state: Optional[BeaconState] = None) -> ShardState: - if validate_message: + if validate: assert beacon_parent_state is not None - assert verify_shard_block_message(beacon_parent_state, shard_state, block) + assert verify_shard_block_message(beacon_parent_state, shard_state, signed_block.message) + assert verify_shard_block_signature(beacon_parent_state, signed_block) - process_shard_block(shard_state, block) + process_shard_block(shard_state, signed_block.message) return shard_state ``` @@ -127,7 +128,7 @@ def is_valid_fraud_proof(beacon_state: BeaconState, else: shard_state = transition.shard_states[offset_index - 1] # Not doing the actual state updates here. - shard_state_transition(shard_state, block, validate_message=False) + shard_state_transition(shard_state, block, validate=False) if shard_state != transition.shard_states[offset_index]: return True diff --git a/specs/phase1/validator.md b/specs/phase1/validator.md index d78726bc2..c0d6ecbf9 100644 --- a/specs/phase1/validator.md +++ b/specs/phase1/validator.md @@ -299,7 +299,7 @@ def get_shard_transition_fields( shard_block = SignedShardBlock(message=ShardBlock(slot=slot, shard=shard)) shard_data_roots.append(Root()) shard_state = shard_state.copy() - shard_state_transition(shard_state, shard_block.message, validate_message=False) + shard_state_transition(shard_state, shard_block, validate=False) shard_states.append(shard_state) shard_block_lengths.append(len(shard_block.message.body)) From c4973792e2d7125b85d43c634f77d08e25dfc8d8 Mon Sep 17 00:00:00 2001 From: Hsiao-Wei Wang Date: Wed, 24 Jun 2020 12:50:27 +0800 Subject: [PATCH 26/28] Make `get_pending_shard_blocks` return `Sequence[SignedShardBlock]` --- specs/phase1/shard-fork-choice.md | 33 +++++++++++-------- .../test/fork_choice/test_on_shard_head.py | 7 ++-- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/specs/phase1/shard-fork-choice.md b/specs/phase1/shard-fork-choice.md index 8d64eb842..c34501a6d 100644 --- a/specs/phase1/shard-fork-choice.md +++ b/specs/phase1/shard-fork-choice.md @@ -36,7 +36,7 @@ This document is the shard chain fork choice spec for part of Ethereum 2.0 Phase @dataclass class ShardStore: shard: Shard - blocks: Dict[Root, ShardBlock] = field(default_factory=dict) + signed_blocks: Dict[Root, SignedShardBlock] = field(default_factory=dict) block_states: Dict[Root, ShardState] = field(default_factory=dict) ``` @@ -46,7 +46,11 @@ class ShardStore: def get_forkchoice_shard_store(anchor_state: BeaconState, shard: Shard) -> ShardStore: return ShardStore( shard=shard, - blocks={anchor_state.shard_states[shard].latest_block_root: ShardBlock(slot=anchor_state.slot, shard=shard)}, + signed_blocks={ + anchor_state.shard_states[shard].latest_block_root: SignedShardBlock( + message=ShardBlock(slot=anchor_state.slot, shard=shard) + ) + }, block_states={anchor_state.shard_states[shard].latest_block_root: anchor_state.copy().shard_states[shard]}, ) ``` @@ -65,7 +69,7 @@ def get_shard_latest_attesting_balance(store: Store, shard_store: ShardStore, ro # would be ignored once their newer vote is accepted. Check if it makes sense. and store.latest_messages[i].shard == shard_store.shard and get_shard_ancestor( - store, shard_store, store.latest_messages[i].shard_root, shard_store.blocks[root].slot + store, shard_store, store.latest_messages[i].shard_root, shard_store.signed_blocks[root].message.slot ) == root ) )) @@ -80,8 +84,8 @@ def get_shard_head(store: Store, shard_store: ShardStore) -> Root: shard_head_state = store.block_states[beacon_head_root].shard_states[shard_store.shard] shard_head_root = shard_head_state.latest_block_root shard_blocks = { - root: shard_block for root, shard_block in shard_store.blocks.items() - if shard_block.slot > shard_head_state.slot + root: signed_shard_block.message for root, signed_shard_block in shard_store.signed_blocks.items() + if signed_shard_block.message.slot > shard_head_state.slot } while True: # Find the valid child block roots @@ -101,7 +105,7 @@ def get_shard_head(store: Store, shard_store: ShardStore) -> Root: ```python def get_shard_ancestor(store: Store, shard_store: ShardStore, root: Root, slot: Slot) -> Root: - block = shard_store.blocks[root] + block = shard_store.signed_blocks[root].message if block.slot > slot: return get_shard_ancestor(store, shard_store, block.shard_parent_root, slot) elif block.slot == slot: @@ -114,7 +118,7 @@ def get_shard_ancestor(store: Store, shard_store: ShardStore, root: Root, slot: #### `get_pending_shard_blocks` ```python -def get_pending_shard_blocks(store: Store, shard_store: ShardStore) -> Sequence[ShardBlock]: +def get_pending_shard_blocks(store: Store, shard_store: ShardStore) -> Sequence[SignedShardBlock]: """ Return the canonical shard block branch that has not yet been crosslinked. """ @@ -126,14 +130,14 @@ def get_pending_shard_blocks(store: Store, shard_store: ShardStore) -> Sequence[ shard_head_root = get_shard_head(store, shard_store) root = shard_head_root - shard_blocks = [] + signed_shard_blocks = [] while root != latest_shard_block_root: - shard_block = shard_store.blocks[root] - shard_blocks.append(shard_block) - root = shard_block.shard_parent_root + signed_shard_block = shard_store.signed_blocks[root] + signed_shard_blocks.append(signed_shard_block) + root = signed_shard_block.message.shard_parent_root - shard_blocks.reverse() - return shard_blocks + signed_shard_blocks.reverse() + return signed_shard_blocks ``` ### Handlers @@ -175,7 +179,8 @@ def on_shard_block(store: Store, shard_store: ShardStore, signed_shard_block: Si validate=True, beacon_parent_state=beacon_parent_state) # Add new block to the store - shard_store.blocks[hash_tree_root(shard_block)] = shard_block + # Note: storing `SignedShardBlock` format for computing `ShardTransition.proposer_signature_aggregate` + shard_store.signed_blocks[hash_tree_root(shard_block)] = signed_shard_block # Add new state for this block to the store shard_store.block_states[hash_tree_root(shard_block)] = shard_state diff --git a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py index f7b888a2e..a87a85917 100644 --- a/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py +++ b/tests/core/pyspec/eth2spec/test/fork_choice/test_on_shard_head.py @@ -22,7 +22,7 @@ def run_on_shard_block(spec, store, shard_store, signed_block, valid=True): assert False spec.on_shard_block(store, shard_store, signed_block) - assert shard_store.blocks[hash_tree_root(signed_block.message)] == signed_block.message + assert shard_store.signed_blocks[hash_tree_root(signed_block.message)] == signed_block def apply_shard_block(spec, store, shard_store, beacon_parent_state, shard_blocks_buffer): @@ -41,10 +41,7 @@ def apply_shard_block(spec, store, shard_store, beacon_parent_state, shard_block def check_pending_shard_blocks(spec, store, shard_store, shard_blocks_buffer): - pending_shard_blocks = [ - spec.SignedShardBlock(message=b) - for b in spec.get_pending_shard_blocks(store, shard_store) - ] + pending_shard_blocks = spec.get_pending_shard_blocks(store, shard_store) assert pending_shard_blocks == shard_blocks_buffer From 593ed032f3e04153b747acff7d016fe1692d1100 Mon Sep 17 00:00:00 2001 From: Lion - dapplion <35266934+dapplion@users.noreply.github.com> Date: Wed, 24 Jun 2020 17:09:58 +0200 Subject: [PATCH 27/28] SSZ Typescript Lodestar implementation repo moved --- ssz/simple-serialize.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ssz/simple-serialize.md b/ssz/simple-serialize.md index 700a428e8..a9d1da56e 100644 --- a/ssz/simple-serialize.md +++ b/ssz/simple-serialize.md @@ -251,7 +251,7 @@ We similarly define "summary types" and "expansion types". For example, [`Beacon | Rust | Lighthouse | Sigma Prime | [https://github.com/sigp/lighthouse/tree/master/eth2/utils/ssz](https://github.com/sigp/lighthouse/tree/master/eth2/utils/ssz) | | Nim | Nimbus | Status | [https://github.com/status-im/nim-beacon-chain/blob/master/beacon_chain/ssz.nim](https://github.com/status-im/nim-beacon-chain/blob/master/beacon_chain/ssz.nim) | | Rust | Shasper | ParityTech | [https://github.com/paritytech/shasper/tree/master/utils/ssz](https://github.com/paritytech/shasper/tree/master/utils/ssz) | -| TypeScript | Lodestar | ChainSafe Systems | [https://github.com/ChainSafe/ssz-js](https://github.com/ChainSafe/ssz-js) | +| TypeScript | Lodestar | ChainSafe Systems | [https://github.com/ChainSafe/ssz-js](https://github.com/ChainSafe/ssz) | | Java | Cava | ConsenSys | [https://www.github.com/ConsenSys/cava/tree/master/ssz](https://www.github.com/ConsenSys/cava/tree/master/ssz) | | Go | Prysm | Prysmatic Labs | [https://github.com/prysmaticlabs/go-ssz](https://github.com/prysmaticlabs/go-ssz) | | Swift | Yeeth | Dean Eigenmann | [https://github.com/yeeth/SimpleSerialize.swift](https://github.com/yeeth/SimpleSerialize.swift) | From 7e04c70ca14dbbb023e2c4bde033a07a09e8eca6 Mon Sep 17 00:00:00 2001 From: terence tsao Date: Wed, 24 Jun 2020 16:53:53 -0700 Subject: [PATCH 28/28] Update beacon-chain.md --- specs/phase1/beacon-chain.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/phase1/beacon-chain.md b/specs/phase1/beacon-chain.md index 5a5200c9e..e103d51f4 100644 --- a/specs/phase1/beacon-chain.md +++ b/specs/phase1/beacon-chain.md @@ -569,7 +569,7 @@ def get_light_client_committee(beacon_state: BeaconState, epoch: Epoch) -> Seque seed=seed, index=0, count=get_active_shard_count(beacon_state), - )[:TARGET_COMMITTEE_SIZE] + )[:LIGHT_CLIENT_COMMITTEE_SIZE] ``` #### `get_shard_proposer_index`