1
+ import functools
1
2
from itertools import (
2
3
repeat ,
3
4
)
5
+
6
+ from cytoolz import (
7
+ pipe
8
+ )
4
9
from typing import (
5
10
Any ,
6
11
Iterable ,
17
22
Hash32 ,
18
23
)
19
24
25
+ from eth .utils import bls
26
+ from eth .utils .bitfield import (
27
+ set_voted ,
28
+ )
29
+ from eth .utils .blake import blake
30
+ from eth .utils .numeric import (
31
+ clamp ,
32
+ )
33
+
20
34
from eth .beacon .block_committees_info import BlockCommitteesInfo
21
35
from eth .beacon .types .shard_and_committees import (
22
36
ShardAndCommittee ,
@@ -220,18 +234,6 @@ def get_active_validator_indices(dynasty: int,
220
234
#
221
235
# Shuffling
222
236
#
223
- def clamp (minval : int , x : int , maxval : int ) -> int :
224
- """
225
- Bound the given ``x`` between ``minval`` and ``maxval``
226
- """
227
- if x <= minval :
228
- return minval
229
- elif x >= maxval :
230
- return maxval
231
- else :
232
- return x
233
-
234
-
235
237
def _get_shuffling_committee_slot_portions (
236
238
active_validators_size : int ,
237
239
cycle_length : int ,
@@ -333,8 +335,8 @@ def get_new_shuffling(*,
333
335
active_validators_size = len (active_validators )
334
336
committees_per_slot = clamp (
335
337
1 ,
338
+ shard_count // cycle_length ,
336
339
active_validators_size // cycle_length // (min_committee_size * 2 ) + 1 ,
337
- shard_count // cycle_length
338
340
)
339
341
shuffled_active_validator_indices = shuffle (active_validators , seed )
340
342
@@ -365,12 +367,13 @@ def get_block_committees_info(parent_block: 'BaseBeaconBlock',
365
367
"""
366
368
Returns the proposer index in committee and the ``shard_id``.
367
369
"""
368
- if len (shards_and_committees ) <= 0 :
369
- raise ValueError ("shards_and_committees should not be empty." )
370
-
371
370
# `proposer_index_in_committee` th attester in `shard_and_committee`
372
371
# is the proposer of the parent block.
373
- shard_and_committee = shards_and_committees [0 ]
372
+ try :
373
+ shard_and_committee = shards_and_committees [0 ]
374
+ except IndexError :
375
+ raise ValueError ("shards_and_committees should not be empty." )
376
+
374
377
proposer_committee_size = len (shard_and_committee .committee )
375
378
if proposer_committee_size <= 0 :
376
379
raise ValueError (
@@ -392,3 +395,89 @@ def get_block_committees_info(parent_block: 'BaseBeaconBlock',
392
395
proposer_committee_size = proposer_committee_size ,
393
396
shards_and_committees = shards_and_committees ,
394
397
)
398
+
399
+
400
+ #
401
+ # Signatures and Aggregation
402
+ #
403
+ def create_signing_message (slot : int ,
404
+ parent_hashes : Iterable [Hash32 ],
405
+ shard_id : int ,
406
+ shard_block_hash : Hash32 ,
407
+ justified_slot : int ) -> bytes :
408
+ # TODO: will be updated to hashed encoded attestation
409
+ return blake (
410
+ slot .to_bytes (8 , byteorder = 'big' ) +
411
+ b'' .join (parent_hashes ) +
412
+ shard_id .to_bytes (2 , byteorder = 'big' ) +
413
+ shard_block_hash +
414
+ justified_slot .to_bytes (8 , 'big' )
415
+ )
416
+
417
+
418
+ def aggregate_attestation_record (last_justified_slot : int ,
419
+ recent_block_hashes : Iterable [Hash32 ],
420
+ block : 'BaseBeaconBlock' ,
421
+ votes : Iterable [Tuple [int , bytes , int ]],
422
+ proposer_attestation : 'AttestationRecord' ,
423
+ cycle_length : int ) -> 'AttestationRecord' :
424
+ """
425
+ Aggregate the votes.
426
+
427
+ TODO: Write tests
428
+ """
429
+ # Get signing message
430
+ parent_hashes = get_hashes_to_sign (
431
+ recent_block_hashes ,
432
+ block ,
433
+ cycle_length ,
434
+ )
435
+ message = create_signing_message (
436
+ block .slot_number ,
437
+ parent_hashes ,
438
+ proposer_attestation .shard_id ,
439
+ block .shard_block_hash ,
440
+ last_justified_slot ,
441
+ )
442
+
443
+ bitfield , sigs = aggregate_votes (
444
+ message ,
445
+ votes ,
446
+ proposer_attestation .bitfield ,
447
+ proposer_attestation .sigs ,
448
+ )
449
+
450
+ return proposer_attestation .copy (
451
+ bitfield = bitfield ,
452
+ sigs = bls .aggregate_sigs (sigs ),
453
+ )
454
+
455
+
456
+ def aggregate_votes (message : bytes ,
457
+ votes : Iterable [Tuple [int , bytes , int ]],
458
+ pre_bitfield : bytes ,
459
+ pre_sigs : Iterable [bytes ]) -> Tuple [bytes , Iterable [int ]]:
460
+ # Update the bitfield and append the signatures
461
+ sigs_with_committe_info = tuple (
462
+ (sig , committee_index )
463
+ for (committee_index , sig , public_key )
464
+ in votes
465
+ if bls .verify (message , public_key , sig )
466
+ )
467
+ try :
468
+ sigs , committee_indices = zip (* sigs_with_committe_info )
469
+ except ValueError :
470
+ sigs = ()
471
+ committee_indices = ()
472
+
473
+ sigs = sigs + tuple (pre_sigs )
474
+ bitfield = pre_bitfield
475
+ bitfield = pipe (
476
+ bitfield ,
477
+ * (
478
+ functools .partial (set_voted , index = committee_index )
479
+ for committee_index in committee_indices
480
+ )
481
+ )
482
+
483
+ return bitfield , bls .aggregate_sigs (sigs )
0 commit comments