Skip to content

Commit 0fe34a3

Browse files
committed
add sanity check test for process_attestations
1 parent b60b560 commit 0fe34a3

File tree

8 files changed

+218
-95
lines changed

8 files changed

+218
-95
lines changed

eth/beacon/helpers.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ def get_block_root(
8484
"""
8585
Returns the block root at a recent ``slot``.
8686
"""
87-
slot_relative_position = current_slot - len(latest_block_roots)
87+
slot_relative_position = max(current_slot - len(latest_block_roots), 0)
8888
return _get_element_from_recent_list(
8989
latest_block_roots,
9090
slot,
@@ -109,7 +109,7 @@ def _get_shard_committees_at_slot(
109109
)
110110
)
111111

112-
slot_relative_position = state_slot - epoch_length
112+
slot_relative_position = max(state_slot - epoch_length, 0)
113113

114114
yield from _get_element_from_recent_list(
115115
shard_committees_at_slots,

eth/beacon/state_machines/base.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
)
1313

1414
from eth.beacon.db.chain import BaseBeaconChainDB
15-
from eth.beacon.types.attestations import Attestation
1615
from eth.beacon.types.blocks import BaseBeaconBlock
1716
from eth.beacon.types.states import BeaconState
1817

@@ -64,15 +63,6 @@ def state_transition(self) -> BaseStateTransition:
6463
def import_block(self, block: BaseBeaconBlock) -> Tuple[BeaconState, BaseBeaconBlock]:
6564
pass
6665

67-
#
68-
# State transition
69-
#
70-
@abstractmethod
71-
def validate_attestation(self,
72-
attestation: Attestation,
73-
is_validating_signatures: bool=True) -> None:
74-
raise NotImplementedError
75-
7666

7767
class BeaconStateMachine(BaseBeaconStateMachine):
7868
def __init__(self,
Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
from typing import Type # noqa: F401
22

3-
from eth.beacon.types.attestations import Attestation
43
from eth.beacon.types.blocks import BaseBeaconBlock # noqa: F401
54
from eth.beacon.types.states import BeaconState # noqa: F401
65

@@ -11,7 +10,6 @@
1110
from .blocks import SerenityBeaconBlock
1211
from .states import SerenityBeaconState
1312
from .state_transitions import SerenityStateTransition
14-
from .validation import validate_serenity_attestation
1513

1614

1715
class SerenityStateMachine(BeaconStateMachine):
@@ -23,14 +21,3 @@ class SerenityStateMachine(BeaconStateMachine):
2321
state_class = SerenityBeaconState # type: Type[BeaconState]
2422
state_transition_class = SerenityStateTransition # type: Type[BaseStateTransition]
2523
config = SERENITY_CONFIG
26-
27-
def validate_attestation(self,
28-
attestation: Attestation,
29-
is_validating_signatures: bool=True) -> None:
30-
validate_serenity_attestation(
31-
self.state,
32-
attestation,
33-
self.config.EPOCH_LENGTH,
34-
self.config.MIN_ATTESTATION_INCLUSION_DELAY,
35-
is_validating_signatures,
36-
)
Lines changed: 36 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,46 @@
11
from eth.beacon.types.blocks import BaseBeaconBlock
2+
from eth.beacon.types.pending_attestation_records import PendingAttestationRecord
23
from eth.beacon.types.states import BeaconState
34
from eth.beacon.state_machines.configs import BeaconConfig
45

6+
from .validation import (
7+
validate_serenity_attestation,
8+
)
9+
510

611
def process_attestations(state: BeaconState,
712
block: BaseBeaconBlock,
8-
config: BeaconConfig) -> BeaconState:
9-
# TODO
10-
# It's just for demo!!!
13+
config: BeaconConfig,
14+
is_validating_signatures: bool=True) -> BeaconState:
15+
"""
16+
Implements 'per-block-processing.operations.attestations' portion of Phase 0 spec:
17+
https://github.com/ethereum/eth2.0-specs/blob/master/specs/core/0_beacon-chain.md#attestations-1
18+
19+
Validate the ``attestations`` contained within the ``block`` in the context of ``state``.
20+
If any invalid, throw ``ValidationError``.
21+
Otherwise, append an ``PendingAttestationRecords`` for each to ``latest_attestations``.
22+
Return resulting ``state``.
23+
"""
24+
for attestation in block.body.attestations:
25+
validate_serenity_attestation(
26+
state,
27+
attestation,
28+
config.EPOCH_LENGTH,
29+
config.MIN_ATTESTATION_INCLUSION_DELAY,
30+
is_validating_signatures,
31+
)
32+
33+
# update_latest_attestations
34+
additional_pending_attestations = tuple(
35+
PendingAttestationRecord(
36+
data=attestation.data,
37+
participation_bitfield=attestation.participation_bitfield,
38+
custody_bitfield=attestation.custody_bitfield,
39+
slot_included=state.slot,
40+
)
41+
for attestation in block.body.attestations
42+
)
1143
state = state.copy(
12-
slot=config.ZERO_BALANCE_VALIDATOR_TTL,
44+
latest_attestations=state.latest_attestations + additional_pending_attestations
1345
)
1446
return state

eth/beacon/state_machines/state_transitions.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
Configurable,
88
)
99

