Skip to content

Commit

Permalink
Add SSZ support to validator registration builder API
Browse files Browse the repository at this point in the history
  • Loading branch information
Tumas committed Feb 7, 2025
1 parent 3882694 commit 2a27aa3
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 21 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

84 changes: 69 additions & 15 deletions builder_api/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use reqwest::{
Client, Response, StatusCode,
};
use serde::de::DeserializeOwned;
use ssz::{SszHash as _, SszRead, SszWrite as _};
use ssz::{ContiguousList, SszHash as _, SszRead, SszWrite as _};
use thiserror::Error;
use typenum::Unsigned as _;
use types::{
Expand Down Expand Up @@ -71,7 +71,8 @@ pub struct Api {
config: BuilderConfig,
client: Client,
metrics: Option<Arc<Metrics>>,
supports_ssz: ArcSwap<Option<bool>>,
supports_block_ssz: ArcSwap<Option<bool>>,
supports_validators_ssz: ArcSwap<Option<bool>>,
}

impl Api {
Expand All @@ -81,7 +82,8 @@ impl Api {
config,
client,
metrics,
supports_ssz: ArcSwap::from_pointee(None),
supports_block_ssz: ArcSwap::from_pointee(None),
supports_validators_ssz: ArcSwap::from_pointee(None),
}
}

Expand Down Expand Up @@ -129,26 +131,75 @@ impl Api {
Ok(())
}

pub async fn register_validators(
pub async fn register_validators<P: Preset>(
&self,
validator_registrations: &[SignedValidatorRegistrationV1],
validator_registrations: ContiguousList<
SignedValidatorRegistrationV1,
P::ValidatorRegistryLimit,
>,
) -> Result<()> {
let _timer = self
.metrics
.as_ref()
.map(|metrics| metrics.builder_register_validator_times.start_timer());

let use_json = self.config.builder_api_format == BuilderApiFormat::Json
|| self
.supports_validators_ssz
.load()
.is_some_and(|supported| !supported);

let response = self
.post_validators::<P>(&validator_registrations, use_json)
.await;

// See <https://github.com/ethereum/builder-specs/pull/110>
if use_json {
response
} else {
match response {
Ok(()) => {
self.supports_validators_ssz.store(Arc::new(Some(true)));
Ok(())
}
Err(error) => {
debug!(
"received error in non-JSON register validators request: {error:?}, \
retrying in JSON"
);

self.supports_validators_ssz.store(Arc::new(Some(false)));
self.post_validators::<P>(&validator_registrations, true)
.await
}
}
}
}

async fn post_validators<P: Preset>(
&self,
validator_registrations: &ContiguousList<
SignedValidatorRegistrationV1,
P::ValidatorRegistryLimit,
>,
use_json: bool,
) -> Result<()> {
debug!("registering validators: {validator_registrations:?}");

let url = self.url("/eth/v1/builder/validators")?;
let response = self
.client
.post(url.into_url())
.json(validator_registrations)
.send()
.await?;
let request = self.client.post(url.into_url());

let response = handle_error(response).await?;
let request = if use_json {
request.json(validator_registrations)
} else {
request
.header(ACCEPT, APPLICATION_OCTET_STREAM.as_ref())
.header(CONTENT_TYPE, APPLICATION_OCTET_STREAM.as_ref())
.body(validator_registrations.to_ssz()?)
};

let response = request.send().await?;
let response = handle_error(response).await;

debug!("register_validators response: {response:?}");

Expand Down Expand Up @@ -262,7 +313,10 @@ impl Api {
.header(ETH_CONSENSUS_VERSION, block.phase().as_ref());

let use_json = self.config.builder_api_format == BuilderApiFormat::Json
|| self.supports_ssz.load().is_some_and(|supported| !supported);
|| self
.supports_block_ssz
.load()
.is_some_and(|supported| !supported);

let request = if use_json {
request.json(block)
Expand Down Expand Up @@ -315,7 +369,7 @@ impl Api {
return response
.json()
.await
.inspect(|_| self.supports_ssz.store(Arc::new(Some(false))))
.inspect(|_| self.supports_block_ssz.store(Arc::new(Some(false))))
.map_err(Into::into);
}

Expand All @@ -324,7 +378,7 @@ impl Api {
let bytes = response.bytes().await?;

return T::from_ssz(&phase, &bytes)
.inspect(|_| self.supports_ssz.store(Arc::new(Some(true))))
.inspect(|_| self.supports_block_ssz.store(Arc::new(Some(true))))
.map_err(Into::into);
}

Expand Down
2 changes: 1 addition & 1 deletion builder_api/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ pub const DEFAULT_BUILDER_MAX_SKIPPED_SLOTS: u64 = 3;

#[derive(Clone, Copy, PartialEq, Eq, Debug, Default, Display, FromStr)]
pub enum BuilderApiFormat {
Json,
#[default]
Json,
Ssz,
}

Expand Down
3 changes: 2 additions & 1 deletion builder_api/src/unphased/containers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ pub struct ValidatorRegistrationV1 {
pub pubkey: PublicKeyBytes,
}

#[derive(Debug, Deserialize, Serialize)]
#[derive(Debug, Deserialize, Serialize, Ssz)]
#[ssz(derive_hash = false, derive_read = false)]
pub struct SignedValidatorRegistrationV1 {
pub message: ValidatorRegistrationV1,
pub signature: SignatureBytes,
Expand Down
1 change: 1 addition & 0 deletions validator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ tap = { workspace = true }
thiserror = { workspace = true }
tokio = { workspace = true }
tower-http = { workspace = true }
try_from_iterator = { workspace = true }
tynm = { workspace = true }
typenum = { workspace = true }
types = { workspace = true }
Expand Down
15 changes: 11 additions & 4 deletions validator/src/validator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,10 +47,11 @@ use rayon::iter::{IntoParallelIterator as _, ParallelIterator as _};
use signer::{Signer, SigningMessage, SigningTriple};
use slasher::{SlasherToValidator, ValidatorToSlasher};
use slashing_protection::SlashingProtector;
use ssz::{BitList, BitVector};
use ssz::{BitList, BitVector, ContiguousList, ReadError};
use static_assertions::assert_not_impl_any;
use std_ext::ArcExt as _;
use tap::{Conv as _, Pipe as _};
use try_from_iterator::TryFromIterator as _;
use types::{
altair::{
containers::{ContributionAndProof, SignedContributionAndProof, SyncCommitteeMessage},
Expand Down Expand Up @@ -2056,11 +2057,17 @@ impl<P: Preset, W: Wait + Sync> Validator<P, W> {
message,
signature: signature.into(),
})
.collect_vec();
.chunks(MAX_VALIDATORS_PER_REGISTRATION)
.into_iter()
.map(ContiguousList::<_, P::ValidatorRegistryLimit>::try_from_iter)
.collect::<Result<Vec<_>, ReadError>>()
.inspect_err(|error| {
warn!("failed to collect validator registrations: {error:?}")
})?;

// Do not submit requests in parallel. Doing so causes all of them to be timed out.
for registration in signed_registrations.chunks(MAX_VALIDATORS_PER_REGISTRATION) {
if let Err(error) = builder_api.register_validators(registration).await {
for registrations in signed_registrations {
if let Err(error) = builder_api.register_validators::<P>(registrations).await {
warn!("failed to register validator batch: {error}");
}
}
Expand Down

0 comments on commit 2a27aa3

Please sign in to comment.