Skip to content
This repository was archived by the owner on Sep 8, 2025. It is now read-only.

Commit b60c113

Browse files
committed
PR feedback and move aggregation sig related functions into aggregation.py
1 parent 3077967 commit b60c113

File tree

6 files changed

+153
-166
lines changed

6 files changed

+153
-166
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/helpers.py

Lines changed: 0 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
repeat,
33
)
44

5-
from cytoolz import (
6-
pipe
7-
)
85
from typing import (
96
Any,
107
Iterable,
@@ -21,11 +18,6 @@
2118
Hash32,
2219
)
2320

24-
from eth.utils import bls
25-
from eth.utils.bitfield import (
26-
set_voted,
27-
)
28-
from eth.utils.blake import blake
2921
from eth.utils.numeric import (
3022
clamp,
3123
)
@@ -394,105 +386,3 @@ def get_block_committees_info(parent_block: 'BaseBeaconBlock',
394386
proposer_committee_size=proposer_committee_size,
395387
shards_and_committees=shards_and_committees,
396388
)
397-
398-
399-
#
400-
# Signatures and Aggregation
401-
#
402-
def create_signing_message(slot: int,
403-
parent_hashes: Iterable[Hash32],
404-
shard_id: int,
405-
shard_block_hash: Hash32,
406-
justified_slot: int) -> bytes:
407-
# TODO: will be updated to hashed encoded attestation
408-
return blake(
409-
slot.to_bytes(8, byteorder='big') +
410-
b''.join(parent_hashes) +
411-
shard_id.to_bytes(2, byteorder='big') +
412-
shard_block_hash +
413-
justified_slot.to_bytes(8, 'big')
414-
)
415-
416-
417-
def aggregate_attestation_record(last_justified_slot: int,
418-
recent_block_hashes: Iterable[Hash32],
419-
block: 'BaseBeaconBlock',
420-
votes: Iterable[Tuple[int, bytes, int]],
421-
proposer_attestation: 'AttestationRecord',
422-
cycle_length: int) -> 'AttestationRecord':
423-
"""
424-
Aggregate the votes.
425-
426-
TODO: Write tests
427-
"""
428-
# Get signing message
429-
parent_hashes = get_hashes_to_sign(
430-
recent_block_hashes,
431-
block,
432-
cycle_length,
433-
)
434-
message = create_signing_message(
435-
block.slot_number,
436-
parent_hashes,
437-
proposer_attestation.shard_id,
438-
proposer_attestation.shard_block_hash,
439-
last_justified_slot,
440-
)
441-
442-
# Verify
443-
# TODO: Verification is very slow, needs to compute in parallel
444-
voting_sigs, committee_indices = verify_votes(message, votes)
445-
446-
# Aggregate the votes
447-
bitfield, sigs = aggregate_votes(
448-
bitfield=proposer_attestation.bitfield,
449-
sigs=proposer_attestation.aggregate_sig,
450-
voting_sigs=voting_sigs,
451-
voting_committee_indices=committee_indices
452-
)
453-
454-
return proposer_attestation.copy(
455-
bitfield=bitfield,
456-
sigs=bls.aggregate_sigs(sigs),
457-
)
458-
459-
460-
def verify_votes(
461-
message: bytes,
462-
votes: Iterable[Tuple[int, bytes, int]]) -> Tuple[Tuple[bytes, ...], Tuple[int, ...]]:
463-
"""
464-
Verify the given votes
465-
"""
466-
sigs_with_committe_info = tuple(
467-
(sig, committee_index)
468-
for (committee_index, sig, public_key)
469-
in votes
470-
if bls.verify(message, public_key, sig)
471-
)
472-
try:
473-
sigs, committee_indices = zip(*sigs_with_committe_info)
474-
except ValueError:
475-
sigs = tuple()
476-
committee_indices = tuple()
477-
478-
return sigs, committee_indices
479-
480-
481-
def aggregate_votes(bitfield: bytes,
482-
sigs: Iterable[bytes],
483-
voting_sigs: Iterable[bytes],
484-
voting_committee_indices: Iterable[int]) -> Tuple[bytes, Tuple[int]]:
485-
"""
486-
Aggregate the votes
487-
"""
488-
# Update the bitfield and append the signatures
489-
sigs = tuple(sigs) + tuple(voting_sigs)
490-
bitfield = pipe(
491-
bitfield,
492-
*(
493-
set_voted(index=committee_index)
494-
for committee_index in voting_committee_indices
495-
)
496-
)
497-
498-
return bitfield, bls.aggregate_sigs(sigs)

eth/beacon/state_machines/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,12 @@
3131
Configurable,
3232
)
3333

34+
from eth.beacon.aggregation import (
35+
create_signing_message,
36+
)
3437
from eth.beacon.block_proposal import BlockProposal
3538
from eth.beacon.db.chain import BaseBeaconChainDB
3639
from eth.beacon.helpers import (
37-
create_signing_message,
3840
get_block_committees_info,
3941
get_hashes_to_sign,
4042
get_new_recent_block_hashes,

eth/beacon/state_machines/validation.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,10 @@
1919
has_voted,
2020
)
2121