10+
from eth.beacon.types.attestations import Attestation
1011
from eth.beacon.types.blocks import BaseBeaconBlock
1112
from eth.beacon.types.states import BeaconState
1213

@@ -34,3 +35,12 @@ def per_block_transition(self, state: BeaconState, block: BaseBeaconBlock) -> Be
3435
@abstractmethod
3536
def per_epoch_transition(self, state: BeaconState, block: BaseBeaconBlock) -> BeaconState:
3637
pass
38+
39+
#
40+
# Operation validations
41+
#
42+
@abstractmethod
43+
def validate_attestation(self,
44+
attestation: Attestation,
45+
is_validating_signatures: bool=True) -> None:
46+
raise NotImplementedError

tests/beacon/conftest.py

Lines changed: 75 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
import pytest
2+
import rlp
23

34
from eth_utils import denoms
45

56
from eth.constants import (
67
ZERO_HASH32,
78
)
9+
810
import eth._utils.bls as bls
911
from eth.beacon._utils.hash import hash_eth2
12+
from eth._utils.bitfield import (
13+
get_empty_bitfield,
14+
)
15+
from eth.beacon.aggregation import (
16+
aggregate_votes,
17+
)
18+
from eth.beacon.enums import (
19+
SignatureDomain,
20+
)
21+
from eth.beacon.helpers import (
22+
get_domain,
23+
)
1024

1125
from eth.beacon.helpers import (
1226
get_new_shuffling,
@@ -20,11 +34,10 @@
2034
SlashableVoteData,
2135
)
2236

23-
from eth.beacon.types.attestation_data import (
24-
AttestationData,
25-
)
26-
37+
from eth.beacon.types.attestation_data import AttestationData
38+
from eth.beacon.types.attestations import Attestation
2739
from eth.beacon.types.states import BeaconState
40+
from eth.beacon.types.crosslink_records import CrosslinkRecord
2841
from eth.beacon.types.deposits import DepositData
2942
from eth.beacon.types.deposit_input import DepositInput
3043

