Skip to content

Commit 8f74e6b

Browse files
authored
Merge pull request #1390 from hwwhww/attestation_validation
Per block transition and attestation
2 parents 128158c + 070a744 commit 8f74e6b

19 files changed

+1505
-78
lines changed

eth/beacon/aggregation.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from typing import (
2+
Iterable,
3+
Tuple,
4+
)
5+
from cytoolz import (
6+
pipe
7+
)
8+
9+
10+
from eth_typing import (
11+
Hash32,
12+
)
13+
14+
from eth.utils import bls
15+
from eth.utils.bitfield import (
16+
set_voted,
17+
)
18+
from eth.utils.blake import blake
19+
20+
21+
def create_signing_message(slot: int,
22+
parent_hashes: Iterable[Hash32],
23+
shard_id: int,
24+
shard_block_hash: Hash32,
25+
justified_slot: int) -> bytes:
26+
"""
27+
Return the signining message for attesting.
28+
"""
29+
# TODO: Will be updated with SSZ encoded attestation.
30+
return blake(
31+
slot.to_bytes(8, byteorder='big') +
32+
b''.join(parent_hashes) +
33+
shard_id.to_bytes(2, byteorder='big') +
34+
shard_block_hash +
35+
justified_slot.to_bytes(8, 'big')
36+
)
37+
38+
39+
def verify_votes(
40+
message: bytes,
41+
votes: Iterable[Tuple[int, bytes, int]]) -> Tuple[Tuple[bytes, ...], Tuple[int, ...]]:
42+
"""
43+
Verify the given votes.
44+
45+
vote: (committee_index, sig, public_key)
46+
"""
47+
sigs_with_committe_info = tuple(
48+
(sig, committee_index)
49+
for (committee_index, sig, public_key)
50+
in votes
51+
if bls.verify(message, public_key, sig)
52+
)
53+
try:
54+
sigs, committee_indices = zip(*sigs_with_committe_info)
55+
except ValueError:
56+
sigs = tuple()
57+
committee_indices = tuple()
58+
59+
return sigs, committee_indices
60+
61+
62+
def aggregate_votes(bitfield: bytes,
63+
sigs: Iterable[bytes],
64+
voting_sigs: Iterable[bytes],
65+
voting_committee_indices: Iterable[int]) -> Tuple[bytes, Tuple[int]]:
66+
"""
67+
Aggregate the votes.
68+
"""
69+
# Update the bitfield and append the signatures
70+
sigs = tuple(sigs) + tuple(voting_sigs)
71+
bitfield = pipe(
72+
bitfield,
73+
*(
74+
set_voted(index=committee_index)
75+
for committee_index in voting_committee_indices
76+
)
77+
)
78+
79+
return bitfield, bls.aggregate_sigs(sigs)

eth/beacon/block_committees_info.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from typing import (
2+
NamedTuple,
3+
Tuple,
4+
TYPE_CHECKING,
5+
)
6+
7+
if TYPE_CHECKING:
8+
from eth.beacon.types.shard_and_committees import ShardAndCommittee # noqa: F401
9+
10+
11+
BlockCommitteesInfo = NamedTuple(
12+
'BlockCommitteesInfo',
13+
(
14+
('proposer_index', int),
15+
('proposer_index_in_committee', int),
16+
('proposer_shard_id', int),
17+
('proposer_committee_size', int),
18+
('shards_and_committees', Tuple['ShardAndCommittee'])
19+
)
20+
)

eth/beacon/block_proposal.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from typing import (
2+
NamedTuple,
3+
Tuple,
4+
TYPE_CHECKING,
5+
)
6+
7+
from eth.constants import (
8+
Hash32,
9+
)
10+
11+
if TYPE_CHECKING:
12+
from eth.beacon.types.blocks import AttestationRecord # noqa: F401
13+
from eth.beacon.types.blocks import BaseBeaconBlock # noqa: F401
14+
15+
16+
BlockProposal = NamedTuple(
17+
'BlockProposal',
18+
(
19+
('block', 'BaseBeaconBlock'),
20+
('shard_id', int),
21+
('shard_block_hash', Tuple[Hash32]),
22+
)
23+
)

eth/beacon/db/chain.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ def get_canonical_block_hash(self, slot: int) -> Hash32:
7272
def get_canonical_block_by_slot(self, slot: int) -> BaseBeaconBlock:
7373
pass
7474

