Skip to content

Commit d195fe8

Browse files
committed
Single attestation "Full" implementation (#7444)
Squashed commit of the following: commit b413e3f Author: Michael Sproul <[email protected]> Date: Mon Jun 16 14:50:57 2025 +1000 Completely remove NotExactlyOneAggregationBitSet commit 2f1dcb2 Author: Eitan Seri-Levi <[email protected]> Date: Sun Jun 8 17:09:26 2025 +0300 fix test commit a22e95e Author: Eitan Seri-Levi <[email protected]> Date: Sun Jun 8 15:41:41 2025 +0300 fix test commit e0d2d6c Author: Eitan Seri-Levi <[email protected]> Date: Sun Jun 8 15:21:40 2025 +0300 fix test commit c2b144c Author: Eitan Seri-Levi <[email protected]> Date: Sun Jun 8 15:01:50 2025 +0300 fix test commit b7f037b Author: Eitan Seri-Levi <[email protected]> Date: Sun Jun 8 14:33:23 2025 +0300 move some checks around commit ec45999 Author: Eitan Seri-Levi <[email protected]> Date: Sun Jun 8 13:19:02 2025 +0300 chec attester is member of the committee commit 40a1f5c Author: Eitan Seri-Levi <[email protected]> Date: Sun Jun 8 12:51:32 2025 +0300 fix import commit 0869f2b Merge: 1f78f3f 170cd0f Author: Eitan Seri-Levi <[email protected]> Date: Sun Jun 8 12:46:35 2025 +0300 Merge branch 'unstable' of https://github.com/sigp/lighthouse into single-attestation-full-implementation commit 1f78f3f Author: Eitan Seri-Levi <[email protected]> Date: Wed Jun 4 07:25:39 2025 -0700 smol cleanup commit f3439d7 Merge: bdd134d ae30480 Author: Eitan Seri-Levi <[email protected]> Date: Mon Jun 2 14:07:47 2025 -0700 merge conflicts commit bdd134d Merge: 07e5f75 50dbfdf Author: Eitan Seri-Levi <[email protected]> Date: Mon May 19 12:55:25 2025 -0700 resolve merge conflicts commit 07e5f75 Merge: f437dd5 23ad833 Author: Eitan Seri-Levi <[email protected]> Date: Sat May 17 09:24:34 2025 -0700 Merge branch 'unstable' into single-attestation-full-implementation commit f437dd5 Merge: 60bdb4c 1d27855 Author: Eitan Seri-Levi <[email protected]> Date: Fri May 16 07:08:49 2025 -0700 Merge conflicts commit 60bdb4c Merge: 4b8a0fb c4182e3 Author: Eitan Seri-Levi <[email protected]> Date: Thu May 15 17:17:56 2025 -0700 Merge branch 'unstable' of https://github.com/sigp/lighthouse into single-attestation-full-implementation commit 4b8a0fb Author: Eitan Seri-Levi <[email protected]> Date: Thu May 15 17:17:40 2025 -0700 Test cases commit 3e8a00c Author: Eitan Seri-Levi <[email protected]> Date: Wed May 14 17:48:15 2025 -0700 lint commit e5a7d6c Author: Eitan Seri-Levi <[email protected]> Date: Wed May 14 17:34:45 2025 -0700 fix tests commit 4a7a1e7 Author: Eitan Seri-Levi <[email protected]> Date: Wed May 14 11:33:31 2025 -0700 single attn fix commit 0e8fc9c Author: Eitan Seri-Levi <[email protected]> Date: Mon May 12 22:45:19 2025 -0700 update commit f4b591b Author: Eitan Seri-Levi <[email protected]> Date: Mon May 12 22:44:39 2025 -0700 update commit a0eb866 Author: Eitan Seri-Levi <[email protected]> Date: Mon May 12 17:53:38 2025 -0700 fix test commit 032aae8 Author: Eitan Seri-Levi <[email protected]> Date: Mon May 12 17:19:25 2025 -0700 fix test commit 90e2078 Author: Eitan Seri-Levi <[email protected]> Date: Mon May 12 17:13:07 2025 -0700 get tests to compile commit 121ce59 Author: Eitan Seri-Levi <[email protected]> Date: Mon May 12 11:49:43 2025 -0700 fix check commit 84f5035 Author: Eitan Seri-Levi <[email protected]> Date: Mon May 12 11:46:27 2025 -0700 fmt commit a307b79 Author: Eitan Seri-Levi <[email protected]> Date: Mon May 12 11:38:42 2025 -0700 Full implementation of single attestation flow
1 parent e84b7b2 commit d195fe8

File tree

24 files changed

+772
-958
lines changed

24 files changed

+772
-958
lines changed

beacon_node/beacon_chain/src/attestation_verification.rs

Lines changed: 148 additions & 115 deletions
Large diffs are not rendered by default.

beacon_node/beacon_chain/src/attestation_verification/batch.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ pub fn batch_verify_unaggregated_attestations<'a, T, I>(
136136
) -> Result<Vec<Result<VerifiedUnaggregatedAttestation<'a, T>, Error>>, Error>
137137
where
138138
T: BeaconChainTypes,
139-
I: Iterator<Item = (&'a Attestation<T::EthSpec>, Option<SubnetId>)> + ExactSizeIterator,
139+
I: Iterator<Item = (&'a SingleAttestation, Option<SubnetId>)> + ExactSizeIterator,
140140
{
141141
let mut num_partially_verified = 0;
142142
let mut num_failed = 0;

beacon_node/beacon_chain/src/beacon_chain.rs

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2054,7 +2054,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
20542054
AttestationError,
20552055
>
20562056
where
2057-
I: Iterator<Item = (&'a Attestation<T::EthSpec>, Option<SubnetId>)> + ExactSizeIterator,
2057+
I: Iterator<Item = (&'a SingleAttestation, Option<SubnetId>)> + ExactSizeIterator,
20582058
{
20592059
batch_verify_unaggregated_attestations(attestations, self)
20602060
}
@@ -2066,7 +2066,7 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
20662066
/// aggregation bit set.
20672067
pub fn verify_unaggregated_attestation_for_gossip<'a>(
20682068
&self,
2069-
unaggregated_attestation: &'a Attestation<T::EthSpec>,
2069+
unaggregated_attestation: &'a SingleAttestation,
20702070
subnet_id: Option<SubnetId>,
20712071
) -> Result<VerifiedUnaggregatedAttestation<'a, T>, AttestationError> {
20722072
metrics::inc_counter(&metrics::UNAGGREGATED_ATTESTATION_PROCESSING_REQUESTS);
@@ -2082,13 +2082,9 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
20822082
.spec
20832083
.fork_name_at_slot::<T::EthSpec>(v.attestation().data().slot);
20842084
if current_fork.electra_enabled() {
2085-
// I don't see a situation where this could return None. The upstream unaggregated attestation checks
2086-
// should have already verified that this is an attestation with a single committee bit set.
2087-
if let Some(single_attestation) = v.single_attestation() {
2088-
event_handler.register(EventKind::SingleAttestation(Box::new(
2089-
single_attestation,
2090-
)));
2091-
}
2085+
event_handler.register(EventKind::SingleAttestation(Box::new(
2086+
v.single_attestation(),
2087+
)));
20922088
}
20932089
}
20942090

Lines changed: 33 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
use crate::attestation_verification::Error;
2-
use types::{Attestation, AttestationElectra, BitList, BitVector, EthSpec, SingleAttestation};
2+
use types::{
3+
Attestation, AttestationBase, AttestationElectra, BitList, BitVector, EthSpec, ForkName,
4+
SingleAttestation,
5+
};
36

47
pub fn single_attestation_to_attestation<E: EthSpec>(
58
single_attestation: &SingleAttestation,
69
committee: &[usize],
10+
fork_name: ForkName,
711
) -> Result<Attestation<E>, Error> {
812
let attester_index = single_attestation.attester_index;
913
let committee_index = single_attestation.committee_index;
@@ -24,23 +28,33 @@ pub fn single_attestation_to_attestation<E: EthSpec>(
2428
slot,
2529
})?;
2630

27-
let mut committee_bits: BitVector<E::MaxCommitteesPerSlot> = BitVector::default();
28-
committee_bits
29-
.set(committee_index as usize, true)
30-
.map_err(|e| Error::Invalid(e.into()))?;
31+
if fork_name.electra_enabled() {
32+
let mut committee_bits: BitVector<E::MaxCommitteesPerSlot> = BitVector::default();
33+
committee_bits
34+
.set(committee_index as usize, true)
35+
.map_err(|e| Error::Invalid(e.into()))?;
3136

32-
let mut aggregation_bits =
33-
BitList::with_capacity(committee.len()).map_err(|e| Error::Invalid(e.into()))?;
34-
aggregation_bits
35-
.set(aggregation_bit, true)
36-
.map_err(|e| Error::Invalid(e.into()))?;
37-
38-
// TODO(electra): consider eventually allowing conversion to non-Electra attestations as well
39-
// to maintain invertability (`Attestation` -> `SingleAttestation` -> `Attestation`).
40-
Ok(Attestation::Electra(AttestationElectra {
41-
aggregation_bits,
42-
committee_bits,
43-
data: single_attestation.data.clone(),
44-
signature: single_attestation.signature.clone(),
45-
}))
37+
let mut aggregation_bits =
38+
BitList::with_capacity(committee.len()).map_err(|e| Error::Invalid(e.into()))?;
39+
aggregation_bits
40+
.set(aggregation_bit, true)
41+
.map_err(|e| Error::Invalid(e.into()))?;
42+
Ok(Attestation::Electra(AttestationElectra {
43+
aggregation_bits,
44+
committee_bits,
45+
data: single_attestation.data.clone(),
46+
signature: single_attestation.signature.clone(),
47+
}))
48+
} else {
49+
let mut aggregation_bits =
50+
BitList::with_capacity(committee.len()).map_err(|e| Error::Invalid(e.into()))?;
51+
aggregation_bits
52+
.set(aggregation_bit, true)
53+
.map_err(|e| Error::Invalid(e.into()))?;
54+
Ok(Attestation::Base(AttestationBase {
55+
aggregation_bits,
56+
data: single_attestation.data.clone(),
57+
signature: single_attestation.signature.clone(),
58+
}))
59+
}
4660
}

beacon_node/beacon_chain/src/test_utils.rs

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,9 +1115,14 @@ where
11151115
attn.aggregation_bits
11161116
.set(aggregation_bit_index, true)
11171117
.unwrap();
1118-
attn
1118+
Attestation::Electra(attn)
1119+
}
1120+
Attestation::Base(mut attn) => {
1121+
attn.aggregation_bits
1122+
.set(aggregation_bit_index, true)
1123+
.unwrap();
1124+
Attestation::Base(attn)
11191125
}
1120-
Attestation::Base(_) => panic!("Must be an Electra attestation"),
11211126
};
11221127

