Skip to content
This repository was archived by the owner on Jul 1, 2021. It is now read-only.

Commit c04c294

Browse files
authored
Merge pull request #1311 from hwwhww/aggregation_strategy
Basic aggregation strategy
2 parents 8eca0b1 + 01b78f6 commit c04c294

File tree

21 files changed

+1210
-235
lines changed

21 files changed

+1210
-235
lines changed
Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,57 @@
1+
from typing import Tuple
2+
3+
from eth_utils import ValidationError
4+
5+
from eth2.beacon.state_machines.forks.serenity.block_validation import (
6+
validate_attestation_slot,
7+
)
18
from eth2.beacon.types.attestations import Attestation
9+
from eth2.beacon.typing import CommitteeIndex, SigningRoot, Slot
10+
from eth2.configs import Eth2Config
211

312
from .pool import OperationPool
413

514

15+
def is_valid_slot(
16+
attestation: Attestation, current_slot: Slot, config: Eth2Config
17+
) -> bool:
18+
try:
19+
validate_attestation_slot(
20+
attestation.data.slot,
21+
current_slot,
22+
config.SLOTS_PER_EPOCH,
23+
config.MIN_ATTESTATION_INCLUSION_DELAY,
24+
)
25+
except ValidationError:
26+
return False
27+
else:
28+
return True
29+
30+
631
class AttestationPool(OperationPool[Attestation]):
7-
pass
32+
def get_valid_attestation_by_current_slot(
33+
self, slot: Slot, config: Eth2Config
34+
) -> Tuple[Attestation, ...]:
35+
return tuple(
36+
filter(
37+
lambda attestation: is_valid_slot(attestation, slot, config),
38+
self._pool_storage.values(),
39+
)
40+
)
41+
42+
def get_acceptable_attestations(
43+
self,
44+
slot: Slot,
45+
committee_index: CommitteeIndex,
46+
beacon_block_root: SigningRoot,
47+
) -> Tuple[Attestation, ...]:
48+
return tuple(
49+
filter(
50+
lambda attestation: (
51+
beacon_block_root == attestation.data.beacon_block_root
52+
and slot == attestation.data.slot
53+
and committee_index == attestation.data.index
54+
),
55+
self._pool_storage.values(),
56+
)
57+
)

eth2/beacon/scripts/quickstart_state/keygen_100_validators.yaml

Lines changed: 199 additions & 0 deletions
Large diffs are not rendered by default.

eth2/beacon/tools/builder/aggregator.py

Lines changed: 134 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
1+
from typing import Sequence
2+
13
from eth_typing import BLSSignature
4+
from eth_utils import ValidationError
25
from ssz import get_hash_tree_root, uint64
36

47
from eth2._utils.bls import bls
58
from eth2._utils.hash import hash_eth2
9+
from eth2.beacon.attestation_helpers import (
10+
validate_indexed_attestation_aggregate_signature,
11+
)
612
from eth2.beacon.committee_helpers import get_beacon_committee
13+
from eth2.beacon.epoch_processing_helpers import (
14+
get_attesting_indices,
15+
get_indexed_attestation,
16+
)
717
from eth2.beacon.helpers import compute_epoch_at_slot, get_domain
818
from eth2.beacon.signature_domain import SignatureDomain
19+
from eth2.beacon.types.aggregate_and_proof import AggregateAndProof
20+
from eth2.beacon.types.attestations import Attestation
921
from eth2.beacon.types.states import BeaconState
10-
from eth2.beacon.typing import CommitteeIndex, Slot
22+
from eth2.beacon.typing import Bitfield, CommitteeIndex, Slot
1123
from eth2.configs import CommitteeConfig
1224

1325
# TODO: TARGET_AGGREGATORS_PER_COMMITTEE is not in Eth2Config now.
1426
TARGET_AGGREGATORS_PER_COMMITTEE = 16
1527

1628

