Skip to content

Commit 76e2a2a

Browse files
committed
add test for validate_serenity_attestation_aggregate_signature
1 parent 85bf6a2 commit 76e2a2a

File tree

3 files changed

+181
-26
lines changed

3 files changed

+181
-26
lines changed

eth/beacon/state_machines/forks/serenity/validation.py

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,7 @@ def validate_serenity_attestation(state: BeaconState,
6868

6969
validate_serenity_attestation_latest_crosslink_root(
7070
attestation.data,
71-
latest_crosslink_shard_block_root=(
72-
state.latest_crosslinks[attestation.data.shard].shard_block_root
73-
),
71+
latest_crosslink_root=state.latest_crosslinks[attestation.data.shard].shard_block_root,
7472
)
7573

7674
validate_serenity_attestation_shard_block_root(attestation.data)
@@ -161,25 +159,25 @@ def validate_serenity_attestation_justified_block_root(attestation_data: Attesta
161159

162160

163161
def validate_serenity_attestation_latest_crosslink_root(attestation_data: AttestationData,
164-
latest_crosslink_shard_block_root: Hash32) -> None:
162+
latest_crosslink_root: Hash32) -> None:
165163
"""
166-
Validate that either the ``latest_crosslink_root`` or ``shard_block_root``
167-
field of ``attestation_data`` is the provided ``latest_crosslink_shard_block_root``.
164+
Validate that either the attestation ``latest_crosslink_root`` or ``shard_block_root``
165+
field of ``attestation_data`` is the provided ``latest_crosslink_root``.
168166
Raise ``ValidationError`` if it's invalid.
169167
"""
170168
acceptable_shard_block_roots = [
171169
attestation_data.latest_crosslink_root,
172170
attestation_data.shard_block_root,
173171
]
174-
if latest_crosslink_shard_block_root not in acceptable_shard_block_roots:
172+
if latest_crosslink_root not in acceptable_shard_block_roots:
175173
raise ValidationError(
176174
"Neither the attestation ``latest_crosslink_root`` nor the attestation "
177-
"``shard_block_root`` are equal to the ``latest_crosslink_shard_block_root``.\n"
175+
"``shard_block_root`` are equal to the ``latest_crosslink_root``.\n"
178176
"\tFound: %s and %s, Expected %s" %
179177
(
180178
attestation_data.latest_crosslink_root,
181179
attestation_data.shard_block_root,
182-
latest_crosslink_shard_block_root,
180+
latest_crosslink_root,
183181
)
184182
)
185183

@@ -216,16 +214,16 @@ def validate_serenity_attestation_aggregate_signature(state: BeaconState,
216214
"""
217215
participant_indices = get_attestation_participants(
218216
state=state,
219-
slot=attestation.slot,
220-
shard=attestation.shard,
217+
slot=attestation.data.slot,
218+
shard=attestation.data.shard,
221219
participation_bitfield=attestation.participation_bitfield,
222220
epoch_length=epoch_length,
223221
)
224222

225-
pubkeys = [
223+
pubkeys = (
226224
state.validator_registry[validator_index].pubkey
227225
for validator_index in participant_indices
228-
]
226+
)
229227
group_public_key = bls.aggregate_pubkeys(pubkeys)
230228

231229
message = hash_eth2(

tests/beacon/conftest.py

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,12 @@
88
import eth._utils.bls as bls
99
from eth.beacon._utils.hash import hash_eth2
1010

11+
from eth.beacon.helpers import (
12+
get_new_shuffling,
13+
)
14+
1115
from eth.beacon.types.proposal_signed_data import (
12-
ProposalSignedData
16+
ProposalSignedData,
1317
)
1418

1519
from eth.beacon.types.slashable_vote_data import (
@@ -20,6 +24,7 @@
2024
AttestationData,
2125
)
2226

27+
from eth.beacon.types.states import BeaconState
2328
from eth.beacon.types.deposits import DepositData
2429
from eth.beacon.types.deposit_input import DepositInput
2530

@@ -46,7 +51,7 @@
4651

4752
@pytest.fixture(scope="session")
4853
def privkeys():
49-
return [int.from_bytes(hash_eth2(str(i).encode('utf-8'))[:4], 'big') for i in range(1000)]
54+
return [int.from_bytes(hash_eth2(str(i).encode('utf-8'))[:4], 'big') for i in range(50)]
5055

5156

5257
@pytest.fixture(scope="session")
@@ -134,8 +139,8 @@ def sample_beacon_state_params(sample_fork_data_params):
134139
'validator_registry_latest_change_slot': 10,
135140
'validator_registry_exit_count': 10,
136141
'validator_registry_delta_chain_tip': b'\x55' * 32,
137-
'randao_mix': b'\x55' * 32,
138-
'next_seed': b'\x55' * 32,
142+
'randao_mix': ZERO_HASH32,
143+
'next_seed': ZERO_HASH32,
139144
'shard_committees_at_slots': (),
140145
'persistent_committees': (),
141146
'persistent_committee_reassignments': (),
@@ -488,6 +493,27 @@ def max_exits():
488493
#
489494
# genesis
490495
#
496+
@pytest.fixture
497+
def genesis_state(sample_beacon_state_params,
498+
genesis_validators,
499+
epoch_length,
500+
target_committee_size,
501+
shard_count):
502+
initial_shuffling = get_new_shuffling(
503+
seed=ZERO_HASH32,
504+
validators=genesis_validators,
505+
crosslinking_start_shard=0,
506+
epoch_length=epoch_length,
507+
target_committee_size=target_committee_size,
508+
shard_count=shard_count
509+
)
510+
511+
return BeaconState(**sample_beacon_state_params).copy(
512+
validator_registry=genesis_validators,
513+
shard_committees_at_slots=initial_shuffling + initial_shuffling,
514+
)
515+
516+
491517
@pytest.fixture
492518
def genesis_validators(init_validator_keys,
493519
init_randao,

tests/beacon/state_machines/test_attestation_validation.py

Lines changed: 140 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,86 @@
11
import pytest
2+
from hypothesis import (
3+
given,
4+
settings,
5+
strategies as st,
6+
)
27

38
from eth_utils import (
49
ValidationError,
510
)
11+
12+
import rlp
13+
614
from eth.constants import (
715
ZERO_HASH32,
816
)
917

18+
from eth._utils import bls
19+
from eth._utils.bitfield import (
20+
get_empty_bitfield,
21+
)
22+
from eth.beacon._utils.hash import (
23+
hash_eth2,
24+
)
25+
from eth.beacon.aggregation import (
26+
aggregate_votes,
27+
)
28+
from eth.beacon.enums import (
29+
SignatureDomain,
30+
)
31+
from eth.beacon.helpers import (
32+
get_domain,
33+
)
1034
from eth.beacon.state_machines.forks.serenity.validation import (
35+
validate_serenity_attestation_aggregate_signature,
1136
validate_serenity_attestation_latest_crosslink_root,
1237
validate_serenity_attestation_justified_block_root,
1338
validate_serenity_attestation_justified_slot,
1439
validate_serenity_attestation_shard_block_root,
1540
validate_serenity_attestation_slot,
1641
)
17-
from eth.beacon.types.attestation_data import (
18-
AttestationData,
19-
)
42+
from eth.beacon.types.attestations import Attestation
43+
from eth.beacon.types.attestation_data import AttestationData
44+
45+
46+
def create_mock_signed_attestation(state,
47+
shard_committee,
48+
voting_committee_indices,
49+
attestation_data,
50+
privkeys):
51+
message = hash_eth2(
52+
rlp.encode(attestation_data) +
53+
(0).to_bytes(1, "big")
54+
)
55+
# participants sign message
56+
signatures = [
57+
bls.sign(
58+
message,
59+
privkeys[shard_committee.committee[committee_index]],
60+
domain=get_domain(
61+
fork_data=state.fork_data,
62+
slot=attestation_data.slot,
63+
domain_type=SignatureDomain.DOMAIN_ATTESTATION,
64+
)
65+
)
66+
for committee_index in voting_committee_indices
67+
]
68+
69+
# aggregate signatures and construct participant bitfield
70+
participation_bitfield, aggregate_signature = aggregate_votes(
71+
bitfield=get_empty_bitfield(len(shard_committee.committee)),
72+
sigs=(),
73+
voting_sigs=signatures,
74+
voting_committee_indices=voting_committee_indices,
75+
)
76+
77+
# create attestation from attestation_data, particpipant_bitfield, and signature
78+
return Attestation(
79+
data=attestation_data,
80+
participation_bitfield=participation_bitfield,
81+
custody_bitfield=b'',
82+
aggregate_signature=aggregate_signature,
83+
)
2084

2185

2286
@pytest.mark.parametrize(
@@ -151,7 +215,7 @@ def test_validate_serenity_attestation_justified_block_root(sample_attestation_d
151215
(
152216
'attestation_latest_crosslink_root,'
153217
'attestation_shard_block_root,'
154-
'latest_crosslink_shard_block_root,'
218+
'latest_crosslink_root,'
155219
'is_valid,'
156220
),
157221
[
@@ -165,7 +229,7 @@ def test_validate_serenity_attestation_justified_block_root(sample_attestation_d
165229
def test_validate_serenity_attestation_latest_crosslink_root(sample_attestation_data_params,
166230
attestation_latest_crosslink_root,
167231
attestation_shard_block_root,
168-
latest_crosslink_shard_block_root,
232+
latest_crosslink_root,
169233
is_valid):
170234
sample_attestation_data_params['latest_crosslink_root'] = attestation_latest_crosslink_root
171235
sample_attestation_data_params['shard_block_root'] = attestation_shard_block_root
@@ -177,13 +241,13 @@ def test_validate_serenity_attestation_latest_crosslink_root(sample_attestation_
177241
if is_valid:
178242
validate_serenity_attestation_latest_crosslink_root(
179243
attestation_data,
180-
latest_crosslink_shard_block_root,
244+
latest_crosslink_root,
181245
)
182246
else:
183247
with pytest.raises(ValidationError):
184248
validate_serenity_attestation_latest_crosslink_root(
185249
attestation_data,
186-
latest_crosslink_shard_block_root,
250+
latest_crosslink_root,
187251
)
188252

189253

@@ -216,5 +280,72 @@ def test_validate_serenity_attestation_shard_block_root(sample_attestation_data_
216280
)
217281

218282

219-
def test_validate_serenity_attestation_aggregate_signature():
220-
pass
283+
@settings(max_examples=1)
284+
@given(random=st.randoms())
285+
@pytest.mark.parametrize(
286+
(
287+
'num_validators,'
288+
'epoch_length,'
289+
'target_committee_size,'
290+
'shard_count,'
291+
'is_valid,'
292+
),
293+
[
294+
(10, 2, 2, 2, True),
295+
(40, 4, 3, 5, True),
296+
(20, 5, 3, 2, True),
297+
(20, 5, 3, 2, False),
298+
(10, 2, 3, 4, False),
299+
],
300+
)
301+
def test_validate_serenity_attestation_aggregate_signature(genesis_state,
302+
epoch_length,
303+
random,
304+
privkeys,
305+
sample_attestation_data_params,
306+
is_valid):
307+
state = genesis_state
308+
309+
# choose committee
310+
slot = 0
311+
shard_committee = state.shard_committees_at_slots[slot][0]
312+
committee_size = len(shard_committee.committee)
313+
314+
# randomly select 3/4 participants from committee
315+
votes_count = len(shard_committee.committee) * 3 // 4
316+
voting_committee_indices = random.sample(range(committee_size), votes_count)
317+
assert len(voting_committee_indices) > 0
318+
319+
attestation_data = AttestationData(**sample_attestation_data_params).copy(
320+
slot=slot,
321+
shard=shard_committee.shard,
322+
)
323+
324+
attestation = create_mock_signed_attestation(
325+
state,
326+
shard_committee,
327+
voting_committee_indices,
328+
attestation_data,
329+
privkeys,
330+
)
331+
332+
if is_valid:
333+
validate_serenity_attestation_aggregate_signature(
334+
state,
335+
attestation,
336+
epoch_length,
337+
)
338+
else:
339+
# mess up signature
340+
attestation = attestation.copy(
341+
aggregate_signature=(
342+
attestation.aggregate_signature[0] + 10,
343+
attestation.aggregate_signature[1] - 1
344+
)
345+
)
346+
with pytest.raises(ValidationError):
347+
validate_serenity_attestation_aggregate_signature(
348+
state,
349+
attestation,
350+
epoch_length,
351+
)

0 commit comments

Comments
 (0)