Altair validator client and HTTP API (#2404)

## Proposed Changes

* Implement the validator client and HTTP API changes necessary to support Altair


Co-authored-by: realbigsean <seananderson33@gmail.com>
Co-authored-by: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
Michael Sproul
2021-08-06 00:47:31 +00:00
parent 350b6f19de
commit 17a2c778e3
44 changed files with 3144 additions and 705 deletions

View File

@@ -12,7 +12,7 @@ pub mod lighthouse;
pub mod lighthouse_vc;
pub mod types;
use self::types::*;
use self::types::{Error as ResponseError, *};
use eth2_libp2p::PeerId;
use futures::Stream;
use futures_util::StreamExt;
@@ -26,6 +26,9 @@ use std::fmt;
use std::iter::Iterator;
use std::time::Duration;
pub const V1: EndpointVersion = EndpointVersion(1);
pub const V2: EndpointVersion = EndpointVersion(2);
#[derive(Debug)]
pub enum Error {
/// The `reqwest` client raised an error.
@@ -86,6 +89,7 @@ pub struct Timeouts {
pub liveness: Duration,
pub proposal: Duration,
pub proposer_duties: Duration,
pub sync_duties: Duration,
}
impl Timeouts {
@@ -96,6 +100,7 @@ impl Timeouts {
liveness: timeout,
proposal: timeout,
proposer_duties: timeout,
sync_duties: timeout,
}
}
}
@@ -142,14 +147,14 @@ impl BeaconNodeHttpClient {
}
}
/// Return the path with the standard `/eth1/v1` prefix applied.
fn eth_path(&self) -> Result<Url, Error> {
/// Return the path with the standard `/eth/vX` prefix applied.
fn eth_path(&self, version: EndpointVersion) -> Result<Url, Error> {
let mut path = self.server.full.clone();
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("eth")
.push("v1");
.push(&version.to_string());
Ok(path)
}
@@ -315,7 +320,7 @@ impl BeaconNodeHttpClient {
///
/// May return a `404` if beacon chain genesis has not yet occurred.
pub async fn get_beacon_genesis(&self) -> Result<GenericResponse<GenesisData>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -332,7 +337,7 @@ impl BeaconNodeHttpClient {
&self,
state_id: StateId,
) -> Result<Option<GenericResponse<RootData>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -351,7 +356,7 @@ impl BeaconNodeHttpClient {
&self,
state_id: StateId,
) -> Result<Option<GenericResponse<Fork>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -370,7 +375,7 @@ impl BeaconNodeHttpClient {
&self,
state_id: StateId,
) -> Result<Option<GenericResponse<FinalityCheckpointsData>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -390,7 +395,7 @@ impl BeaconNodeHttpClient {
state_id: StateId,
ids: Option<&[ValidatorId]>,
) -> Result<Option<GenericResponse<Vec<ValidatorBalanceData>>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -420,7 +425,7 @@ impl BeaconNodeHttpClient {
ids: Option<&[ValidatorId]>,
statuses: Option<&[ValidatorStatus]>,
) -> Result<Option<GenericResponse<Vec<ValidatorData>>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -460,7 +465,7 @@ impl BeaconNodeHttpClient {
index: Option<u64>,
epoch: Option<Epoch>,
) -> Result<Option<GenericResponse<Vec<CommitteeData>>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -487,6 +492,29 @@ impl BeaconNodeHttpClient {
self.get_opt(path).await
}
/// `GET beacon/states/{state_id}/sync_committees?epoch`
pub async fn get_beacon_states_sync_committees(
&self,
state_id: StateId,
epoch: Option<Epoch>,
) -> Result<GenericResponse<SyncCommitteeByValidatorIndices>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("states")
.push(&state_id.to_string())
.push("sync_committees");
if let Some(epoch) = epoch {
path.query_pairs_mut()
.append_pair("epoch", &epoch.to_string());
}
self.get(path).await
}
/// `GET beacon/states/{state_id}/validators/{validator_id}`
///
/// Returns `Ok(None)` on a 404 error.
@@ -495,7 +523,7 @@ impl BeaconNodeHttpClient {
state_id: StateId,
validator_id: &ValidatorId,
) -> Result<Option<GenericResponse<ValidatorData>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -516,7 +544,7 @@ impl BeaconNodeHttpClient {
slot: Option<Slot>,
parent_root: Option<Hash256>,
) -> Result<Option<GenericResponse<Vec<BlockHeaderData>>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -543,7 +571,7 @@ impl BeaconNodeHttpClient {
&self,
block_id: BlockId,
) -> Result<Option<GenericResponse<BlockHeaderData>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -561,7 +589,7 @@ impl BeaconNodeHttpClient {
&self,
block: &SignedBeaconBlock<T>,
) -> Result<(), Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -574,14 +602,32 @@ impl BeaconNodeHttpClient {
Ok(())
}
/// `GET beacon/blocks`
/// `GET v2/beacon/blocks`
///
/// Returns `Ok(None)` on a 404 error.
pub async fn get_beacon_blocks<T: EthSpec>(
&self,
block_id: BlockId,
) -> Result<Option<GenericResponse<SignedBeaconBlock<T>>>, Error> {
let mut path = self.eth_path()?;
) -> Result<Option<ForkVersionedResponse<SignedBeaconBlock<T>>>, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("blocks")
.push(&block_id.to_string());
self.get_opt(path).await
}
/// `GET v1/beacon/blocks` (LEGACY)
///
/// Returns `Ok(None)` on a 404 error.
pub async fn get_beacon_blocks_v1<T: EthSpec>(
&self,
block_id: BlockId,
) -> Result<Option<ForkVersionedResponse<SignedBeaconBlock<T>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -600,7 +646,7 @@ impl BeaconNodeHttpClient {
block_id: BlockId,
spec: &ChainSpec,
) -> Result<Option<SignedBeaconBlock<T>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -621,7 +667,7 @@ impl BeaconNodeHttpClient {
&self,
block_id: BlockId,
) -> Result<Option<GenericResponse<RootData>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -640,7 +686,7 @@ impl BeaconNodeHttpClient {
&self,
block_id: BlockId,
) -> Result<Option<GenericResponse<Vec<Attestation<T>>>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -657,7 +703,7 @@ impl BeaconNodeHttpClient {
&self,
attestations: &[Attestation<T>],
) -> Result<(), Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -665,15 +711,8 @@ impl BeaconNodeHttpClient {
.push("pool")
.push("attestations");
let response = self
.client
.post(path)
.timeout(self.timeouts.attestation)
.json(attestations)
.send()
.await
.map_err(Error::Reqwest)?;
ok_or_indexed_error(response).await?;
self.post_with_timeout(path, &attestations, self.timeouts.attestation)
.await?;
Ok(())
}
@@ -684,7 +723,7 @@ impl BeaconNodeHttpClient {
slot: Option<Slot>,
committee_index: Option<u64>,
) -> Result<GenericResponse<Vec<Attestation<T>>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -710,7 +749,7 @@ impl BeaconNodeHttpClient {
&self,
slashing: &AttesterSlashing<T>,
) -> Result<(), Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -727,7 +766,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_pool_attester_slashings<T: EthSpec>(
&self,
) -> Result<GenericResponse<Vec<AttesterSlashing<T>>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -743,7 +782,7 @@ impl BeaconNodeHttpClient {
&self,
slashing: &ProposerSlashing,
) -> Result<(), Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -760,7 +799,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_pool_proposer_slashings(
&self,
) -> Result<GenericResponse<Vec<ProposerSlashing>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -776,7 +815,7 @@ impl BeaconNodeHttpClient {
&self,
exit: &SignedVoluntaryExit,
) -> Result<(), Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -793,7 +832,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_pool_voluntary_exits(
&self,
) -> Result<GenericResponse<Vec<SignedVoluntaryExit>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -804,9 +843,44 @@ impl BeaconNodeHttpClient {
self.get(path).await
}
/// `POST beacon/pool/sync_committees`
pub async fn post_beacon_pool_sync_committee_signatures(
&self,
signatures: &[SyncCommitteeMessage],
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("beacon")
.push("pool")
.push("sync_committees");
self.post(path, &signatures).await?;
Ok(())
}
/// `POST validator/contribution_and_proofs`
pub async fn post_validator_contribution_and_proofs<T: EthSpec>(
&self,
signed_contributions: &[SignedContributionAndProof<T>],
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("contribution_and_proofs");
self.post(path, &signed_contributions).await?;
Ok(())
}
/// `GET config/fork_schedule`
pub async fn get_config_fork_schedule(&self) -> Result<GenericResponse<Vec<Fork>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -818,7 +892,7 @@ impl BeaconNodeHttpClient {
/// `GET config/spec`
pub async fn get_config_spec(&self) -> Result<GenericResponse<ConfigAndPreset>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -832,7 +906,7 @@ impl BeaconNodeHttpClient {
pub async fn get_config_deposit_contract(
&self,
) -> Result<GenericResponse<DepositContractData>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -844,7 +918,7 @@ impl BeaconNodeHttpClient {
/// `GET node/version`
pub async fn get_node_version(&self) -> Result<GenericResponse<VersionData>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -856,7 +930,7 @@ impl BeaconNodeHttpClient {
/// `GET node/identity`
pub async fn get_node_identity(&self) -> Result<GenericResponse<IdentityData>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -868,7 +942,7 @@ impl BeaconNodeHttpClient {
/// `GET node/syncing`
pub async fn get_node_syncing(&self) -> Result<GenericResponse<SyncingData>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -880,7 +954,7 @@ impl BeaconNodeHttpClient {
/// `GET node/health`
pub async fn get_node_health(&self) -> Result<StatusCode, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -906,7 +980,7 @@ impl BeaconNodeHttpClient {
&self,
peer_id: PeerId,
) -> Result<GenericResponse<PeerData>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -923,7 +997,7 @@ impl BeaconNodeHttpClient {
states: Option<&[PeerState]>,
directions: Option<&[PeerDirection]>,
) -> Result<PeersData, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -953,7 +1027,7 @@ impl BeaconNodeHttpClient {
/// `GET node/peer_count`
pub async fn get_node_peer_count(&self) -> Result<GenericResponse<PeerCount>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -963,12 +1037,29 @@ impl BeaconNodeHttpClient {
self.get(path).await
}
/// `GET debug/beacon/states/{state_id}`
/// `GET v2/debug/beacon/states/{state_id}`
pub async fn get_debug_beacon_states<T: EthSpec>(
&self,
state_id: StateId,
) -> Result<Option<GenericResponse<BeaconState<T>>>, Error> {
let mut path = self.eth_path()?;
) -> Result<Option<ForkVersionedResponse<BeaconState<T>>>, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("debug")
.push("beacon")
.push("states")
.push(&state_id.to_string());
self.get_opt(path).await
}
/// `GET v1/debug/beacon/states/{state_id}` (LEGACY)
pub async fn get_debug_beacon_states_v1<T: EthSpec>(
&self,
state_id: StateId,
) -> Result<Option<ForkVersionedResponse<BeaconState<T>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -987,7 +1078,7 @@ impl BeaconNodeHttpClient {
state_id: StateId,
spec: &ChainSpec,
) -> Result<Option<BeaconState<T>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1006,7 +1097,7 @@ impl BeaconNodeHttpClient {
pub async fn get_debug_beacon_heads(
&self,
) -> Result<GenericResponse<Vec<ChainHeadData>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1022,7 +1113,7 @@ impl BeaconNodeHttpClient {
&self,
epoch: Epoch,
) -> Result<DutiesResponse<Vec<ProposerData>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1035,14 +1126,14 @@ impl BeaconNodeHttpClient {
.await
}
/// `GET validator/blocks/{slot}`
/// `GET v2/validator/blocks/{slot}`
pub async fn get_validator_blocks<T: EthSpec>(
&self,
slot: Slot,
randao_reveal: &SignatureBytes,
graffiti: Option<&Graffiti>,
) -> Result<GenericResponse<BeaconBlock<T>>, Error> {
let mut path = self.eth_path()?;
) -> Result<ForkVersionedResponse<BeaconBlock<T>>, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1067,7 +1158,7 @@ impl BeaconNodeHttpClient {
slot: Slot,
committee_index: CommitteeIndex,
) -> Result<GenericResponse<AttestationData>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1087,7 +1178,7 @@ impl BeaconNodeHttpClient {
slot: Slot,
attestation_data_root: Hash256,
) -> Result<Option<GenericResponse<Attestation<T>>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1105,6 +1196,32 @@ impl BeaconNodeHttpClient {
.await
}
/// `GET validator/sync_committee_contribution`
pub async fn get_validator_sync_committee_contribution<T: EthSpec>(
&self,
sync_committee_data: &SyncContributionData,
) -> Result<Option<GenericResponse<SyncCommitteeContribution<T>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("sync_committee_contribution");
path.query_pairs_mut()
.append_pair("slot", &sync_committee_data.slot.to_string())
.append_pair(
"beacon_block_root",
&format!("{:?}", sync_committee_data.beacon_block_root),
)
.append_pair(
"subcommittee_index",
&sync_committee_data.subcommittee_index.to_string(),
);
self.get_opt(path).await
}
/// `POST lighthouse/liveness`
pub async fn post_lighthouse_liveness(
&self,
@@ -1135,7 +1252,7 @@ impl BeaconNodeHttpClient {
epoch: Epoch,
indices: &[u64],
) -> Result<DutiesResponse<Vec<AttesterData>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1153,22 +1270,15 @@ impl BeaconNodeHttpClient {
&self,
aggregates: &[SignedAggregateAndProof<T>],
) -> Result<(), Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("aggregate_and_proofs");
let response = self
.client
.post(path)
.timeout(self.timeouts.attestation)
.json(aggregates)
.send()
.await
.map_err(Error::Reqwest)?;
ok_or_indexed_error(response).await?;
self.post_with_timeout(path, &aggregates, self.timeouts.attestation)
.await?;
Ok(())
}
@@ -1178,7 +1288,7 @@ impl BeaconNodeHttpClient {
&self,
subscriptions: &[BeaconCommitteeSubscription],
) -> Result<(), Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
@@ -1190,12 +1300,29 @@ impl BeaconNodeHttpClient {
Ok(())
}
/// `POST validator/sync_committee_subscriptions`
pub async fn post_validator_sync_committee_subscriptions(
&self,
subscriptions: &[SyncCommitteeSubscription],
) -> Result<(), Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("sync_committee_subscriptions");
self.post(path, &subscriptions).await?;
Ok(())
}
/// `GET events?topics`
pub async fn get_events<T: EthSpec>(
&self,
topic: &[EventTopic],
) -> Result<impl Stream<Item = Result<EventKind<T>, Error>>, Error> {
let mut path = self.eth_path()?;
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("events");
@@ -1219,6 +1346,25 @@ impl BeaconNodeHttpClient {
Err(e) => Err(Error::Reqwest(e)),
}))
}
/// `POST validator/duties/sync/{epoch}`
pub async fn post_validator_duties_sync(
&self,
epoch: Epoch,
indices: &[u64],
) -> Result<GenericResponse<Vec<SyncDuty>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("validator")
.push("duties")
.push("sync")
.push(&epoch.to_string());
self.post_with_timeout_and_response(path, &indices, self.timeouts.sync_duties)
.await
}
}
/// Returns `Ok(response)` if the response is a `200 OK` response. Otherwise, creates an
@@ -1229,21 +1375,10 @@ async fn ok_or_error(response: Response) -> Result<Response, Error> {
if status == StatusCode::OK {
Ok(response)
} else if let Ok(message) = response.json().await {
Err(Error::ServerMessage(message))
} else {
Err(Error::StatusCode(status))
}
}
/// Returns `Ok(response)` if the response is a `200 OK` response. Otherwise, creates an
/// appropriate indexed error message.
async fn ok_or_indexed_error(response: Response) -> Result<Response, Error> {
let status = response.status();
if status == StatusCode::OK {
Ok(response)
} else if let Ok(message) = response.json().await {
Err(Error::ServerIndexedMessage(message))
match message {
ResponseError::Message(message) => Err(Error::ServerMessage(message)),
ResponseError::Indexed(indexed) => Err(Error::ServerIndexedMessage(indexed)),
}
} else {
Err(Error::StatusCode(status))
}

View File

@@ -10,6 +10,14 @@ use std::fmt;
use std::str::{from_utf8, FromStr};
pub use types::*;
/// An API error serializable to JSON.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Error {
Indexed(IndexedErrorMessage),
Message(ErrorMessage),
}
/// An API error serializable to JSON.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ErrorMessage {
@@ -43,6 +51,30 @@ impl Failure {
}
}
/// The version of a single API endpoint, e.g. the `v1` in `/eth/v1/beacon/blocks`.
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct EndpointVersion(pub u64);
impl FromStr for EndpointVersion {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(version_str) = s.strip_prefix('v') {
u64::from_str(version_str)
.map(EndpointVersion)
.map_err(|_| ())
} else {
Err(())
}
}
}
impl std::fmt::Display for EndpointVersion {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(fmt, "v{}", self.0)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GenesisData {
#[serde(with = "serde_utils::quoted_u64")]
@@ -179,6 +211,14 @@ impl<'a, T: Serialize> From<&'a T> for GenericResponseRef<'a, T> {
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
// #[serde(bound = "T: Serialize + serde::de::DeserializeOwned")]
pub struct ForkVersionedResponse<T> {
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<ForkName>,
pub data: T,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct RootData {
pub root: Hash256,
@@ -378,6 +418,11 @@ pub struct CommitteesQuery {
pub epoch: Option<Epoch>,
}
#[derive(Serialize, Deserialize)]
pub struct SyncCommitteesQuery {
pub epoch: Option<Epoch>,
}
#[derive(Serialize, Deserialize)]
pub struct AttestationPoolQuery {
pub slot: Option<Slot>,
@@ -399,6 +444,20 @@ pub struct CommitteeData {
pub validators: Vec<u64>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SyncCommitteeByValidatorIndices {
#[serde(with = "serde_utils::quoted_u64_vec")]
pub validators: Vec<u64>,
pub validator_aggregates: Vec<SyncSubcommittee>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SyncSubcommittee {
#[serde(with = "serde_utils::quoted_u64_vec")]
pub indices: Vec<u64>,
}
#[derive(Serialize, Deserialize)]
pub struct HeadersQuery {
pub slot: Option<Slot>,