17-
def slot_signature(
29+
def get_slot_signature(
1830
state: BeaconState, slot: Slot, privkey: int, config: CommitteeConfig
1931
) -> BLSSignature:
2032
"""
@@ -54,3 +66,123 @@ def is_aggregator(
5466
committee = get_beacon_committee(state, slot, index, config)
5567
modulo = max(1, len(committee) // TARGET_AGGREGATORS_PER_COMMITTEE)
5668
return int.from_bytes(hash_eth2(signature)[0:8], byteorder="little") % modulo == 0
69+
70+
71+
def get_aggregate_from_valid_committee_attestations(
72+
attestations: Sequence[Attestation]
73+
) -> Attestation:
74+
"""
75+
Return the aggregate attestation.
76+
77+
The given attestations SHOULD have the same `data: AttestationData` and are valid.
78+
"""
79+
signatures = [attestation.signature for attestation in attestations]
80+
aggregate_signature = bls.aggregate_signatures(signatures)
81+
82+
all_aggregation_bits = [
83+
attestation.aggregation_bits for attestation in attestations
84+
]
85+
aggregation_bits = tuple(map(any, zip(*all_aggregation_bits)))
86+
87+
assert len(attestations) > 0
88+
89+
return Attestation.create(
90+
data=attestations[0].data,
91+
aggregation_bits=Bitfield(aggregation_bits),
92+
signature=aggregate_signature,
93+
)
94+
95+
96+
#
97+
# Validation
98+
#
99+
100+
101+
def validate_aggregate_and_proof(
102+
state: BeaconState,
103+
aggregate_and_proof: AggregateAndProof,
104+
attestation_propagation_slot_range: int,
105+
config: CommitteeConfig,
106+
) -> None:
107+
"""
108+
Validate aggregate_and_proof
109+
110+
Reference: https://github.com/ethereum/eth2.0-specs/blob/master/specs/networking/p2p-interface.md#global-topics # noqa: E501
111+
"""
112+
attestation = aggregate_and_proof.aggregate
113+
114+
validate_attestation_propagation_slot_range(
115+
state, attestation, attestation_propagation_slot_range
116+
)
117+
118+
attesting_indices = get_attesting_indices(
119+
state, attestation.data, attestation.aggregation_bits, config
120+
)
121+
if aggregate_and_proof.aggregator_index not in attesting_indices:
122+
raise ValidationError(
123+
f"The aggregator index ({aggregate_and_proof.aggregator_index}) is not within"
124+
f" the aggregate's committee {attesting_indices}"
125+
)
126+
127+
if not is_aggregator(
128+
state,
129+
attestation.data.slot,
130+
attestation.data.index,
131+
aggregate_and_proof.selection_proof,
132+
config,
133+
):
134+
raise ValidationError(
135+
f"The given validator {aggregate_and_proof.aggregator_index}"
136+
" is not a selected aggregator"
137+
)
138+
139+
validate_aggregator_proof(state, aggregate_and_proof, config)
140+
141+
validate_attestation_signature(state, attestation, config)
142+
143+
144+
def validate_attestation_propagation_slot_range(
145+
state: BeaconState,
146+
attestation: Attestation,
147+
attestation_propagation_slot_range: int,
148+
) -> None:
149+
if (
150+
attestation.data.slot + attestation_propagation_slot_range < state.slot
151+
or attestation.data.slot > state.slot
152+
):
153+
raise ValidationError(
154+
"attestation.data.slot should be within the last"
155+
" {attestation_propagation_slot_range} slots. Got"
156+
f" attestationdata.slot={attestation.data.slot},"
157+
f" current slot={state.slot}"
158+
)
159+
160+
161+
def validate_aggregator_proof(
162+
state: BeaconState, aggregate_and_proof: AggregateAndProof, config: CommitteeConfig
163+
) -> None:
164+
slot = aggregate_and_proof.aggregate.data.slot
165+
pubkey = state.validators[aggregate_and_proof.aggregator_index].pubkey
166+
domain = get_domain(
167+
state,
168+
SignatureDomain.DOMAIN_BEACON_ATTESTER,
169+
config.SLOTS_PER_EPOCH,
170+
message_epoch=compute_epoch_at_slot(slot, config.SLOTS_PER_EPOCH),
171+
)
172+
message_hash = get_hash_tree_root(slot, sedes=uint64)
173+
174+
bls.validate(
175+
message_hash=message_hash,
176+
pubkey=pubkey,
177+
signature=aggregate_and_proof.selection_proof,
178+
domain=domain,
179+
)
180+
181+
182+
def validate_attestation_signature(
183+
state: BeaconState, attestation: Attestation, config: CommitteeConfig
184+
) -> None:
185+
indexed_attestation = get_indexed_attestation(state, attestation, config)
186+
validate_indexed_attestation_aggregate_signature(
187+
state, indexed_attestation, config.SLOTS_PER_EPOCH
188+
)

eth2/beacon/tools/builder/proposer.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,9 @@ def create_block_on_state(
8282
parent_block=parent_block, block_params=FromBlockParams(slot=slot)
8383
)
8484

85+
# MAX_ATTESTATIONS
86+
attestations = attestations[: config.MAX_ATTESTATIONS]
87+
8588
# TODO: Add more operations
8689
randao_reveal = _generate_randao_reveal(privkey, slot, state, config)
8790
eth1_data = state.eth1_data

eth2/beacon/tools/factories.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,13 @@ def _create(
4646
is disabled.
4747
"""
4848
override_lengths(cls.config)
49+
if "num_validators" in kwargs:
50+
num_validators = kwargs["num_validators"]
51+
else:
52+
num_validators = cls.num_validators
4953

5054
if kwargs["genesis_state"] is None:
51-
keymap = mk_keymap_of_size(cls.num_validators)
55+
keymap = mk_keymap_of_size(num_validators)
5256
genesis_state, genesis_block = create_mock_genesis(
5357
config=cls.config,
5458
pubkeys=tuple(keymap.keys()),

eth2/beacon/types/aggregate_and_proof.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,29 @@
1616
class AggregateAndProof(HashableContainer):
1717

1818
fields = [
19-
("index", uint64),
20-
("selection_proof", bytes96),
19+
("aggregator_index", uint64),
2120
("aggregate", Attestation),
21+
("selection_proof", bytes96),
2222
]
2323

2424
@classmethod
2525
def create(
2626
cls: Type[TAggregateAndProof],
27-
index: ValidatorIndex = default_validator_index,
28-
selection_proof: BLSSignature = EMPTY_SIGNATURE,
27+
aggregator_index: ValidatorIndex = default_validator_index,
2928
aggregate: Attestation = default_attestation,
29+
selection_proof: BLSSignature = EMPTY_SIGNATURE,
3030
) -> TAggregateAndProof:
3131
return super().create(
32-
index=index, selection_proof=selection_proof, aggregate=aggregate
32+
aggregator_index=aggregator_index,
33+
aggregate=aggregate,
34+
selection_proof=selection_proof,
3335
)
3436

3537
def __str__(self) -> str:
3638
return (
37-
f"index={self.index},"
38-
f" selection_proof={humanize_hash(self.selection_proof)},"
39+
f"aggregator_index={self.aggregator_index},"
3940
f" aggregate={self.aggregate},"
41+
f" selection_proof={humanize_hash(self.selection_proof)},"
4042
)
4143

4244

eth2/beacon/typing.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,27 @@ def __str__(self) -> str:
3737
SigningRoot = NewType("SigningRoot", Hash32)
3838

3939

40+
#
41+
# Networkinig
42+
#
43+
44+
# CommitteeIndex % ATTESTATION_SUBNET_COUNT
45+
SubnetId = NewType("SubnetId", int)
46+
47+
48+
#
49+
# Helpers
50+
#
51+
52+
4053
class FromBlockParams(NamedTuple):
4154
slot: Slot = None
4255

4356

44-
# defaults to emulate "zero types"
57+
#
58+
# Defaults to emulate "zero types"
59+
#
60+
4561
default_slot = Slot(0)
4662
default_epoch = Epoch(0)
4763
default_committee_index = CommitteeIndex(0)

0 commit comments

Comments
 (0)