Skip to content

Commit a93cafe

Browse files
authored
Implement selections Beacon API endpoints to support DVT middleware (#7016)
* #6610 - [x] Add `beacon_committee_selections` endpoint - [x] Test beacon committee aggregator and confirmed working - [x] Add `sync_committee_selections` endpoint - [x] Test sync committee aggregator and confirmed working
1 parent 7b5be8b commit a93cafe

File tree

8 files changed

+658
-237
lines changed

8 files changed

+658
-237
lines changed

common/eth2/src/lib.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,13 @@ pub const JSON_CONTENT_TYPE_HEADER: &str = "application/json";
5555
const HTTP_ATTESTATION_TIMEOUT_QUOTIENT: u32 = 4;
5656
const HTTP_ATTESTER_DUTIES_TIMEOUT_QUOTIENT: u32 = 4;
5757
const HTTP_ATTESTATION_SUBSCRIPTIONS_TIMEOUT_QUOTIENT: u32 = 24;
58+
const HTTP_ATTESTATION_AGGREGATOR_TIMEOUT_QUOTIENT: u32 = 24; // For DVT involving middleware only
5859
const HTTP_LIVENESS_TIMEOUT_QUOTIENT: u32 = 4;
5960
const HTTP_PROPOSAL_TIMEOUT_QUOTIENT: u32 = 2;
6061
const HTTP_PROPOSER_DUTIES_TIMEOUT_QUOTIENT: u32 = 4;
6162
const HTTP_SYNC_COMMITTEE_CONTRIBUTION_TIMEOUT_QUOTIENT: u32 = 4;
6263
const HTTP_SYNC_DUTIES_TIMEOUT_QUOTIENT: u32 = 4;
64+
const HTTP_SYNC_AGGREGATOR_TIMEOUT_QUOTIENT: u32 = 24; // For DVT involving middleware only
6365
const HTTP_GET_BEACON_BLOCK_SSZ_TIMEOUT_QUOTIENT: u32 = 4;
6466
const HTTP_GET_DEBUG_BEACON_STATE_QUOTIENT: u32 = 4;
6567
const HTTP_GET_DEPOSIT_SNAPSHOT_QUOTIENT: u32 = 4;
@@ -150,11 +152,13 @@ pub struct Timeouts {
150152
pub attestation: Duration,
151153
pub attester_duties: Duration,
152154
pub attestation_subscriptions: Duration,
155+
pub attestation_aggregators: Duration,
153156
pub liveness: Duration,
154157
pub proposal: Duration,
155158
pub proposer_duties: Duration,
156159
pub sync_committee_contribution: Duration,
157160
pub sync_duties: Duration,
161+
pub sync_aggregators: Duration,
158162
pub get_beacon_blocks_ssz: Duration,
159163
pub get_debug_beacon_states: Duration,
160164
pub get_deposit_snapshot: Duration,
@@ -168,11 +172,13 @@ impl Timeouts {
168172
attestation: timeout,
169173
attester_duties: timeout,
170174
attestation_subscriptions: timeout,
175+
attestation_aggregators: timeout,
171176
liveness: timeout,
172177
proposal: timeout,
173178
proposer_duties: timeout,
174179
sync_committee_contribution: timeout,
175180
sync_duties: timeout,
181+
sync_aggregators: timeout,
176182
get_beacon_blocks_ssz: timeout,
177183
get_debug_beacon_states: timeout,
178184
get_deposit_snapshot: timeout,
@@ -187,12 +193,14 @@ impl Timeouts {
187193
attester_duties: base_timeout / HTTP_ATTESTER_DUTIES_TIMEOUT_QUOTIENT,
188194
attestation_subscriptions: base_timeout
189195
/ HTTP_ATTESTATION_SUBSCRIPTIONS_TIMEOUT_QUOTIENT,
196+
attestation_aggregators: base_timeout / HTTP_ATTESTATION_AGGREGATOR_TIMEOUT_QUOTIENT,
190197
liveness: base_timeout / HTTP_LIVENESS_TIMEOUT_QUOTIENT,
191198
proposal: base_timeout / HTTP_PROPOSAL_TIMEOUT_QUOTIENT,
192199
proposer_duties: base_timeout / HTTP_PROPOSER_DUTIES_TIMEOUT_QUOTIENT,
193200
sync_committee_contribution: base_timeout
194201
/ HTTP_SYNC_COMMITTEE_CONTRIBUTION_TIMEOUT_QUOTIENT,
195202
sync_duties: base_timeout / HTTP_SYNC_DUTIES_TIMEOUT_QUOTIENT,
203+
sync_aggregators: base_timeout / HTTP_SYNC_AGGREGATOR_TIMEOUT_QUOTIENT,
196204
get_beacon_blocks_ssz: base_timeout / HTTP_GET_BEACON_BLOCK_SSZ_TIMEOUT_QUOTIENT,
197205
get_debug_beacon_states: base_timeout / HTTP_GET_DEBUG_BEACON_STATE_QUOTIENT,
198206
get_deposit_snapshot: base_timeout / HTTP_GET_DEPOSIT_SNAPSHOT_QUOTIENT,
@@ -2841,6 +2849,42 @@ impl BeaconNodeHttpClient {
28412849
)
28422850
.await
28432851
}
2852+
2853+
/// `POST validator/beacon_committee_selections`
2854+
pub async fn post_validator_beacon_committee_selections(
2855+
&self,
2856+
selections: &[BeaconCommitteeSelection],
2857+
) -> Result<GenericResponse<Vec<BeaconCommitteeSelection>>, Error> {
2858+
let mut path = self.eth_path(V1)?;
2859+
2860+
path.path_segments_mut()
2861+
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
2862+
.push("validator")
2863+
.push("beacon_committee_selections");
2864+
2865+
self.post_with_timeout_and_response(
2866+
path,
2867+
&selections,
2868+
self.timeouts.attestation_aggregators,
2869+
)
2870+
.await
2871+
}
2872+
2873+
/// `POST validator/sync_committee_selections`
2874+
pub async fn post_validator_sync_committee_selections(
2875+
&self,
2876+
selections: &[SyncCommitteeSelection],
2877+
) -> Result<GenericResponse<Vec<SyncCommitteeSelection>>, Error> {
2878+
let mut path = self.eth_path(V1)?;
2879+
2880+
path.path_segments_mut()
2881+
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
2882+
.push("validator")
2883+
.push("sync_committee_selections");
2884+
2885+
self.post_with_timeout_and_response(path, &selections, self.timeouts.sync_aggregators)
2886+
.await
2887+
}
28442888
}
28452889

28462890
/// Returns `Ok(response)` if the response is a `200 OK` response. Otherwise, creates an

common/eth2/src/types.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -967,6 +967,23 @@ pub struct PeerCount {
967967
pub disconnecting: u64,
968968
}
969969

970+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
971+
pub struct BeaconCommitteeSelection {
972+
#[serde(with = "serde_utils::quoted_u64")]
973+
pub validator_index: u64,
974+
pub slot: Slot,
975+
pub selection_proof: Signature,
976+
}
977+
978+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
979+
pub struct SyncCommitteeSelection {
980+
#[serde(with = "serde_utils::quoted_u64")]
981+
pub validator_index: u64,
982+
pub slot: Slot,
983+
#[serde(with = "serde_utils::quoted_u64")]
984+
pub subcommittee_index: u64,
985+
pub selection_proof: Signature,
986+
}
970987
// --------- Server Sent Event Types -----------
971988

972989
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]

consensus/types/src/selection_proof.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ use crate::{
33
};
44
use ethereum_hashing::hash;
55
use safe_arith::{ArithError, SafeArith};
6+
use serde::{Deserialize, Serialize};
67
use ssz::Encode;
78
use std::cmp;
89

910
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
10-
#[derive(PartialEq, Debug, Clone)]
11+
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
12+
#[serde(transparent)]
1113
pub struct SelectionProof(Signature);
1214

1315
impl SelectionProof {

consensus/types/src/sync_selection_proof.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,14 @@ use crate::{
77
};
88
use ethereum_hashing::hash;
99
use safe_arith::{ArithError, SafeArith};
10+
use serde::{Deserialize, Serialize};
1011
use ssz::Encode;
1112
use ssz_types::typenum::Unsigned;
1213
use std::cmp;
1314

1415
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
15-
#[derive(PartialEq, Debug, Clone)]
16+
#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
17+
#[serde(transparent)]
1618
pub struct SyncSelectionProof(Signature);
1719

1820
impl SyncSelectionProof {

validator_client/src/lib.rs

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ pub mod cli;
22
pub mod config;
33

44
use crate::cli::ValidatorClient;
5+
use crate::duties_service::SelectionProofConfig;
56
pub use config::Config;
67
use initialized_validators::InitializedValidators;
78
use metrics::set_gauge;
@@ -55,6 +56,22 @@ const WAITING_FOR_GENESIS_POLL_TIME: Duration = Duration::from_secs(12);
5556

5657
const DOPPELGANGER_SERVICE_NAME: &str = "doppelganger";
5758

59+
/// Compute attestation selection proofs this many slots before they are required.
60+
///
61+
/// At start-up selection proofs will be computed with less lookahead out of necessity.
62+
const SELECTION_PROOF_SLOT_LOOKAHEAD: u64 = 8;
63+
64+
/// The attestation selection proof lookahead for those running with the --distributed flag.
65+
const SELECTION_PROOF_SLOT_LOOKAHEAD_DVT: u64 = 1;
66+
67+
/// Fraction of a slot at which attestation selection proof signing should happen (2 means half way).
68+
const SELECTION_PROOF_SCHEDULE_DENOM: u32 = 2;
69+
70+
/// Number of epochs in advance to compute sync selection proofs when not in `distributed` mode.
71+
pub const AGGREGATION_PRE_COMPUTE_EPOCHS: u64 = 2;
72+
/// Number of slots in advance to compute sync selection proofs when in `distributed` mode.
73+
pub const AGGREGATION_PRE_COMPUTE_SLOTS_DISTRIBUTED: u64 = 1;
74+
5875
type ValidatorStore<E> = LighthouseValidatorStore<SystemTimeSlotClock, E>;
5976

6077
#[derive(Clone)]
@@ -407,6 +424,41 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
407424
validator_store.prune_slashing_protection_db(slot.epoch(E::slots_per_epoch()), true);
408425
}
409426

427+
// Define a config to be pass to duties_service.
428+
// The defined config here defaults to using selections_endpoint and parallel_sign (i.e., distributed mode)
429+
// Other DVT applications, e.g., Anchor can pass in different configs to suit different needs.
430+
let attestation_selection_proof_config = if config.distributed {
431+
SelectionProofConfig {
432+
lookahead_slot: SELECTION_PROOF_SLOT_LOOKAHEAD_DVT,
433+
computation_offset: slot_clock.slot_duration() / SELECTION_PROOF_SCHEDULE_DENOM,
434+
selections_endpoint: true,
435+
parallel_sign: true,
436+
}
437+
} else {
438+
SelectionProofConfig {
439+
lookahead_slot: SELECTION_PROOF_SLOT_LOOKAHEAD,
440+
computation_offset: slot_clock.slot_duration() / SELECTION_PROOF_SCHEDULE_DENOM,
441+
selections_endpoint: false,
442+
parallel_sign: false,
443+
}
444+
};
445+
446+
let sync_selection_proof_config = if config.distributed {
447+
SelectionProofConfig {
448+
lookahead_slot: AGGREGATION_PRE_COMPUTE_SLOTS_DISTRIBUTED,
449+
computation_offset: Duration::default(),
450+
selections_endpoint: true,
451+
parallel_sign: true,
452+
}
453+
} else {
454+
SelectionProofConfig {
455+
lookahead_slot: E::slots_per_epoch() * AGGREGATION_PRE_COMPUTE_EPOCHS,
456+
computation_offset: Duration::default(),
457+
selections_endpoint: false,
458+
parallel_sign: false,
459+
}
460+
};
461+
410462
let duties_service = Arc::new(
411463
DutiesServiceBuilder::new()
412464
.slot_clock(slot_clock.clone())
@@ -415,7 +467,8 @@ impl<E: EthSpec> ProductionValidatorClient<E> {
415467
.spec(context.eth2_config.spec.clone())
416468
.executor(context.executor.clone())
417469
.enable_high_validator_count_metrics(config.enable_high_validator_count_metrics)
418-
.distributed(config.distributed)
470+
.attestation_selection_proof_config(attestation_selection_proof_config)
471+
.sync_selection_proof_config(sync_selection_proof_config)
419472
.disable_attesting(config.disable_attesting)
420473
.build()?,
421474
);

0 commit comments

Comments
 (0)