Merge pull request #2175 from ralexstokes/add-light-client-tests
Add light client tests
This commit is contained in:
commit
f73189c30f
|
@ -0,0 +1,33 @@
|
|||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
)
|
||||
from eth2spec.utils import bls
|
||||
|
||||
|
||||
def compute_sync_committee_signature(spec, state, slot, privkey):
|
||||
domain = spec.get_domain(state, spec.DOMAIN_SYNC_COMMITTEE, spec.compute_epoch_at_slot(slot))
|
||||
if slot == state.slot:
|
||||
block_root = build_empty_block_for_next_slot(spec, state).parent_root
|
||||
else:
|
||||
block_root = spec.get_block_root_at_slot(state, slot)
|
||||
signing_root = spec.compute_signing_root(block_root, domain)
|
||||
return bls.Sign(privkey, signing_root)
|
||||
|
||||
|
||||
def compute_aggregate_sync_committee_signature(spec, state, slot, participants):
|
||||
if len(participants) == 0:
|
||||
return spec.G2_POINT_AT_INFINITY
|
||||
|
||||
signatures = []
|
||||
for validator_index in participants:
|
||||
privkey = privkeys[validator_index]
|
||||
signatures.append(
|
||||
compute_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
slot,
|
||||
privkey,
|
||||
)
|
||||
)
|
||||
return bls.Aggregate(signatures)
|
|
@ -0,0 +1,232 @@
|
|||
import random
|
||||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
transition_unsigned_block,
|
||||
)
|
||||
from eth2spec.test.helpers.state import (
|
||||
state_transition_and_sign_block,
|
||||
transition_to,
|
||||
)
|
||||
from eth2spec.test.helpers.sync_committee import (
|
||||
compute_aggregate_sync_committee_signature,
|
||||
)
|
||||
from eth2spec.test.context import (
|
||||
PHASE0, PHASE1,
|
||||
expect_assertion_error,
|
||||
with_all_phases_except,
|
||||
spec_state_test,
|
||||
)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0, PHASE1])
|
||||
@spec_state_test
|
||||
def test_invalid_signature_missing_participant(spec, state):
|
||||
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
|
||||
random_participant = random.choice(committee)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
# Exclude one participant whose signature was included.
|
||||
block.body.sync_committee_bits = [index != random_participant for index in committee]
|
||||
block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
block.slot - 1,
|
||||
committee, # full committee signs
|
||||
)
|
||||
|
||||
yield 'blocks', [block]
|
||||
expect_assertion_error(lambda: spec.process_sync_committee(state, block.body))
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0, PHASE1])
|
||||
@spec_state_test
|
||||
def test_invalid_signature_extra_participant(spec, state):
|
||||
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
|
||||
random_participant = random.choice(committee)
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
# Exclude one signature even though the block claims the entire committee participated.
|
||||
block.body.sync_committee_bits = [True] * len(committee)
|
||||
block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
block.slot - 1,
|
||||
[index for index in committee if index != random_participant],
|
||||
)
|
||||
|
||||
yield 'blocks', [block]
|
||||
expect_assertion_error(lambda: spec.process_sync_committee(state, block.body))
|
||||
yield 'post', None
|
||||
|
||||
|
||||
def compute_sync_committee_participant_reward(spec, state, participant_index, active_validator_count, committee_size):
|
||||
base_reward = spec.get_base_reward(state, participant_index)
|
||||
proposer_reward = spec.get_proposer_reward(state, participant_index)
|
||||
max_participant_reward = base_reward - proposer_reward
|
||||
return max_participant_reward * active_validator_count // committee_size // spec.SLOTS_PER_EPOCH
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0, PHASE1])
|
||||
@spec_state_test
|
||||
def test_sync_committee_rewards(spec, state):
|
||||
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
|
||||
committee_size = len(committee)
|
||||
active_validator_count = len(spec.get_active_validator_indices(state, spec.get_current_epoch(state)))
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
pre_balances = state.balances.copy()
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.sync_committee_bits = [True] * committee_size
|
||||
block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
block.slot - 1,
|
||||
committee,
|
||||
)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
||||
|
||||
for index in range(len(state.validators)):
|
||||
expected_reward = 0
|
||||
|
||||
if index == block.proposer_index:
|
||||
expected_reward += sum([spec.get_proposer_reward(state, index) for index in committee])
|
||||
|
||||
if index in committee:
|
||||
expected_reward += compute_sync_committee_participant_reward(
|
||||
spec,
|
||||
state,
|
||||
index,
|
||||
active_validator_count,
|
||||
committee_size
|
||||
)
|
||||
|
||||
assert state.balances[index] == pre_balances[index] + expected_reward
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0, PHASE1])
|
||||
@spec_state_test
|
||||
def test_invalid_signature_past_block(spec, state):
|
||||
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
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)
|
||||
# Valid sync committee signature here...
|
||||
block.body.sync_committee_bits = [True] * len(committee)
|
||||
block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
block.slot - 1,
|
||||
committee,
|
||||
)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
blocks.append(signed_block)
|
||||
|
||||
invalid_block = build_empty_block_for_next_slot(spec, state)
|
||||
# Invalid signature from a slot other than the previous
|
||||
invalid_block.body.sync_committee_bits = [True] * len(committee)
|
||||
invalid_block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
invalid_block.slot - 2,
|
||||
committee,
|
||||
)
|
||||
blocks.append(invalid_block)
|
||||
|
||||
expect_assertion_error(lambda: transition_unsigned_block(spec, state, invalid_block))
|
||||
|
||||
yield 'blocks', blocks
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0, PHASE1])
|
||||
@spec_state_test
|
||||
def test_invalid_signature_previous_committee(spec, state):
|
||||
# NOTE: the `state` provided is at genesis and the process to select
|
||||
# sync committees currently returns the same committee for the first and second
|
||||
# periods at genesis.
|
||||
# To get a distinct committee so we can generate an "old" signature, we need to advance
|
||||
# 2 EPOCHS_PER_SYNC_COMMITTEE_PERIOD periods.
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
previous_committee = state.next_sync_committee
|
||||
|
||||
epoch_in_future_sync_commitee_period = current_epoch + 2 * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
|
||||
slot_in_future_sync_committee_period = epoch_in_future_sync_commitee_period * spec.SLOTS_PER_EPOCH
|
||||
transition_to(spec, state, slot_in_future_sync_committee_period)
|
||||
|
||||
pubkeys = [validator.pubkey for validator in state.validators]
|
||||
committee = [pubkeys.index(pubkey) for pubkey in previous_committee.pubkeys]
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.sync_committee_bits = [True] * len(committee)
|
||||
block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
block.slot - 1,
|
||||
committee,
|
||||
)
|
||||
|
||||
yield 'blocks', [block]
|
||||
expect_assertion_error(lambda: spec.process_sync_committee(state, block.body))
|
||||
yield 'post', None
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0, PHASE1])
|
||||
@spec_state_test
|
||||
def test_valid_signature_future_committee(spec, state):
|
||||
# NOTE: the `state` provided is at genesis and the process to select
|
||||
# sync committees currently returns the same committee for the first and second
|
||||
# periods at genesis.
|
||||
# To get a distinct committee so we can generate an "old" signature, we need to advance
|
||||
# 2 EPOCHS_PER_SYNC_COMMITTEE_PERIOD periods.
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
old_current_sync_committee = state.current_sync_committee
|
||||
old_next_sync_committee = state.next_sync_committee
|
||||
|
||||
epoch_in_future_sync_committee_period = current_epoch + 2 * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD
|
||||
slot_in_future_sync_committee_period = epoch_in_future_sync_committee_period * spec.SLOTS_PER_EPOCH
|
||||
transition_to(spec, state, slot_in_future_sync_committee_period)
|
||||
|
||||
sync_committee = state.current_sync_committee
|
||||
|
||||
expected_sync_committee = spec.get_sync_committee(state, epoch_in_future_sync_committee_period)
|
||||
|
||||
assert sync_committee == expected_sync_committee
|
||||
assert sync_committee != old_current_sync_committee
|
||||
assert sync_committee != old_next_sync_committee
|
||||
|
||||
pubkeys = [validator.pubkey for validator in state.validators]
|
||||
committee_indices = [pubkeys.index(pubkey) for pubkey in sync_committee.pubkeys]
|
||||
|
||||
yield 'pre', state
|
||||
|
||||
block = build_empty_block_for_next_slot(spec, state)
|
||||
block.body.sync_committee_bits = [True] * len(committee_indices)
|
||||
block.body.sync_committee_signature = compute_aggregate_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
block.slot - 1,
|
||||
committee_indices,
|
||||
)
|
||||
|
||||
signed_block = state_transition_and_sign_block(spec, state, block)
|
||||
|
||||
yield 'blocks', [signed_block]
|
||||
yield 'post', state
|
|
@ -0,0 +1,37 @@
|
|||
from eth2spec.test.context import (
|
||||
PHASE0, PHASE1,
|
||||
with_all_phases_except,
|
||||
spec_state_test,
|
||||
)
|
||||
from eth2spec.test.helpers.state import transition_to
|
||||
from eth2spec.test.helpers.epoch_processing import (
|
||||
run_epoch_processing_with,
|
||||
)
|
||||
|
||||
|
||||
@with_all_phases_except([PHASE0, PHASE1])
|
||||
@spec_state_test
|
||||
def test_sync_committees_progress(spec, state):
|
||||
current_epoch = spec.get_current_epoch(state)
|
||||
# NOTE: if not in the genesis epoch, period math below needs to be
|
||||
# adjusted relative to the current epoch
|
||||
assert current_epoch == 0
|
||||
|
||||
first_sync_committee = state.current_sync_committee
|
||||
second_sync_committee = state.next_sync_committee
|
||||
|
||||
slot_at_end_of_current_period = spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD * spec.SLOTS_PER_EPOCH - 1
|
||||
transition_to(spec, state, slot_at_end_of_current_period)
|
||||
|
||||
# Ensure assignments have not changed:
|
||||
assert state.current_sync_committee == first_sync_committee
|
||||
assert state.next_sync_committee == second_sync_committee
|
||||
|
||||
yield from run_epoch_processing_with(spec, state, 'process_final_updates')
|
||||
|
||||
# Can compute the third committee having computed final balances in the last epoch
|
||||
# of this `EPOCHS_PER_SYNC_COMMITTEE_PERIOD`
|
||||
third_sync_committee = spec.get_sync_committee(state, 2 * spec.EPOCHS_PER_SYNC_COMMITTEE_PERIOD)
|
||||
|
||||
assert state.current_sync_committee == second_sync_committee
|
||||
assert state.next_sync_committee == third_sync_committee
|
|
@ -1,6 +1,4 @@
|
|||
import random
|
||||
from eth2spec.test.helpers.keys import privkeys
|
||||
from eth2spec.utils import bls
|
||||
from eth2spec.test.helpers.state import (
|
||||
state_transition_and_sign_block,
|
||||
next_epoch,
|
||||
|
@ -8,6 +6,9 @@ from eth2spec.test.helpers.state import (
|
|||
from eth2spec.test.helpers.block import (
|
||||
build_empty_block_for_next_slot,
|
||||
)
|
||||
from eth2spec.test.helpers.sync_committee import (
|
||||
compute_aggregate_sync_committee_signature,
|
||||
)
|
||||
from eth2spec.test.context import (
|
||||
PHASE0, PHASE1,
|
||||
with_all_phases_except,
|
||||
|
@ -15,34 +16,6 @@ from eth2spec.test.context import (
|
|||
)
|
||||
|
||||
|
||||
def compute_sync_committee_signature(spec, state, slot, privkey):
|
||||
domain = spec.get_domain(state, spec.DOMAIN_SYNC_COMMITTEE, spec.compute_epoch_at_slot(slot))
|
||||
if slot == state.slot:
|
||||
block_root = build_empty_block_for_next_slot(spec, state).parent_root
|
||||
else:
|
||||
block_root = spec.get_block_root_at_slot(state, slot)
|
||||
signing_root = spec.compute_signing_root(block_root, domain)
|
||||
return bls.Sign(privkey, signing_root)
|
||||
|
||||
|
||||
def compute_aggregate_sync_committee_signature(spec, state, slot, participants):
|
||||
if len(participants) == 0:
|
||||
return spec.G2_POINT_AT_INFINITY
|
||||
|
||||
signatures = []
|
||||
for validator_index in participants:
|
||||
privkey = privkeys[validator_index]
|
||||
signatures.append(
|
||||
compute_sync_committee_signature(
|
||||
spec,
|
||||
state,
|
||||
slot,
|
||||
privkey,
|
||||
)
|
||||
)
|
||||
return bls.Aggregate(signatures)
|
||||
|
||||
|
||||
def run_sync_committee_sanity_test(spec, state, fraction_full=1.0):
|
||||
committee = spec.get_sync_committee_indices(state, spec.get_current_epoch(state))
|
||||
participants = random.sample(committee, int(len(committee) * fraction_full))
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import (
|
||||
from eth2spec.test.helpers.epoch_processing import (
|
||||
run_epoch_processing_with, run_epoch_processing_to
|
||||
)
|
||||
from eth2spec.test.helpers.state import transition_to
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import (
|
||||
from eth2spec.test.helpers.epoch_processing import (
|
||||
run_epoch_processing_with
|
||||
)
|
||||
from eth2spec.test.helpers.state import transition_to
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
from eth2spec.test.helpers.deposits import mock_deposit
|
||||
from eth2spec.test.helpers.state import next_epoch, next_slots
|
||||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
|
||||
|
||||
|
||||
def run_process_registry_updates(spec, state):
|
||||
|
|
|
@ -19,7 +19,7 @@ from eth2spec.test.helpers.attestations import (
|
|||
)
|
||||
from eth2spec.test.helpers.rewards import leaking
|
||||
from eth2spec.test.helpers.attester_slashings import get_indexed_attestation_participants
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
|
||||
from random import Random
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
from eth2spec.test.context import spec_state_test, with_all_phases
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import (
|
||||
from eth2spec.test.helpers.epoch_processing import (
|
||||
run_epoch_processing_with, run_epoch_processing_to
|
||||
)
|
||||
from eth2spec.test.helpers.state import next_epoch
|
||||
|
|
|
@ -15,7 +15,7 @@ from eth2spec.test.context import (
|
|||
with_configs,
|
||||
)
|
||||
from eth2spec.test.phase0.block_processing.test_process_attestation import run_attestation_processing
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
|
||||
|
||||
from eth2spec.test.phase1.block_processing.test_process_chunk_challenge import (
|
||||
run_chunk_challenge_processing,
|
||||
|
|
|
@ -17,7 +17,7 @@ from eth2spec.test.context import (
|
|||
spec_state_test,
|
||||
)
|
||||
from eth2spec.test.phase0.block_processing.test_process_attestation import run_attestation_processing
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
|
||||
|
||||
from eth2spec.test.phase1.block_processing.test_process_chunk_challenge import (
|
||||
run_chunk_challenge_processing,
|
||||
|
|
|
@ -10,7 +10,7 @@ from eth2spec.test.context import (
|
|||
with_configs,
|
||||
spec_state_test,
|
||||
)
|
||||
from eth2spec.test.phase0.epoch_processing.run_epoch_process_base import run_epoch_processing_with
|
||||
from eth2spec.test.helpers.epoch_processing import run_epoch_processing_with
|
||||
from eth2spec.test.phase1.block_processing.test_process_custody_key_reveal import run_custody_key_reveal_processing
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue