diff --git a/validator_client/validator_services/src/attestation_service.rs b/validator_client/validator_services/src/attestation_service.rs index da6e8f35886..458a410561d 100644 --- a/validator_client/validator_services/src/attestation_service.rs +++ b/validator_client/validator_services/src/attestation_service.rs @@ -180,8 +180,9 @@ impl AttestationService Result<(), String> { let slot = self.slot_clock.now().ok_or("Failed to read slot clock")?; let duration_to_next_slot = self @@ -189,6 +190,15 @@ impl AttestationService AttestationService> = self + let aggregate_duties_by_committee_index: HashMap> = self .duties_service .attesters(slot) .into_iter() @@ -208,23 +218,21 @@ impl AttestationService AttestationService, - aggregate_production_instant: Instant, ) -> Result<(), ()> { let attestations_timer = validator_metrics::start_timer_vec( &validator_metrics::ATTESTATION_SERVICE_TIMES, &[validator_metrics::ATTESTATIONS], ); - // There's not need to produce `Attestation` or `SignedAggregateAndProof` if we do not have - // any validators for the given `slot` and `committee_index`. + // There's not need to produce `Attestation` if we do not have + // any validators for the given `slot`. if validator_duties.is_empty() { return Ok(()); } - // Step 1. - // - // Download, sign and publish an `Attestation` for each validator. - let attestation_opt = self - .produce_and_publish_attestations(slot, committee_index, &validator_duties) + // Download, sign and publish an `Attestation` for all validators at once + self.produce_and_publish_attestations(slot, &validator_duties) .await .map_err(move |e| { crit!( error = format!("{:?}", e), - committee_index, slot = slot.as_u64(), "Error during attestation routine" ) })?; drop(attestations_timer); + Ok(()) + } - // Step 2. - // - // If an attestation was produced, make an aggregate. - if let Some(attestation_data) = attestation_opt { - // First, wait until the `aggregation_production_instant` (2/3rds - // of the way though the slot). As verified in the - // `delay_triggers_when_in_the_past` test, this code will still run - // even if the instant has already elapsed. - sleep_until(aggregate_production_instant).await; - - // Start the metrics timer *after* we've done the delay. - let _aggregates_timer = validator_metrics::start_timer_vec( - &validator_metrics::ATTESTATION_SERVICE_TIMES, - &[validator_metrics::AGGREGATES], - ); - - // Then download, sign and publish a `SignedAggregateAndProof` for each - // validator that is elected to aggregate for this `slot` and - // `committee_index`. - self.produce_and_publish_aggregates( - &attestation_data, - committee_index, - &validator_duties, - ) + /// Produce and publish aggregated attestations for validators + async fn publish_aggregates( + self, + slot: Slot, + committee_index: CommitteeIndex, + validator_duties: Vec, + aggregate_production_instant: Instant, + ) -> Result<(), ()> { + // There's not need to produce `SignedAggregateAndProof` if we do not have + // any validators for the given `slot` and `committee_index`. + if validator_duties.is_empty() { + return Ok(()); + } + + // Wait until the `aggregation_production_instant` (2/3rds + // of the way though the slot). As verified in the + // `delay_triggers_when_in_the_past` test, this code will still run + // even if the instant has already elapsed. + sleep_until(aggregate_production_instant).await; + + // Start the metrics timer *after* we've done the delay. + let _aggregates_timer = validator_metrics::start_timer_vec( + &validator_metrics::ATTESTATION_SERVICE_TIMES, + &[validator_metrics::AGGREGATES], + ); + + let attestation_data = self + .beacon_nodes + .first_success(|beacon_node| async move { + beacon_node + .get_validator_attestation_data(slot, 0) + .await + .map_err(|e| { + format!( + "Failed to produce attestation data for aggregation: {:?}", + e + ) + }) + .map(|result| result.data) + }) + .await + .map_err(|e| { + error!( + error = %e, + slot = slot.as_u64(), + "Failed to produce attestation data for aggregation" + ); + })?; + + // Download, sign and publish a `SignedAggregateAndProof` for each + // validator that is elected to aggregate for this `slot` and + // `committee_index`. + self.produce_and_publish_aggregates(&attestation_data, committee_index, &validator_duties) .await .map_err(move |e| { crit!( error = format!("{:?}", e), committee_index, slot = slot.as_u64(), - "Error during attestation routine" + "Error during aggregate attestation routine" ) })?; - } Ok(()) } @@ -331,7 +356,6 @@ impl AttestationService Result, String> { if validator_duties.is_empty() { @@ -352,7 +376,8 @@ impl AttestationService AttestationService AttestationService AttestationService