22-
from eth.beacon.helpers import (
22+
from eth.beacon.aggregation import (
2323
create_signing_message,
24+
)
25+
from eth.beacon.helpers import (
2426
get_attestation_indices,
2527
get_block_committees_info,
2628
get_signed_parent_hashes,

tests/beacon/test_aggregation.py

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import pytest
2+
from hypothesis import (
3+
given,
4+
settings,
5+
strategies as st,
6+
)
7+
8+
from eth.utils import bls
9+
from eth.utils.bitfield import (
10+
get_empty_bitfield,
11+
has_voted,
12+
)
13+
from eth.beacon.aggregation import (
14+
aggregate_votes,
15+
verify_votes,
16+
)
17+
18+
19+
@settings(max_examples=1)
20+
@given(random=st.randoms())
21+
@pytest.mark.parametrize(
22+
(
23+
'votes_count'
24+
),
25+
[
26+
(0),
27+
(9),
28+
],
29+
)
30+
def test_aggregate_votes(votes_count, random, privkeys, pubkeys):
31+
bit_count = 10
32+
pre_bitfield = get_empty_bitfield(bit_count)
33+
pre_sigs = ()
34+
35+
random_votes = random.sample([i for i in range(bit_count)], votes_count)
36+
message = b'hello'
37+
38+
# Get votes: (committee_index, sig, public_key)
39+
votes = [
40+
(committee_index, bls.sign(message, privkeys[committee_index]), pubkeys[committee_index])
41+
for committee_index in random_votes
42+
]
43+
44+
# Verify
45+
sigs, committee_indices = verify_votes(message, votes)
46+
47+
# Aggregate the votes
48+
bitfield, sigs = aggregate_votes(
49+
bitfield=pre_bitfield,
50+
sigs=pre_sigs,
51+
voting_sigs=sigs,
52+
voting_committee_indices=committee_indices
53+
)
54+
55+
try:
56+
_, _, pubs = zip(*votes)
57+
except ValueError:
58+
pubs = ()
59+
60+
voted_index = [
61+
committee_index
62+
for committee_index in random_votes
63+
if has_voted(bitfield, committee_index)
64+
]
65+
assert len(voted_index) == len(votes)
66+
67+
aggregated_pubs = bls.aggregate_pubs(pubs)
68+
assert bls.verify(message, aggregated_pubs, sigs)

tests/beacon/test_helpers.py

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from eth.beacon.types.shard_and_committees import ShardAndCommittee
1515
from eth.beacon.helpers import (
1616
_get_element_from_recent_list,
17-
aggregate_votes,
1817
get_attestation_indices,
1918
get_block_hash,
2019
get_hashes_from_recent_block_hashes,
@@ -23,7 +22,6 @@
2322
get_shards_and_committees_for_slot,
2423
get_signed_parent_hashes,
2524
get_block_committees_info,
26-
verify_votes,
2725
)
2826

2927
from tests.beacon.helpers import (
@@ -400,55 +398,3 @@ def mock_get_shards_and_committees_for_slot(parent_block,
400398
block_committees_info.proposer_index_in_committee ==
401399
result_proposer_index_in_committee
402400
)
403-
404-
405-
@settings(max_examples=1)
406-
@given(random=st.randoms())
407-
@pytest.mark.parametrize(
408-
(
409-
'votes_count'
410-
),
411-
[
412-
(0),
413-
(9),
414-
],
415-
)
416-
def test_aggregate_votes(votes_count, random, privkeys, pubkeys):
417-
bit_count = 10
418-
pre_bitfield = get_empty_bitfield(bit_count)
419-
pre_sigs = ()
420-
421-
random_votes = random.sample([i for i in range(bit_count)], votes_count)
422-
message = b'hello'
423-
424-
# Get votes: (committee_index, sig, public_key)
425-
votes = [
426-
(committee_index, bls.sign(message, privkeys[committee_index]), pubkeys[committee_index])
427-
for committee_index in random_votes
428-
]
429-
430-
# Verify
431-
sigs, committee_indices = verify_votes(message, votes)
432-
433-
# Aggregate the votes
434-
bitfield, sigs = aggregate_votes(
435-
bitfield=pre_bitfield,
436-
sigs=pre_sigs,
437-
voting_sigs=sigs,
438-
voting_committee_indices=committee_indices
439-
)
440-
441-
try:
442-
_, _, pubs = zip(*votes)
443-
except ValueError:
444-
pubs = ()
445-
446-
voted_index = [
447-
committee_index
448-
for committee_index in random_votes
449-
if has_voted(bitfield, committee_index)
450-
]
451-
assert len(voted_index) == len(votes)
452-
453-
aggregated_pubs = bls.aggregate_pubs(pubs)
454-
assert bls.verify(message, aggregated_pubs, sigs)

0 commit comments

Comments
 (0)