75+
@abstractmethod
76+
def get_canonical_block_hash_by_slot(self, slot: int) -> Hash32:
77+
pass
78+
7579
@abstractmethod
7680
def get_canonical_head(self) -> BaseBeaconBlock:
7781
pass
@@ -195,7 +199,7 @@ def _get_canonical_block_hash(db: BaseDB, slot: int) -> Hash32:
195199

196200
def get_canonical_block_by_slot(self, slot: int) -> BaseBeaconBlock:
197201
"""
198-
Return the block header with the given slot in the canonical chain.
202+
Return the block with the given slot in the canonical chain.
199203
200204
Raise BlockNotFound if there's no block with the given slot in the
201205
canonical chain.
@@ -207,10 +211,26 @@ def _get_canonical_block_by_slot(
207211
cls,
208212
db: BaseDB,
209213
slot: int) -> BaseBeaconBlock:
210-
validate_slot(slot)
211-
canonical_block_hash = cls._get_canonical_block_hash(db, slot)
214+
canonical_block_hash = cls._get_canonical_block_hash_by_slot(db, slot)
212215
return cls._get_block_by_hash(db, canonical_block_hash)
213216

217+
def get_canonical_block_hash_by_slot(self, slot: int) -> Hash32:
218+
"""
219+
Return the block hash with the given slot in the canonical chain.
220+
221+
Raise BlockNotFound if there's no block with the given slot in the
222+
canonical chain.
223+
"""
224+
return self._get_canonical_block_hash_by_slot(self.db, slot)
225+
226+
@classmethod
227+
def _get_canonical_block_hash_by_slot(
228+
cls,
229+
db: BaseDB,
230+
slot: int) -> Hash32:
231+
validate_slot(slot)
232+
return cls._get_canonical_block_hash(db, slot)
233+
214234
def get_canonical_head(self) -> BaseBeaconBlock:
215235
"""
216236
Return the current block at the head of the chain.

eth/beacon/helpers.py

Lines changed: 39 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from itertools import (
22
repeat,
33
)
4+
45
from typing import (
56
Any,
67
Iterable,
@@ -17,9 +18,11 @@
1718
Hash32,
1819
)
1920

