Skip to content

Commit ad421db

Browse files
committed
working through attestation validations
1 parent 59be468 commit ad421db

File tree

2 files changed

+281
-0
lines changed

2 files changed

+281
-0
lines changed
Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
from typing import (
2+
Sequence,
3+
)
4+
5+
from eth_typing import (
6+
Hash32
7+
)
8+
from eth_utils import (
9+
ValidationError,
10+
)
11+
12+
13+
from eth.beacon.helpers import (
14+
get_block_hash,
15+
)
16+
from eth.beacon.types.states import BeaconState # noqa: F401
17+
18+
19+
#
20+
# Attestation validation
21+
#
22+
def validate_attestation(state: BeaconState,
23+
attestation: 'AttestationRecord',
24+
epoch_length: int,
25+
min_attestation_inclusion_delay: int,
26+
is_validating_signatures: bool=True) -> None:
27+
"""
28+
Validate the given ``attestation``.
29+
Raise ``ValidationError`` if it's invalid.
30+
"""
31+
32+
validate_attestation_slot(
33+
attestation_data=attestation.data,
34+
current_slot=state.slot,
35+
epoch_length=epoch_length,
36+
min_attestation_inclusion_delay=min_attestation_inclusion_delay,
37+
)
38+
39+
validate_attestation_justified_slot(
40+
attestation_data=attestation.data,
41+
current_slot=state.slot,
42+
previous_justified_slot=state.previous_justified_slot,
43+
justified_slot=state.justified_slot,
44+
epoch_length=epoch_length,
45+
)
46+
47+
validate_attestation_justified_block_root(
48+
attestation_data=attestation.data,
49+
justified_block_root=get_block_hash(
50+
state.latest_block_hashes,
51+
current_slot=state.slot,
52+
slot=attestation.data.justified_slot,
53+
),
54+
)
55+
56+
validate_attestation_latest_crosslink_root(
57+
attestation,
58+
state.latest_crosslinks[attestation.data.shard].shard_block_root,
59+
)
60+
61+
if is_validating_signatures:
62+
validate_attestation_aggregate_signature(
63+
state,
64+
attestation,
65+
)
66+
67+
68+
def validate_attestation_slot(attestation_data: 'AttestationData',
69+
current_slot: int,
70+
epoch_length: int,
71+
min_attestation_inclusion_delay: int) -> None:
72+
"""
73+
Validate ``slot`` field of ``attestation_data``.
74+
Raise ``ValidationError`` if it's invalid.
75+
"""
76+
if attestation_data.slot + min_attestation_inclusion_delay > current_slot:
77+
raise ValidationError(
78+
"Attestation slot plus min inclusion delay is too high:\n"
79+
"\tFound: %s (%s + %s), Needed less than or equal to %s" %
80+
(
81+
attestation_data.slot + min_attestation_inclusion_delay,
82+
attestation_data.slot,
83+
min_attestation_inclusion_delay,
84+
current_slot,
85+
)
86+
)
87+
if attestation_data.slot + epoch_length < current_slot:
88+
raise ValidationError(
89+
"Attestation slot plus epoch length is too low:\n"
90+
"\tFound: %s (%s + %s), Needed greater than or equal to: %s" %
91+
(
92+
attestation_data.slot + epoch_length,
93+
attestation_data.slot,
94+
epoch_length,
95+
current_slot,
96+
)
97+
)
98+
99+
100+
def validate_attestation_justified_slot(attestation_data: 'AttestationData',
101+
current_slot: int,
102+
previous_justified_slot: int,
103+
justified_slot: int,
104+
epoch_length: int) -> None:
105+
if attestation_data.slot >= current_slot - (current_slot % epoch_length):
106+
if attestation_data.justified_slot != justified_slot:
107+
raise ValidationError(
108+
"Attestation slot is after recent epoch transition but "
109+
"is not targeting the justified slot:\n"
110+
"\tFound: %s, Expected %s" %
111+
(attestation_data.justified_slot, justified_slot)
112+
)
113+
else:
114+
if attestation_data.justified_slot != previous_justified_slot:
115+
raise ValidationError(
116+
"Attestation slot is before recent epoch transition but "
117+
"is not targeting the previous justified slot:\n"
118+
"\tFound: %s, Expected %s" %
119+
(attestation_data.justified_slot, previous_justified_slot)
120+
)
121+
122+
123+
def validate_attestation_justified_block_root(attestation_data: 'AttestationData',
124+
justified_block_root: Hash32) -> None:
125+
if attestation_data.justified_block_hash != justified_block_root:
126+
raise ValidationError(
127+
"Attestation justified block root is not equal to the block root at the "
128+
"justified slot:\n"
129+
"\tFound: %s, Expected %s at slot %s" %
130+
(
131+
attestation_data.justified_block_hash,
132+
justified_block_root,
133+
attestation_data.justified_slot,
134+
)
135+
)
136+
137+
138+
def validate_attestation_latest_crosslink_root(attestation_data: 'AttestationData',
139+
latest_crosslink_shard_block_root: Hash32) -> None:
140+
pass
141+
142+
143+
def validate_attestation_aggregate_signature(state: BeaconState,
144+
attestation: 'AttestationRecord') -> None:
145+
pass
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import pytest
2+
3+
from eth_utils import (
4+
ValidationError,
5+
)
6+
7+
from eth.beacon.state_machines.validation import (
8+
validate_attestation_slot,
9+
validate_attestation_justified_block_root,
10+
validate_attestation_justified_slot,
11+
)
12+
from eth.beacon.types.attestation_data import (
13+
AttestationData,
14+
)
15+
16+
17+
@pytest.mark.parametrize(
18+
(
19+
'attestation_slot,current_slot,epoch_length,'
20+
'min_attestation_inclusion_delay,is_valid'
21+
),
22+
[
23+
(0, 5, 5, 1, True),
24+
(0, 5, 5, 5, True),
25+
(0, 5, 5, 6, False), # not past min inclusion delay
26+
(7, 5, 10, 1, False), # attestation slot in future
27+
(10, 20, 10, 2, True),
28+
(9, 20, 10, 2, False), # more than epoch_length slots have past
29+
]
30+
)
31+
def test_validate_attestation_slot(sample_attestation_data_params,
32+
attestation_slot,
33+
current_slot,
34+
epoch_length,
35+
min_attestation_inclusion_delay,
36+
is_valid):
37+
sample_attestation_data_params['slot'] = attestation_slot
38+
attestation_data = AttestationData(**sample_attestation_data_params)
39+
40+
if is_valid:
41+
validate_attestation_slot(
42+
attestation_data,
43+
current_slot,
44+
epoch_length,
45+
min_attestation_inclusion_delay,
46+
)
47+
else:
48+
with pytest.raises(ValidationError):
49+
validate_attestation_slot(
50+
attestation_data,
51+
current_slot,
52+
epoch_length,
53+
min_attestation_inclusion_delay,
54+
)
55+
56+
57+
@pytest.mark.parametrize(
58+
(
59+
'attestation_slot,'
60+
'attestation_justified_slot,'
61+
'current_slot,'
62+
'previous_justified_slot,'
63+
'justified_slot,'
64+
'epoch_length,'
65+
'is_valid'
66+
),
67+
[
68+
(13, 5, 14, 0, 5, 5, True),
69+
(13, 0, 14, 0, 5, 5, False), # targeting previous but should be targeting current
70+
(13, 20, 14, 0, 5, 5, False), # targeting future slot but should be targeting current
71+
(29, 10, 30, 10, 20, 10, True),
72+
(29, 20, 30, 10, 20, 10, False), # targeting current but should be targeting previous
73+
(29, 36, 30, 10, 20, 10, False), # targeting future slot but should be targeting previous
74+
(10, 10, 10, 10, 10, 10, True),
75+
]
76+
)
77+
def test_validate_attestation_justified_slot(sample_attestation_data_params,
78+
attestation_slot,
79+
attestation_justified_slot,
80+
current_slot,
81+
previous_justified_slot,
82+
justified_slot,
83+
epoch_length,
84+
is_valid):
85+
sample_attestation_data_params['slot'] = attestation_slot
86+
sample_attestation_data_params['justified_slot'] = attestation_justified_slot
87+
attestation_data = AttestationData(**sample_attestation_data_params)
88+
89+
if is_valid:
90+
validate_attestation_justified_slot(
91+
attestation_data,
92+
current_slot,
93+
previous_justified_slot,
94+
justified_slot,
95+
epoch_length,
96+
)
97+
else:
98+
with pytest.raises(ValidationError):
99+
validate_attestation_justified_slot(
100+
attestation_data,
101+
current_slot,
102+
previous_justified_slot,
103+
justified_slot,
104+
epoch_length,
105+
)
106+
107+
108+
@pytest.mark.parametrize(
109+
(
110+
'attestation_justified_block_root,'
111+
'justified_block_root,'
112+
'is_valid'
113+
),
114+
[
115+
(b'\x42' * 32, b'\x35' * 32, False),
116+
(b'\x42' * 32, b'\x42' * 32, True),
117+
]
118+
)
119+
def test_validate_attestation_justified_block_root(sample_attestation_data_params,
120+
attestation_justified_block_root,
121+
justified_block_root,
122+
is_valid):
123+
sample_attestation_data_params['justified_block_hash'] = attestation_justified_block_root
124+
attestation_data = AttestationData(**sample_attestation_data_params)
125+
126+
if is_valid:
127+
validate_attestation_justified_block_root(
128+
attestation_data,
129+
justified_block_root
130+
)
131+
else:
132+
with pytest.raises(ValidationError):
133+
validate_attestation_justified_block_root(
134+
attestation_data,
135+
justified_block_root
136+
)

0 commit comments

Comments
 (0)