Ensure /eth/v2/beacon/pool/attestations honors committee_index (#7298)

#7294


  Fix the filtering logic so that we actually filter by committee index for both `Base` and `Electra` attestations.

Added a tiny optimization when calculating committee_index to prevent unneeded memory allocations

Added a regression test
This commit is contained in:
Eitan Seri-Levi
2025-04-10 21:47:30 -07:00
committed by GitHub
parent ef8ec35ac5
commit af51d50b05
5 changed files with 143 additions and 19 deletions

View File

@@ -28,6 +28,7 @@ use http_api::{
use lighthouse_network::{types::SyncState, Enr, EnrExt, PeerId};
use logging::test_logger;
use network::NetworkReceivers;
use operation_pool::attestation_storage::CheckpointKey;
use proto_array::ExecutionStatus;
use sensitive_url::SensitiveUrl;
use slot_clock::SlotClock;
@@ -2119,7 +2120,7 @@ impl ApiTester {
self
}
pub async fn test_get_beacon_pool_attestations(self) -> Self {
pub async fn test_get_beacon_pool_attestations(self) {
let result = self
.client
.get_beacon_pool_attestations_v1(None, None)
@@ -2138,9 +2139,80 @@ impl ApiTester {
.await
.unwrap()
.data;
assert_eq!(result, expected);
self
let result_committee_index_filtered = self
.client
.get_beacon_pool_attestations_v1(None, Some(0))
.await
.unwrap()
.data;
let expected_committee_index_filtered = expected
.clone()
.into_iter()
.filter(|att| att.get_committee_indices_map().contains(&0))
.collect::<Vec<_>>();
assert_eq!(
result_committee_index_filtered,
expected_committee_index_filtered
);
let result_committee_index_filtered = self
.client
.get_beacon_pool_attestations_v1(None, Some(1))
.await
.unwrap()
.data;
let expected_committee_index_filtered = expected
.clone()
.into_iter()
.filter(|att| att.get_committee_indices_map().contains(&1))
.collect::<Vec<_>>();
assert_eq!(
result_committee_index_filtered,
expected_committee_index_filtered
);
let fork_name = self
.harness
.chain
.spec
.fork_name_at_slot::<E>(self.harness.chain.slot().unwrap());
// aggregate electra attestations
if fork_name.electra_enabled() {
// Take and drop the lock in a block to avoid clippy complaining
// about taking locks across await points
{
let mut all_attestations = self.chain.op_pool.attestations.write();
let (prev_epoch_key, curr_epoch_key) =
CheckpointKey::keys_for_state(&self.harness.get_current_state());
all_attestations.aggregate_across_committees(prev_epoch_key);
all_attestations.aggregate_across_committees(curr_epoch_key);
}
let result_committee_index_filtered = self
.client
.get_beacon_pool_attestations_v2(None, Some(0))
.await
.unwrap()
.data;
let mut expected = self.chain.op_pool.get_all_attestations();
expected.extend(self.chain.naive_aggregation_pool.read().iter().cloned());
let expected_committee_index_filtered = expected
.clone()
.into_iter()
.filter(|att| att.get_committee_indices_map().contains(&0))
.collect::<Vec<_>>();
assert_eq!(
result_committee_index_filtered,
expected_committee_index_filtered
);
}
}
pub async fn test_post_beacon_pool_attester_slashings_valid_v1(mut self) -> Self {
@@ -6463,10 +6535,30 @@ async fn beacon_get_blocks() {
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn beacon_get_pools() {
async fn test_beacon_pool_attestations_electra() {
let mut config = ApiTesterConfig::default();
config.spec.altair_fork_epoch = Some(Epoch::new(0));
config.spec.bellatrix_fork_epoch = Some(Epoch::new(0));
config.spec.capella_fork_epoch = Some(Epoch::new(0));
config.spec.deneb_fork_epoch = Some(Epoch::new(0));
config.spec.electra_fork_epoch = Some(Epoch::new(0));
ApiTester::new_from_config(config)
.await
.test_get_beacon_pool_attestations()
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn test_beacon_pool_attestations_base() {
ApiTester::new()
.await
.test_get_beacon_pool_attestations()
.await;
}
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
async fn beacon_get_pools() {
ApiTester::new()
.await
.test_get_beacon_pool_attester_slashings()
.await