Skip to content

Commit c13e42e

Browse files
committed
allow duplicates in indexed payload attestation indices
1 parent 7ddaa5d commit c13e42e

File tree

6 files changed

+27
-118
lines changed

6 files changed

+27
-118
lines changed

consensus/state_processing/src/per_block_processing/is_valid_indexed_payload_attestation.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,31 +14,31 @@ pub fn is_valid_indexed_payload_attestation<E: EthSpec>(
1414
verify_signatures: VerifySignatures,
1515
spec: &ChainSpec,
1616
) -> Result<(), BlockOperationError<Invalid>> {
17-
// Verify indices are sorted and unique
17+
// Verify indices are non-empty and sorted (duplicates allowed)
1818
let indices = &indexed_payload_attestation.attesting_indices;
1919
verify!(!indices.is_empty(), Invalid::IndicesEmpty);
2020
let check_sorted = |list: &[u64]| -> Result<(), BlockOperationError<Invalid>> {
2121
list.iter()
2222
.tuple_windows()
2323
.enumerate()
2424
.try_for_each(|(i, (x, y))| {
25-
if x < y {
25+
if x <= y {
2626
Ok(())
2727
} else {
2828
Err(error(Invalid::BadValidatorIndicesOrdering(i)))
2929
}
3030
})?;
3131
Ok(())
3232
};
33-
check_sorted(&indices)?;
33+
check_sorted(indices)?;
3434

3535
if verify_signatures.is_true() {
3636
verify!(
3737
indexed_payload_attestation_signature_set(
3838
state,
3939
|i| get_pubkey_from_state(state, i),
4040
&indexed_payload_attestation.signature,
41-
&indexed_payload_attestation,
41+
indexed_payload_attestation,
4242
spec
4343
)?
4444
.verify(),

consensus/state_processing/src/per_block_processing/process_operations.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,11 @@ pub fn process_consolidation_request<E: EthSpec>(
796796
Ok(())
797797
}
798798

799+
// TODO(EIP-7732): Add test cases for `process_payload_attestations` to
800+
// `consensus/state_processing/src/per_block_processing/tests.rs`.
801+
// The tests will require being able to build Gloas blocks with PayloadAttestations,
802+
// which currently fails due to incomplete Gloas block structure as mentioned here
803+
// https://github.com/sigp/lighthouse/pull/8273
799804
pub fn process_payload_attestation<E: EthSpec>(
800805
state: &mut BeaconState<E>,
801806
payload_attestation: &PayloadAttestation<E>,

consensus/state_processing/src/per_block_processing/verify_payload_attestation.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use super::VerifySignatures;
22
use super::errors::{BlockOperationError, PayloadAttestationInvalid as Invalid};
33
use crate::ConsensusContext;
44
use crate::per_block_processing::is_valid_indexed_payload_attestation;
5+
use safe_arith::SafeArith;
56
use types::*;
67

78
pub fn verify_payload_attestation<'ctxt, E: EthSpec>(
@@ -24,7 +25,7 @@ pub fn verify_payload_attestation<'ctxt, E: EthSpec>(
2425

2526
// Check that the attestation is for the previous slot
2627
verify!(
27-
data.slot + 1 == state.slot(),
28+
data.slot.safe_add(1)? == state.slot(),
2829
Invalid::SlotMismatch {
2930
expected: state.slot().saturating_sub(Slot::new(1)),
3031
found: data.slot,
@@ -36,7 +37,7 @@ pub fn verify_payload_attestation<'ctxt, E: EthSpec>(
3637

3738
is_valid_indexed_payload_attestation(
3839
state,
39-
&indexed_payload_attestation,
40+
indexed_payload_attestation,
4041
verify_signatures,
4142
spec,
4243
)?;

consensus/types/src/beacon_state.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1083,7 +1083,7 @@ impl<E: EthSpec> BeaconState<E> {
10831083

10841084
if self.fork_name_unchecked().gloas_enabled() {
10851085
self.compute_balance_weighted_selection(indices, &seed, 1, true, spec)?
1086-
.get(0)
1086+
.first()
10871087
.copied()
10881088
.ok_or(Error::InsufficientValidators)
10891089
} else {
@@ -1328,6 +1328,7 @@ impl<E: EthSpec> BeaconState<E> {
13281328
/// Compute the sync committee indices for the next sync committee.
13291329
fn get_next_sync_committee_indices(&self, spec: &ChainSpec) -> Result<Vec<usize>, Error> {
13301330
let epoch = self.current_epoch().safe_add(1)?;
1331+
13311332
let active_validator_indices = self.get_active_validator_indices(epoch, spec)?;
13321333
let seed = self.get_seed(epoch, Domain::SyncCommittee, spec)?;
13331334

@@ -2795,8 +2796,6 @@ impl<E: EthSpec> BeaconState<E> {
27952796
///
27962797
/// If shuffle_indices is True, candidate indices are themselves sampled from indices
27972798
/// by shuffling it, otherwise indices is traversed in order.
2798-
///
2799-
/// Spec: compute_balance_weighted_selection helper
28002799
fn compute_balance_weighted_selection(
28012800
&self,
28022801
indices: &[usize],
@@ -2814,7 +2813,7 @@ impl<E: EthSpec> BeaconState<E> {
28142813
let mut count = 0usize;
28152814

28162815
while selected.len() < size {
2817-
let mut next_index = count % total;
2816+
let mut next_index = count.safe_rem(total)?;
28182817

28192818
if shuffle_indices {
28202819
next_index =
@@ -2828,7 +2827,7 @@ impl<E: EthSpec> BeaconState<E> {
28282827
selected.push(*candidate_index);
28292828
}
28302829

2831-
count += 1;
2830+
count.safe_add_assign(1)?;
28322831
}
28332832

28342833
Ok(selected)
@@ -2855,7 +2854,8 @@ impl<E: EthSpec> BeaconState<E> {
28552854
MAX_RANDOM_BYTE
28562855
};
28572856

2858-
Ok(effective_balance * max_random_value >= max_effective_balance * random_value)
2857+
Ok(effective_balance.safe_mul(max_random_value)?
2858+
>= max_effective_balance.safe_mul(random_value)?)
28592859
}
28602860
}
28612861

consensus/types/src/beacon_state/tests.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,15 @@ async fn build_state<E: EthSpec>(validator_count: usize) -> BeaconState<E> {
5353
.head_beacon_state_cloned()
5454
}
5555

56-
/// TODO(EIP7732) Add test for ptc attester index using get_ptc_attester_seed
56+
/// TODO(EIP-7732): Add tests for PTC (Payload Timeliness Committee) functions:
57+
/// - get_ptc: Test committee selection, size, balance-weighted selection
58+
/// - get_ptc_attester_seed: Test seed generation and determinism
59+
/// - compute_balance_weighted_selection: Test selection algorithm with various balances
60+
/// - compute_balance_weighted_acceptance: Test acceptance probability
61+
/// These tests require being able to build Gloas states with initialized committee caches,
62+
/// which currently fails due to incomplete Gloas block structure as mentioned here:
63+
/// https://github.com/sigp/lighthouse/pull/8273
64+
/// Similar to existing committee_consistency_test suite for get_beacon_committee.
5765
5866
async fn test_beacon_proposer_index<E: EthSpec>() {
5967
let spec = E::default_spec();

consensus/types/src/ptc.rs

Lines changed: 0 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -21,108 +21,3 @@ impl<E: EthSpec> IntoIterator for PTC<E> {
2121
self.0.into_iter()
2222
}
2323
}
24-
25-
// TODO(EIP7732) Mark's implementation below was using from_committees to filter out ptc members from attestation committees, but I don't see that in the spec, so perhaps is a deprecated implementation
26-
// impl<E: EthSpec> PTC<E> {
27-
// pub fn from_committees(committees: &[BeaconCommittee]) -> Result<Self, BeaconStateError> {
28-
// // this function is only used here and
29-
// // I have no idea where else to put it
30-
// fn bit_floor(n: u64) -> u64 {
31-
// if n == 0 {
32-
// 0
33-
// } else {
34-
// 1 << (n.leading_zeros() as u64 ^ 63)
35-
// }
36-
// }
37-
38-
// let committee_count_per_slot = committees.len() as u64;
39-
// let committees_per_slot = bit_floor(std::cmp::min(
40-
// committee_count_per_slot,
41-
// E::PTCSize::to_u64(),
42-
// )) as usize;
43-
// let members_per_committee = E::PTCSize::to_usize().safe_div(committees_per_slot)?;
44-
45-
// let mut ptc = Vec::with_capacity(E::PTCSize::to_usize());
46-
// for idx in 0..committees_per_slot {
47-
// let beacon_committee = committees
48-
// .get(idx as usize)
49-
// .ok_or_else(|| Error::InvalidCommitteeIndex(idx as u64))?;
50-
// ptc.extend_from_slice(&beacon_committee.committee[..members_per_committee]);
51-
// }
52-
53-
// Ok(Self(FixedVector::from(ptc)))
54-
// }
55-
// }
56-
57-
// impl<'a, E: EthSpec> IntoIterator for &'a PTC<E> {
58-
// type Item = &'a usize;
59-
// type IntoIter = std::slice::Iter<'a, usize>;
60-
61-
// fn into_iter(self) -> Self::IntoIter {
62-
// self.0.iter()
63-
// }
64-
// }
65-
66-
// impl<E: EthSpec> IntoIterator for PTC<E> {
67-
// type Item = usize;
68-
// type IntoIter = std::vec::IntoIter<usize>;
69-
70-
// fn into_iter(self) -> Self::IntoIter {
71-
// self.0.into_iter()
72-
// }
73-
// }
74-
// use crate::*;
75-
// use safe_arith::SafeArith;
76-
77-
// /// TODO(EIP-7732): is it easier to return u64 or usize?
78-
// #[derive(Clone, Debug, PartialEq)]
79-
// pub struct PTC<E: EthSpec>(FixedVector<usize, E::PTCSize>);
80-
81-
// impl<E: EthSpec> PTC<E> {
82-
// pub fn from_committees(committees: &[BeaconCommittee]) -> Result<Self, BeaconStateError> {
83-
// // this function is only used here and
84-
// // I have no idea where else to put it
85-
// fn bit_floor(n: u64) -> u64 {
86-
// if n == 0 {
87-
// 0
88-
// } else {
89-
// 1 << (n.leading_zeros() as u64 ^ 63)
90-
// }
91-
// }
92-
93-
// let committee_count_per_slot = committees.len() as u64;
94-
// let committees_per_slot = bit_floor(std::cmp::min(
95-
// committee_count_per_slot,
96-
// E::PTCSize::to_u64(),
97-
// )) as usize;
98-
// let members_per_committee = E::PTCSize::to_usize().safe_div(committees_per_slot)?;
99-
100-
// let mut ptc = Vec::with_capacity(E::PTCSize::to_usize());
101-
// for idx in 0..committees_per_slot {
102-
// let beacon_committee = committees
103-
// .get(idx as usize)
104-
// .ok_or_else(|| Error::InvalidCommitteeIndex(idx as u64))?;
105-
// ptc.extend_from_slice(&beacon_committee.committee[..members_per_committee]);
106-
// }
107-
108-
// Ok(Self(FixedVector::from(ptc)))
109-
// }
110-
// }
111-
112-
// impl<'a, E: EthSpec> IntoIterator for &'a PTC<E> {
113-
// type Item = &'a usize;
114-
// type IntoIter = std::slice::Iter<'a, usize>;
115-
116-
// fn into_iter(self) -> Self::IntoIter {
117-
// self.0.iter()
118-
// }
119-
// }
120-
121-
// impl<E: EthSpec> IntoIterator for PTC<E> {
122-
// type Item = usize;
123-
// type IntoIter = std::vec::IntoIter<usize>;
124-
125-
// fn into_iter(self) -> Self::IntoIter {
126-
// self.0.into_iter()
127-
// }
128-
// }

0 commit comments

Comments
 (0)