Skip to content

Commit 7786c3a

Browse files
committed
add most attestation validations including tests
1 parent ad421db commit 7786c3a

File tree

2 files changed

+140
-20
lines changed

2 files changed

+140
-20
lines changed

eth/beacon/state_machines/validation.py

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,27 @@
1-
from typing import (
2-
Sequence,
3-
)
4-
51
from eth_typing import (
62
Hash32
73
)
84
from eth_utils import (
95
ValidationError,
106
)
7+
from eth.constants import (
8+
ZERO_HASH32,
9+
)
1110

1211

1312
from eth.beacon.helpers import (
1413
get_block_hash,
1514
)
1615
from eth.beacon.types.states import BeaconState # noqa: F401
16+
from eth.beacon.types.attestations import Attestation # noqa: F401
17+
from eth.beacon.types.attestation_data import AttestationData # noqa: F401
1718

1819

1920
#
2021
# Attestation validation
2122
#
2223
def validate_attestation(state: BeaconState,
23-
attestation: 'AttestationRecord',
24+
attestation: Attestation,
2425
epoch_length: int,
2526
min_attestation_inclusion_delay: int,
2627
is_validating_signatures: bool=True) -> None:
@@ -54,18 +55,22 @@ def validate_attestation(state: BeaconState,
5455
)
5556

5657
validate_attestation_latest_crosslink_root(
57-
attestation,
58-
state.latest_crosslinks[attestation.data.shard].shard_block_root,
58+
attestation_data=attestation.data,
59+
latest_crosslink_shard_block_root=(
60+
state.latest_crosslinks[attestation.data.shard].shard_block_root
61+
),
5962
)
6063

64+
validate_attestation_shard_block_root(attestation_data=attestation.data)
65+
6166
if is_validating_signatures:
6267
validate_attestation_aggregate_signature(
6368
state,
6469
attestation,
6570
)
6671

6772