11231128
let aggregation_bits = attestation.get_aggregation_bits();
@@ -1145,8 +1150,10 @@ where
11451150
let single_attestation =
11461151
attestation.to_single_attestation_with_attester_index(attester_index as u64)?;
11471152

1153+
let fork_name = self.spec.fork_name_at_slot::<E>(attestation.data().slot);
11481154
let attestation: Attestation<E> =
1149-
single_attestation_to_attestation(&single_attestation, committee.committee).unwrap();
1155+
single_attestation_to_attestation(&single_attestation, committee.committee, fork_name)
1156+
.unwrap();
11501157

11511158
assert_eq!(
11521159
single_attestation.committee_index,
@@ -2404,7 +2411,11 @@ where
24042411
})
24052412
}
24062413

2407-
pub fn process_attestations(&self, attestations: HarnessAttestations<E>) {
2414+
pub fn process_attestations(
2415+
&self,
2416+
attestations: HarnessAttestations<E>,
2417+
state: &BeaconState<E>,
2418+
) {
24082419
let num_validators = self.validator_keypairs.len();
24092420
let mut unaggregated = Vec::with_capacity(num_validators);
24102421
// This is an over-allocation, but it should be fine. It won't be *that* memory hungry and
@@ -2413,17 +2424,49 @@ where
24132424

24142425
for (unaggregated_attestations, maybe_signed_aggregate) in attestations.iter() {
24152426
for (attn, subnet) in unaggregated_attestations {
2416-
unaggregated.push((attn, Some(*subnet)));
2427+
let aggregation_bits = attn.get_aggregation_bits();
2428+
2429+
if aggregation_bits.len() != 1 {
2430+
panic!("Must be an unaggregated attestation")
2431+
}
2432+
2433+
let aggregation_bit = *aggregation_bits.first().unwrap();
2434+
2435+
let committee = state
2436+
.get_beacon_committee(attn.data().slot, attn.committee_index().unwrap())
2437+
.unwrap();
2438+
2439+
let attester_index = committee
2440+
.committee
2441+
.iter()
2442+
.enumerate()
2443+
.find_map(|(i, &index)| {
2444+
if aggregation_bit as usize == i {
2445+
return Some(index);
2446+
}
2447+
None
2448+
})
2449+
.unwrap();
2450+
2451+
let single_attestation = attn
2452+
.to_single_attestation_with_attester_index(attester_index as u64)
2453+
.unwrap();
2454+
2455+
unaggregated.push((single_attestation, Some(*subnet)));
24172456
}
24182457

24192458
if let Some(a) = maybe_signed_aggregate {
24202459
aggregated.push(a)
24212460
}
24222461
}
24232462

2463+
//TODO(single-attestation) convert to single attn
2464+
24242465
for result in self
24252466
.chain
2426-
.batch_verify_unaggregated_attestations_for_gossip(unaggregated.into_iter())
2467+
.batch_verify_unaggregated_attestations_for_gossip(
2468+
unaggregated.iter().map(|(attn, subnet)| (attn, *subnet)),
2469+
)
24272470
.unwrap()
24282471
{
24292472
let verified = result.unwrap();
@@ -2490,7 +2533,7 @@ where
24902533
) {
24912534
let attestations =
24922535
self.make_attestations(validators, state, state_root, block_hash, block.slot());
2493-
self.process_attestations(attestations);
2536+
self.process_attestations(attestations, state);
24942537
}
24952538

24962539
pub fn sync_committee_sign_block(

0 commit comments

Comments
 (0)