@@ -498,7 +511,9 @@ def genesis_state(sample_beacon_state_params,
498511
genesis_validators,
499512
epoch_length,
500513
target_committee_size,
501-
shard_count):
514+
initial_slot_number,
515+
shard_count,
516+
latest_block_roots_length):
502517
initial_shuffling = get_new_shuffling(
503518
seed=ZERO_HASH32,
504519
validators=genesis_validators,
@@ -511,6 +526,14 @@ def genesis_state(sample_beacon_state_params,
511526
return BeaconState(**sample_beacon_state_params).copy(
512527
validator_registry=genesis_validators,
513528
shard_committees_at_slots=initial_shuffling + initial_shuffling,
529+
latest_block_roots=tuple(ZERO_HASH32 for _ in range(latest_block_roots_length)),
530+
latest_crosslinks=tuple(
531+
CrosslinkRecord(
532+
slot=initial_slot_number,
533+
shard_block_root=ZERO_HASH32,
534+
)
535+
for _ in range(shard_count)
536+
)
514537
)
515538

516539

@@ -530,3 +553,50 @@ def genesis_validators(init_validator_keys,
530553
exit_count=0,
531554
) for pub in init_validator_keys
532555
)
556+
557+
558+
#
559+
# Create mock consensus objects
560+
#
561+
@pytest.fixture
562+
def create_mock_signed_attestation(privkeys):
563+
def create_mock_signed_attestation(state,
564+
shard_committee,
565+
voting_committee_indices,
566+
attestation_data):
567+
message = hash_eth2(
568+
rlp.encode(attestation_data) +
569+
(0).to_bytes(1, "big")
570+
)
571+
# participants sign message
572+
signatures = [
573+
bls.sign(
574+
message,
575+
privkeys[shard_committee.committee[committee_index]],
576+
domain=get_domain(
577+
fork_data=state.fork_data,
578+
slot=attestation_data.slot,
579+
domain_type=SignatureDomain.DOMAIN_ATTESTATION,
580+
)
581+
)
582+
for committee_index in voting_committee_indices
583+
]
584+
585+
# aggregate signatures and construct participant bitfield
586+
participation_bitfield, aggregate_signature = aggregate_votes(
587+
bitfield=get_empty_bitfield(len(shard_committee.committee)),
588+
sigs=(),
589+
voting_sigs=signatures,
590+
voting_committee_indices=voting_committee_indices,
591+
)
592+
593+
# create attestation from attestation_data, particpipant_bitfield, and signature
594+
return Attestation(
595+
data=attestation_data,
596+
participation_bitfield=participation_bitfield,
597+
custody_bitfield=b'',
598+
aggregate_signature=aggregate_signature,
599+
)
600+
601+
return create_mock_signed_attestation
602+

tests/beacon/state_machines/test_attestation_validation.py

Lines changed: 2 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -9,28 +9,10 @@
99
ValidationError,
1010
)
1111

12-
import rlp
13-
1412
from eth.constants import (
1513
ZERO_HASH32,
1614
)
1715

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-
)
3416
from eth.beacon.state_machines.forks.serenity.validation import (
3517
validate_serenity_attestation_aggregate_signature,
3618
validate_serenity_attestation_latest_crosslink_root,
@@ -39,50 +21,9 @@
3921
validate_serenity_attestation_shard_block_root,
4022
validate_serenity_attestation_slot,
4123
)
42-
from eth.beacon.types.attestations import Attestation
4324
from eth.beacon.types.attestation_data import AttestationData
4425

4526

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-
)
84-
85-
8627
@pytest.mark.parametrize(
8728
(
8829
'attestation_slot,'
@@ -301,8 +242,9 @@ def test_validate_serenity_attestation_shard_block_root(sample_attestation_data_
301242
def test_validate_serenity_attestation_aggregate_signature(genesis_state,
302243
epoch_length,
303244
random,
304-
privkeys,
305245
sample_attestation_data_params,
246+
create_mock_signed_attestation,
247+
config,
306248
is_valid):
307249
state = genesis_state
308250

@@ -326,7 +268,6 @@ def test_validate_serenity_attestation_aggregate_signature(genesis_state,
326268
shard_committee,
327269
voting_committee_indices,
328270
attestation_data,
329-
privkeys,
330271
)
331272

332273
if is_valid:

0 commit comments

Comments
 (0)