68-
def validate_attestation_slot(attestation_data: 'AttestationData',
73+
def validate_attestation_slot(attestation_data: AttestationData,
6974
current_slot: int,
7075
epoch_length: int,
7176
min_attestation_inclusion_delay: int) -> None:
@@ -97,35 +102,43 @@ def validate_attestation_slot(attestation_data: 'AttestationData',
97102
)
98103

99104

100-
def validate_attestation_justified_slot(attestation_data: 'AttestationData',
105+
def validate_attestation_justified_slot(attestation_data: AttestationData,
101106
current_slot: int,
102107
previous_justified_slot: int,
103108
justified_slot: int,
104109
epoch_length: int) -> None:
110+
"""
111+
Validate ``justified_slot`` field of ``attestation_data``.
112+
Raise ``ValidationError`` if it's invalid.
113+
"""
105114
if attestation_data.slot >= current_slot - (current_slot % epoch_length):
106115
if attestation_data.justified_slot != justified_slot:
107116
raise ValidationError(
108-
"Attestation slot is after recent epoch transition but "
109-
"is not targeting the justified slot:\n"
117+
"Attestation ``slot`` is after recent epoch transition but attestation"
118+
"``justified_slot`` is not targeting the ``justified_slot``:\n"
110119
"\tFound: %s, Expected %s" %
111120
(attestation_data.justified_slot, justified_slot)
112121
)
113122
else:
114123
if attestation_data.justified_slot != previous_justified_slot:
115124
raise ValidationError(
116-
"Attestation slot is before recent epoch transition but "
117-
"is not targeting the previous justified slot:\n"
125+
"Attestation ``slot`` is before recent epoch transition but attestation"
126+
"``justified_slot`` is not targeting the ``previous_justified_slot:\n"
118127
"\tFound: %s, Expected %s" %
119128
(attestation_data.justified_slot, previous_justified_slot)
120129
)
121130

122131

123-
def validate_attestation_justified_block_root(attestation_data: 'AttestationData',
132+
def validate_attestation_justified_block_root(attestation_data: AttestationData,
124133
justified_block_root: Hash32) -> None:
134+
"""
135+
Validate ``justified_block_hash`` field of ``attestation_data``.
136+
Raise ``ValidationError`` if it's invalid.
137+
"""
125138
if attestation_data.justified_block_hash != justified_block_root:
126139
raise ValidationError(
127-
"Attestation justified block root is not equal to the block root at the "
128-
"justified slot:\n"
140+
"Attestation ``justified_block_hash`` is not equal to the "
141+
"``justified_block_root`` at the ``justified_slot``:\n"
129142
"\tFound: %s, Expected %s at slot %s" %
130143
(
131144
attestation_data.justified_block_hash,
@@ -135,11 +148,49 @@ def validate_attestation_justified_block_root(attestation_data: 'AttestationData
135148
)
136149

137150

138-
def validate_attestation_latest_crosslink_root(attestation_data: 'AttestationData',
151+
def validate_attestation_latest_crosslink_root(attestation_data: AttestationData,
139152
latest_crosslink_shard_block_root: Hash32) -> None:
140-
pass
153+
"""
154+
Validate that either the ``latest_crosslink_hash`` or ``shard_block_hash``
155+
field of ``attestation_data`` is the provided ``latest_crosslink_shard_block_root``.
156+
Raise ``ValidationError`` if it's invalid.
157+
"""
158+
acceptable_shard_block_roots = [
159+
attestation_data.latest_crosslink_hash,
160+
attestation_data.shard_block_hash,
161+
]
162+
if latest_crosslink_shard_block_root not in acceptable_shard_block_roots:
163+
raise ValidationError(
164+
"Neither the attestation ``latest_crosslink_hash`` nor the attestation "
165+
"``shard_block_hash`` are equal to the ``latest_crosslink_shard_block_root``.\n"
166+
"\tFound: %s and %s, Expected %s" %
167+
(
168+
attestation_data.latest_crosslink_hash,
169+
attestation_data.shard_block_hash,
170+
latest_crosslink_shard_block_root,
171+
)
172+
)
173+
174+
175+
def validate_attestation_shard_block_root(attestation_data: AttestationData) -> None:
176+
"""
177+
Validate ``shard_block_hash`` field of `attestation_data`.
178+
Raise ``ValidationError`` if it's invalid.
179+
180+
Note: This is the Phase 0 version of ``shard_block_hash`` validation.
181+
This is a built-in stub and will be changed in phase 1.
182+
"""
183+
if attestation_data.shard_block_hash != ZERO_HASH32:
184+
raise ValidationError(
185+
"Attestation ``shard_block_hash`` is not ZERO_HASH32.\n"
186+
"\tFound: %s, Expected %s" %
187+
(
188+
attestation_data.shard_block_hash,
189+
ZERO_HASH32,
190+
)
191+
)
141192

142193

143194
def validate_attestation_aggregate_signature(state: BeaconState,
144-
attestation: 'AttestationRecord') -> None:
195+
attestation: Attestation) -> None:
145196
pass

tests/beacon/state_machines/test_attestation_validation.py

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,16 @@
33
from eth_utils import (
44
ValidationError,
55
)
6+
from eth.constants import (
7+
ZERO_HASH32,
8+
)
69

710
from eth.beacon.state_machines.validation import (
8-
validate_attestation_slot,
11+
validate_attestation_latest_crosslink_root,
912
validate_attestation_justified_block_root,
1013
validate_attestation_justified_slot,
14+
validate_attestation_shard_block_root,
15+
validate_attestation_slot,
1116
)
1217
from eth.beacon.types.attestation_data import (
1318
AttestationData,
@@ -134,3 +139,67 @@ def test_validate_attestation_justified_block_root(sample_attestation_data_param
134139
attestation_data,
135140
justified_block_root
136141
)
142+
143+
144+
@pytest.mark.parametrize(
145+
(
146+
'attestation_latest_crosslink_hash,'
147+
'attestation_shard_block_hash,'
148+
'latest_crosslink_shard_block_root,'
149+
'is_valid'
150+
),
151+
[
152+
(b'\x66' * 32, b'\x42' * 32, b'\x35' * 32, False),
153+
(b'\x42' * 32, b'\x42' * 32, b'\x66' * 32, False),
154+
(b'\x66' * 32, b'\x42' * 32, b'\x42' * 32, True),
155+
(b'\x42' * 32, b'\x35' * 32, b'\x42' * 32, True),
156+
(b'\x42' * 32, b'\x42' * 32, b'\x42' * 32, True),
157+
]
158+
)
159+
def test_validate_attestation_latest_crosslink_root(sample_attestation_data_params,
160+
attestation_latest_crosslink_hash,
161+
attestation_shard_block_hash,
162+
latest_crosslink_shard_block_root,
163+
is_valid):
164+
sample_attestation_data_params['latest_crosslink_hash'] = attestation_latest_crosslink_hash
165+
sample_attestation_data_params['shard_block_hash'] = attestation_shard_block_hash
166+
attestation_data = AttestationData(**sample_attestation_data_params)
167+
168+
if is_valid:
169+
validate_attestation_latest_crosslink_root(
170+
attestation_data,
171+
latest_crosslink_shard_block_root,
172+
)
173+
else:
174+
with pytest.raises(ValidationError):
175+
validate_attestation_latest_crosslink_root(
176+
attestation_data,
177+
latest_crosslink_shard_block_root,
178+
)
179+
180+
181+
@pytest.mark.parametrize(
182+
(
183+
'attestation_shard_block_hash,is_valid'
184+
),
185+
[
186+
(ZERO_HASH32, True),
187+
(b'\x35' * 32, False),
188+
(b'\x66' * 32, False),
189+
]
190+
)
191+
def test_validate_attestation_shard_block_root(sample_attestation_data_params,
192+
attestation_shard_block_hash,
193+
is_valid):
194+
sample_attestation_data_params['shard_block_hash'] = attestation_shard_block_hash
195+
attestation_data = AttestationData(**sample_attestation_data_params)
196+
197+
if is_valid:
198+
validate_attestation_shard_block_root(
199+
attestation_data,
200+
)
201+
else:
202+
with pytest.raises(ValidationError):
203+
validate_attestation_shard_block_root(
204+
attestation_data,
205+
)

0 commit comments

Comments
 (0)