20-
from eth.utils.blake import (
21-
blake,
21+
from eth.utils.numeric import (
22+
clamp,
2223
)
24+
25+
from eth.beacon.block_committees_info import BlockCommitteesInfo
2326
from eth.beacon.types.shard_and_committees import (
2427
ShardAndCommittee,
2528
)
@@ -42,7 +45,7 @@ def _get_element_from_recent_list(
4245
target_slot: int,
4346
slot_relative_position: int) -> Any:
4447
"""
45-
Returns the element from ``target_list`` by the ``target_slot`` number,
48+
Return the element from ``target_list`` by the ``target_slot`` number,
4649
where the the element should be at ``target_slot - slot_relative_position``th
4750
element of the given ``target_list``.
4851
"""
@@ -72,7 +75,7 @@ def get_block_hash(
7275
slot: int,
7376
cycle_length: int) -> Hash32:
7477
"""
75-
Returns the blockhash from ``ActiveState.recent_block_hashes`` by
78+
Return the blockhash from ``ActiveState.recent_block_hashes`` by
7679
``current_block_slot_number``.
7780
"""
7881
if len(recent_block_hashes) != cycle_length * 2:
@@ -125,7 +128,7 @@ def get_hashes_to_sign(recent_block_hashes: Sequence[Hash32],
125128
to_slot=block.slot_number - 1,
126129
cycle_length=cycle_length,
127130
)
128-
yield blake(block.hash)
131+
yield block.hash
129132

130133

131134
@to_tuple
@@ -189,7 +192,7 @@ def get_attestation_indices(crystallized_state: 'CrystallizedState',
189192
attestation: 'AttestationRecord',
190193
cycle_length: int) -> Iterable[int]:
191194
"""
192-
Returns committee of the given attestation.
195+
Return committee of the given attestation.
193196
"""
194197
shard_id = attestation.shard_id
195198

@@ -228,7 +231,7 @@ def _get_shuffling_committee_slot_portions(
228231
min_committee_size: int,
229232
shard_count: int) -> Tuple[int, int]:
230233
"""
231-
Returns committees number per slot and slots number per committee.
234+
Return committees number per slot and slots number per committee.
232235
"""
233236
# If there are enough active validators to form committees for every slot
234237
if active_validators_size >= cycle_length * min_committee_size:
@@ -276,7 +279,7 @@ def get_new_shuffling(*,
276279
min_committee_size: int,
277280
shard_count: int) -> Iterable[Iterable[ShardAndCommittee]]:
278281
"""
279-
Returns shuffled ``shard_and_committee_for_slots`` (``[[ShardAndCommittee]]``) of
282+
Return shuffled ``shard_and_committee_for_slots`` (``[[ShardAndCommittee]]``) of
280283
the given active ``validators``.
281284
282285
Two-dimensional:
@@ -321,24 +324,19 @@ def get_new_shuffling(*,
321324
"""
322325
active_validators = get_active_validator_indices(dynasty, validators)
323326
active_validators_size = len(active_validators)
324-
325-
committees_per_slot, slots_per_committee = _get_shuffling_committee_slot_portions(
326-
active_validators_size,
327-
cycle_length,
328-
min_committee_size,
329-
shard_count,
327+
committees_per_slot = clamp(
328+
1,
329+
shard_count // cycle_length,
330+
active_validators_size // cycle_length // (min_committee_size * 2) + 1,
330331
)
331-
332332
shuffled_active_validator_indices = shuffle(active_validators, seed)
333333

334334
# Split the shuffled list into cycle_length pieces
335335
validators_per_slot = split(shuffled_active_validator_indices, cycle_length)
336-
for slot, slot_indices in enumerate(validators_per_slot):
336+
for index, slot_indices in enumerate(validators_per_slot):
337337
# Split the shuffled list into committees_per_slot pieces
338338
shard_indices = split(slot_indices, committees_per_slot)
339-
shard_id_start = crosslinking_start_shard + (
340-
slot * committees_per_slot // slots_per_committee
341-
)
339+
shard_id_start = crosslinking_start_shard + index * committees_per_slot
342340
yield _get_shards_and_committees_for_shard_indices(
343341
shard_indices,
344342
shard_id_start,
@@ -349,31 +347,42 @@ def get_new_shuffling(*,
349347
#
350348
# Get proposer postition
351349
#
352-
def get_proposer_position(parent_block: 'BaseBeaconBlock',
353-
crystallized_state: 'CrystallizedState',
354-
cycle_length: int) -> Tuple[int, int]:
350+
def get_block_committees_info(parent_block: 'BaseBeaconBlock',
351+
crystallized_state: 'CrystallizedState',
352+
cycle_length: int) -> BlockCommitteesInfo:
355353
shards_and_committees = get_shards_and_committees_for_slot(
356354
crystallized_state,
357355
parent_block.slot_number,
358356
cycle_length,
359357
)
360358
"""
361-
Returns the proposer index in committee and the ``shard_id``.
359+
Return the block committees and proposer info with BlockCommitteesInfo pack.
362360
"""
363-
if len(shards_and_committees) <= 0:
364-
raise ValueError("shards_and_committees should not be empty.")
365-
366361
# `proposer_index_in_committee` th attester in `shard_and_committee`
367362
# is the proposer of the parent block.
368-
shard_and_committee = shards_and_committees[0]
369-
if len(shard_and_committee.committee) <= 0:
363+
try:
364+
shard_and_committee = shards_and_committees[0]
365+
except IndexError:
366+
raise ValueError("shards_and_committees should not be empty.")
367+
368+
proposer_committee_size = len(shard_and_committee.committee)
369+
if proposer_committee_size <= 0:
370370
raise ValueError(
371371
"The first committee should not be empty"
372372
)
373373

374374
proposer_index_in_committee = (
375375
parent_block.slot_number %
376-
len(shard_and_committee.committee)
376+
proposer_committee_size
377377
)
378378

379-
return proposer_index_in_committee, shard_and_committee.shard_id
379+
# The index in CrystallizedState.validators
380+
proposer_index = shard_and_committee.committee[proposer_index_in_committee]
381+
382+
return BlockCommitteesInfo(
383+
proposer_index=proposer_index,
384+
proposer_index_in_committee=proposer_index_in_committee,
385+
proposer_shard_id=shard_and_committee.shard_id,
386+
proposer_committee_size=proposer_committee_size,
387+
shards_and_committees=shards_and_committees,
388+
)

0 commit comments

Comments
 (0)