diff --git a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py index fa7f89fdc..1228136ac 100644 --- a/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py +++ b/tests/core/pyspec/eth2spec/test/altair/block_processing/test_process_sync_aggregate.py @@ -63,6 +63,29 @@ def get_committee_indices(spec, state, duplicates=False): state.randao_mixes[randao_index] = hash(state.randao_mixes[randao_index]) +@with_altair_and_later +@spec_state_test +@always_bls +def test_invalid_signature_bad_domain(spec, state): + committee_indices = compute_committee_indices(spec, state, state.current_sync_committee) + rng = random.Random(2020) + random_participant = rng.choice(committee_indices) + + block = build_empty_block_for_next_slot(spec, state) + # Exclude one participant whose signature was included. + block.body.sync_aggregate = spec.SyncAggregate( + sync_committee_bits=[index != random_participant for index in committee_indices], + sync_committee_signature=compute_aggregate_sync_committee_signature( + spec, + state, + block.slot - 1, + committee_indices, # full committee signs + domain_type=spec.DOMAIN_BEACON_ATTESTER, # Incorrect domain + ) + ) + yield from run_sync_committee_processing(spec, state, block, expect_exception=True) + + @with_altair_and_later @spec_state_test @always_bls @@ -170,7 +193,7 @@ def test_sync_committee_rewards_nonduplicate_committee(spec, state): active_validator_count = len(spec.get_active_validator_indices(state, spec.get_current_epoch(state))) # Preconditions of this test case - assert active_validator_count >= spec.SYNC_COMMITTEE_SIZE + assert active_validator_count > spec.SYNC_COMMITTEE_SIZE assert committee_size == len(set(committee_indices)) yield from run_successful_sync_committee_test(spec, state, committee_indices, committee_bits) @@ -179,7 +202,41 @@ def test_sync_committee_rewards_nonduplicate_committee(spec, state): @with_altair_and_later @with_presets([MAINNET], reason="to create duplicate committee") @spec_state_test -def test_sync_committee_rewards_duplicate_committee(spec, state): +def test_sync_committee_rewards_duplicate_committee_no_participation(spec, state): + committee_indices = get_committee_indices(spec, state, duplicates=True) + committee_size = len(committee_indices) + committee_bits = [False] * committee_size + assert len(committee_bits) == committee_size + active_validator_count = len(spec.get_active_validator_indices(state, spec.get_current_epoch(state))) + + # Preconditions of this test case + assert active_validator_count < spec.SYNC_COMMITTEE_SIZE + assert committee_size > len(set(committee_indices)) + + yield from run_successful_sync_committee_test(spec, state, committee_indices, committee_bits) + + +@with_altair_and_later +@with_presets([MAINNET], reason="to create duplicate committee") +@spec_state_test +def test_sync_committee_rewards_duplicate_committee_half_participation(spec, state): + committee_indices = get_committee_indices(spec, state, duplicates=True) + committee_size = len(committee_indices) + committee_bits = [True] * (committee_size // 2) + [False] * (committee_size // 2) + assert len(committee_bits) == committee_size + active_validator_count = len(spec.get_active_validator_indices(state, spec.get_current_epoch(state))) + + # Preconditions of this test case + assert active_validator_count < spec.SYNC_COMMITTEE_SIZE + assert committee_size > len(set(committee_indices)) + + yield from run_successful_sync_committee_test(spec, state, committee_indices, committee_bits) + + +@with_altair_and_later +@with_presets([MAINNET], reason="to create duplicate committee") +@spec_state_test +def test_sync_committee_rewards_duplicate_committee_full_participation(spec, state): committee_indices = get_committee_indices(spec, state, duplicates=True) committee_size = len(committee_indices) committee_bits = [True] * committee_size @@ -219,7 +276,6 @@ def test_sync_committee_rewards_empty_participants(spec, state): def test_invalid_signature_past_block(spec, state): committee_indices = compute_committee_indices(spec, state, state.current_sync_committee) - blocks = [] for _ in range(2): # NOTE: need to transition twice to move beyond the degenerate case at genesis block = build_empty_block_for_next_slot(spec, state) @@ -234,8 +290,7 @@ def test_invalid_signature_past_block(spec, state): ) ) - signed_block = state_transition_and_sign_block(spec, state, block) - blocks.append(signed_block) + state_transition_and_sign_block(spec, state, block) invalid_block = build_empty_block_for_next_slot(spec, state) # Invalid signature from a slot other than the previous @@ -327,3 +382,78 @@ def test_valid_signature_future_committee(spec, state): ) yield from run_sync_committee_processing(spec, state, block) + + +@with_altair_and_later +@spec_state_test +@always_bls +@with_presets([MINIMAL], reason="prefer short search to find matching proposer") +def test_proposer_in_committee_without_participation(spec, state): + committee_indices = compute_committee_indices(spec, state, state.current_sync_committee) + + # NOTE: seem to reliably be getting a matching proposer in the first epoch w/ ``MINIMAL`` preset. + for _ in range(spec.SLOTS_PER_EPOCH): + block = build_empty_block_for_next_slot(spec, state) + proposer_index = block.proposer_index + proposer_pubkey = state.validators[proposer_index].pubkey + proposer_is_in_sync_committee = proposer_pubkey in state.current_sync_committee.pubkeys + if proposer_is_in_sync_committee: + participation = [index != proposer_index for index in committee_indices] + participants = [index for index in committee_indices if index != proposer_index] + else: + participation = [True for _ in committee_indices] + participants = committee_indices + # Valid sync committee signature here... + block.body.sync_aggregate = spec.SyncAggregate( + sync_committee_bits=participation, + sync_committee_signature=compute_aggregate_sync_committee_signature( + spec, + state, + block.slot - 1, + participants, + ) + ) + + if proposer_is_in_sync_committee: + assert state.validators[block.proposer_index].pubkey in state.current_sync_committee.pubkeys + yield from run_sync_committee_processing(spec, state, block) + break + else: + state_transition_and_sign_block(spec, state, block) + else: + raise AssertionError("failed to find a proposer in the sync committee set; check test setup") + + +@with_altair_and_later +@spec_state_test +@always_bls +@with_presets([MINIMAL], reason="prefer short search to find matching proposer") +def test_proposer_in_committee_with_participation(spec, state): + committee_indices = compute_committee_indices(spec, state, state.current_sync_committee) + participation = [True for _ in committee_indices] + + # NOTE: seem to reliably be getting a matching proposer in the first epoch w/ ``MINIMAL`` preset. + for _ in range(spec.SLOTS_PER_EPOCH): + block = build_empty_block_for_next_slot(spec, state) + proposer_index = block.proposer_index + proposer_pubkey = state.validators[proposer_index].pubkey + proposer_is_in_sync_committee = proposer_pubkey in state.current_sync_committee.pubkeys + + # Valid sync committee signature here... + block.body.sync_aggregate = spec.SyncAggregate( + sync_committee_bits=participation, + sync_committee_signature=compute_aggregate_sync_committee_signature( + spec, + state, + block.slot - 1, + committee_indices, + ) + ) + + if proposer_is_in_sync_committee: + assert state.validators[block.proposer_index].pubkey in state.current_sync_committee.pubkeys + yield from run_sync_committee_processing(spec, state, block) + return + else: + state_transition_and_sign_block(spec, state, block) + raise AssertionError("failed to find a proposer in the sync committee set; check test setup") diff --git a/tests/core/pyspec/eth2spec/test/helpers/sync_committee.py b/tests/core/pyspec/eth2spec/test/helpers/sync_committee.py index fa753db52..71be65044 100644 --- a/tests/core/pyspec/eth2spec/test/helpers/sync_committee.py +++ b/tests/core/pyspec/eth2spec/test/helpers/sync_committee.py @@ -7,8 +7,10 @@ from eth2spec.test.helpers.block import ( from eth2spec.utils import bls -def compute_sync_committee_signature(spec, state, slot, privkey, block_root=None): - domain = spec.get_domain(state, spec.DOMAIN_SYNC_COMMITTEE, spec.compute_epoch_at_slot(slot)) +def compute_sync_committee_signature(spec, state, slot, privkey, block_root=None, domain_type=None): + if not domain_type: + domain_type = spec.DOMAIN_SYNC_COMMITTEE + domain = spec.get_domain(state, domain_type, spec.compute_epoch_at_slot(slot)) if block_root is None: if slot == state.slot: block_root = build_empty_block_for_next_slot(spec, state).parent_root @@ -18,7 +20,7 @@ def compute_sync_committee_signature(spec, state, slot, privkey, block_root=None return bls.Sign(privkey, signing_root) -def compute_aggregate_sync_committee_signature(spec, state, slot, participants, block_root=None): +def compute_aggregate_sync_committee_signature(spec, state, slot, participants, block_root=None, domain_type=None): if len(participants) == 0: return spec.G2_POINT_AT_INFINITY @@ -32,6 +34,7 @@ def compute_aggregate_sync_committee_signature(spec, state, slot, participants, slot, privkey, block_root=block_root, + domain_type=domain_type, ) ) return bls.Aggregate(signatures)