mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-18 13:28:33 +00:00
Implement standard eth2.0 API (#1569)
- Resolves #1550 - Resolves #824 - Resolves #825 - Resolves #1131 - Resolves #1411 - Resolves #1256 - Resolve #1177 - Includes the `ShufflingId` struct initially defined in #1492. That PR is now closed and the changes are included here, with significant bug fixes. - Implement the https://github.com/ethereum/eth2.0-APIs in a new `http_api` crate using `warp`. This replaces the `rest_api` crate. - Add a new `common/eth2` crate which provides a wrapper around `reqwest`, providing the HTTP client that is used by the validator client and for testing. This replaces the `common/remote_beacon_node` crate. - Create a `http_metrics` crate which is a dedicated server for Prometheus metrics (they are no longer served on the same port as the REST API). We now have flags for `--metrics`, `--metrics-address`, etc. - Allow the `subnet_id` to be an optional parameter for `VerifiedUnaggregatedAttestation::verify`. This means it does not need to be provided unnecessarily by the validator client. - Move `fn map_attestation_committee` in `mod beacon_chain::attestation_verification` to a new `fn with_committee_cache` on the `BeaconChain` so the same cache can be used for obtaining validator duties. - Add some other helpers to `BeaconChain` to assist with common API duties (e.g., `block_root_at_slot`, `head_beacon_block_root`). - Change the `NaiveAggregationPool` so it can index attestations by `hash_tree_root(attestation.data)`. This is a requirement of the API. - Add functions to `BeaconChainHarness` to allow it to create slashings and exits. - Allow for `eth1::Eth1NetworkId` to go to/from a `String`. - Add functions to the `OperationPool` to allow getting all objects in the pool. - Add function to `BeaconState` to check if a committee cache is initialized. - Fix bug where `seconds_per_eth1_block` was not transferring over from `YamlConfig` to `ChainSpec`. - Add the `deposit_contract_address` to `YamlConfig` and `ChainSpec`. We needed to be able to return it in an API response. - Change some uses of serde `serialize_with` and `deserialize_with` to a single use of `with` (code quality). - Impl `Display` and `FromStr` for several BLS fields. - Check for clock discrepancy when VC polls BN for sync state (with +/- 1 slot tolerance). This is not intended to be comprehensive, it was just easy to do. - See #1434 for a per-endpoint overview. - Seeking clarity here: https://github.com/ethereum/eth2.0-APIs/issues/75 - [x] Add docs for prom port to close #1256 - [x] Follow up on this #1177 - [x] ~~Follow up with #1424~~ Will fix in future PR. - [x] Follow up with #1411 - [x] ~~Follow up with #1260~~ Will fix in future PR. - [x] Add quotes to all integers. - [x] Remove `rest_types` - [x] Address missing beacon block error. (#1629) - [x] ~~Add tests for lighthouse/peers endpoints~~ Wontfix - [x] ~~Follow up with validator status proposal~~ Tracked in #1434 - [x] Unify graffiti structs - [x] ~~Start server when waiting for genesis?~~ Will fix in future PR. - [x] TODO in http_api tests - [x] Move lighthouse endpoints off /eth/v1 - [x] Update docs to link to standard - ~~Blocked on #1586~~ Co-authored-by: Michael Sproul <michael@sigmaprime.io>
This commit is contained in:
784
common/eth2/src/lib.rs
Normal file
784
common/eth2/src/lib.rs
Normal file
@@ -0,0 +1,784 @@
|
||||
//! This crate provides two major things:
|
||||
//!
|
||||
//! 1. The types served by the `http_api` crate.
|
||||
//! 2. A wrapper around `reqwest` that forms a HTTP client, able of consuming the endpoints served
|
||||
//! by the `http_api` crate.
|
||||
//!
|
||||
//! Eventually it would be ideal to publish this crate on crates.io, however we have some local
|
||||
//! dependencies preventing this presently.
|
||||
|
||||
#[cfg(feature = "lighthouse")]
|
||||
pub mod lighthouse;
|
||||
pub mod types;
|
||||
|
||||
use self::types::*;
|
||||
use reqwest::{IntoUrl, Response};
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
|
||||
pub use reqwest;
|
||||
pub use reqwest::{StatusCode, Url};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// The `reqwest` client raised an error.
|
||||
Reqwest(reqwest::Error),
|
||||
/// The server returned an error message where the body was able to be parsed.
|
||||
ServerMessage(ErrorMessage),
|
||||
/// The server returned an error message where the body was unable to be parsed.
|
||||
StatusCode(StatusCode),
|
||||
/// The supplied URL is badly formatted. It should look something like `http://127.0.0.1:5052`.
|
||||
InvalidUrl(Url),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// If the error has a HTTP status code, return it.
|
||||
pub fn status(&self) -> Option<StatusCode> {
|
||||
match self {
|
||||
Error::Reqwest(error) => error.status(),
|
||||
Error::ServerMessage(msg) => StatusCode::try_from(msg.code).ok(),
|
||||
Error::StatusCode(status) => Some(*status),
|
||||
Error::InvalidUrl(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{:?}", self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around `reqwest::Client` which provides convenience methods for interfacing with a
|
||||
/// Lighthouse Beacon Node HTTP server (`http_api`).
|
||||
#[derive(Clone)]
|
||||
pub struct BeaconNodeHttpClient {
|
||||
client: reqwest::Client,
|
||||
server: Url,
|
||||
}
|
||||
|
||||
impl BeaconNodeHttpClient {
|
||||
pub fn new(server: Url) -> Self {
|
||||
Self {
|
||||
client: reqwest::Client::new(),
|
||||
server,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_components(server: Url, client: reqwest::Client) -> Self {
|
||||
Self { client, server }
|
||||
}
|
||||
|
||||
/// Return the path with the standard `/eth1/v1` prefix applied.
|
||||
fn eth_path(&self) -> Result<Url, Error> {
|
||||
let mut path = self.server.clone();
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("eth")
|
||||
.push("v1");
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// Perform a HTTP GET request.
|
||||
async fn get<T: DeserializeOwned, U: IntoUrl>(&self, url: U) -> Result<T, Error> {
|
||||
let response = self.client.get(url).send().await.map_err(Error::Reqwest)?;
|
||||
ok_or_error(response)
|
||||
.await?
|
||||
.json()
|
||||
.await
|
||||
.map_err(Error::Reqwest)
|
||||
}
|
||||
|
||||
/// Perform a HTTP GET request, returning `None` on a 404 error.
|
||||
async fn get_opt<T: DeserializeOwned, U: IntoUrl>(&self, url: U) -> Result<Option<T>, Error> {
|
||||
let response = self.client.get(url).send().await.map_err(Error::Reqwest)?;
|
||||
match ok_or_error(response).await {
|
||||
Ok(resp) => resp.json().await.map(Option::Some).map_err(Error::Reqwest),
|
||||
Err(err) => {
|
||||
if err.status() == Some(StatusCode::NOT_FOUND) {
|
||||
Ok(None)
|
||||
} else {
|
||||
Err(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a HTTP POST request.
|
||||
async fn post<T: Serialize, U: IntoUrl>(&self, url: U, body: &T) -> Result<(), Error> {
|
||||
let response = self
|
||||
.client
|
||||
.post(url)
|
||||
.json(body)
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::Reqwest)?;
|
||||
ok_or_error(response).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET beacon/genesis`
|
||||
///
|
||||
/// ## Errors
|
||||
///
|
||||
/// 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()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("genesis");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/states/{state_id}/root`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_states_root(
|
||||
&self,
|
||||
state_id: StateId,
|
||||
) -> Result<Option<GenericResponse<RootData>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("states")
|
||||
.push(&state_id.to_string())
|
||||
.push("root");
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/states/{state_id}/fork`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_states_fork(
|
||||
&self,
|
||||
state_id: StateId,
|
||||
) -> Result<Option<GenericResponse<Fork>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("states")
|
||||
.push(&state_id.to_string())
|
||||
.push("fork");
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/states/{state_id}/finality_checkpoints`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_states_finality_checkpoints(
|
||||
&self,
|
||||
state_id: StateId,
|
||||
) -> Result<Option<GenericResponse<FinalityCheckpointsData>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("states")
|
||||
.push(&state_id.to_string())
|
||||
.push("finality_checkpoints");
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/states/{state_id}/validators`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_states_validators(
|
||||
&self,
|
||||
state_id: StateId,
|
||||
) -> Result<Option<GenericResponse<Vec<ValidatorData>>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("states")
|
||||
.push(&state_id.to_string())
|
||||
.push("validators");
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/states/{state_id}/committees?slot,index`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_states_committees(
|
||||
&self,
|
||||
state_id: StateId,
|
||||
epoch: Epoch,
|
||||
slot: Option<Slot>,
|
||||
index: Option<u64>,
|
||||
) -> Result<Option<GenericResponse<Vec<CommitteeData>>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("states")
|
||||
.push(&state_id.to_string())
|
||||
.push("committees")
|
||||
.push(&epoch.to_string());
|
||||
|
||||
if let Some(slot) = slot {
|
||||
path.query_pairs_mut()
|
||||
.append_pair("slot", &slot.to_string());
|
||||
}
|
||||
|
||||
if let Some(index) = index {
|
||||
path.query_pairs_mut()
|
||||
.append_pair("index", &index.to_string());
|
||||
}
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/states/{state_id}/validators/{validator_id}`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_states_validator_id(
|
||||
&self,
|
||||
state_id: StateId,
|
||||
validator_id: &ValidatorId,
|
||||
) -> Result<Option<GenericResponse<ValidatorData>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("states")
|
||||
.push(&state_id.to_string())
|
||||
.push("validators")
|
||||
.push(&validator_id.to_string());
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/headers?slot,parent_root`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_headers(
|
||||
&self,
|
||||
slot: Option<Slot>,
|
||||
parent_root: Option<Hash256>,
|
||||
) -> Result<Option<GenericResponse<Vec<BlockHeaderData>>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("headers");
|
||||
|
||||
if let Some(slot) = slot {
|
||||
path.query_pairs_mut()
|
||||
.append_pair("slot", &slot.to_string());
|
||||
}
|
||||
|
||||
if let Some(root) = parent_root {
|
||||
path.query_pairs_mut()
|
||||
.append_pair("parent_root", &format!("{:?}", root));
|
||||
}
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/headers/{block_id}`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_headers_block_id(
|
||||
&self,
|
||||
block_id: BlockId,
|
||||
) -> Result<Option<GenericResponse<BlockHeaderData>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("headers")
|
||||
.push(&block_id.to_string());
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `POST beacon/blocks`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn post_beacon_blocks<T: EthSpec>(
|
||||
&self,
|
||||
block: &SignedBeaconBlock<T>,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("blocks");
|
||||
|
||||
self.post(path, block).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET 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()?;
|
||||
|
||||
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 beacon/blocks/{block_id}/root`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_blocks_root(
|
||||
&self,
|
||||
block_id: BlockId,
|
||||
) -> Result<Option<GenericResponse<RootData>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("blocks")
|
||||
.push(&block_id.to_string())
|
||||
.push("root");
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/blocks/{block_id}/attestations`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_blocks_attestations<T: EthSpec>(
|
||||
&self,
|
||||
block_id: BlockId,
|
||||
) -> Result<Option<GenericResponse<Vec<Attestation<T>>>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("blocks")
|
||||
.push(&block_id.to_string())
|
||||
.push("attestations");
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `POST beacon/pool/attestations`
|
||||
pub async fn post_beacon_pool_attestations<T: EthSpec>(
|
||||
&self,
|
||||
attestation: &Attestation<T>,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("attestations");
|
||||
|
||||
self.post(path, attestation).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET beacon/pool/attestations`
|
||||
pub async fn get_beacon_pool_attestations<T: EthSpec>(
|
||||
&self,
|
||||
) -> Result<GenericResponse<Vec<Attestation<T>>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("attestations");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `POST beacon/pool/attester_slashings`
|
||||
pub async fn post_beacon_pool_attester_slashings<T: EthSpec>(
|
||||
&self,
|
||||
slashing: &AttesterSlashing<T>,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("attester_slashings");
|
||||
|
||||
self.post(path, slashing).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET beacon/pool/attester_slashings`
|
||||
pub async fn get_beacon_pool_attester_slashings<T: EthSpec>(
|
||||
&self,
|
||||
) -> Result<GenericResponse<Vec<AttesterSlashing<T>>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("attester_slashings");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `POST beacon/pool/proposer_slashings`
|
||||
pub async fn post_beacon_pool_proposer_slashings(
|
||||
&self,
|
||||
slashing: &ProposerSlashing,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("proposer_slashings");
|
||||
|
||||
self.post(path, slashing).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET beacon/pool/proposer_slashings`
|
||||
pub async fn get_beacon_pool_proposer_slashings(
|
||||
&self,
|
||||
) -> Result<GenericResponse<Vec<ProposerSlashing>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("proposer_slashings");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `POST beacon/pool/voluntary_exits`
|
||||
pub async fn post_beacon_pool_voluntary_exits(
|
||||
&self,
|
||||
exit: &SignedVoluntaryExit,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("voluntary_exits");
|
||||
|
||||
self.post(path, exit).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET beacon/pool/voluntary_exits`
|
||||
pub async fn get_beacon_pool_voluntary_exits(
|
||||
&self,
|
||||
) -> Result<GenericResponse<Vec<SignedVoluntaryExit>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("voluntary_exits");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET config/fork_schedule`
|
||||
pub async fn get_config_fork_schedule(&self) -> Result<GenericResponse<Vec<Fork>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("config")
|
||||
.push("fork_schedule");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET config/fork_schedule`
|
||||
pub async fn get_config_spec(&self) -> Result<GenericResponse<YamlConfig>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("config")
|
||||
.push("spec");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET config/deposit_contract`
|
||||
pub async fn get_config_deposit_contract(
|
||||
&self,
|
||||
) -> Result<GenericResponse<DepositContractData>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("config")
|
||||
.push("deposit_contract");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET node/version`
|
||||
pub async fn get_node_version(&self) -> Result<GenericResponse<VersionData>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("node")
|
||||
.push("version");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET node/syncing`
|
||||
pub async fn get_node_syncing(&self) -> Result<GenericResponse<SyncingData>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("node")
|
||||
.push("syncing");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET 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()?;
|
||||
|
||||
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 debug/beacon/heads`
|
||||
pub async fn get_debug_beacon_heads(
|
||||
&self,
|
||||
) -> Result<GenericResponse<Vec<ChainHeadData>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("debug")
|
||||
.push("beacon")
|
||||
.push("heads");
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET validator/duties/attester/{epoch}?index`
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// The `index` query parameter accepts a list of validator indices.
|
||||
pub async fn get_validator_duties_attester(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
index: Option<&[u64]>,
|
||||
) -> Result<GenericResponse<Vec<AttesterData>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("duties")
|
||||
.push("attester")
|
||||
.push(&epoch.to_string());
|
||||
|
||||
if let Some(index) = index {
|
||||
let string = index
|
||||
.iter()
|
||||
.map(|i| i.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",");
|
||||
path.query_pairs_mut().append_pair("index", &string);
|
||||
}
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET validator/duties/proposer/{epoch}`
|
||||
pub async fn get_validator_duties_proposer(
|
||||
&self,
|
||||
epoch: Epoch,
|
||||
) -> Result<GenericResponse<Vec<ProposerData>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("duties")
|
||||
.push("proposer")
|
||||
.push(&epoch.to_string());
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET validator/duties/attester/{epoch}?index`
|
||||
///
|
||||
/// ## Note
|
||||
///
|
||||
/// The `index` query parameter accepts a list of validator indices.
|
||||
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()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("blocks")
|
||||
.push(&slot.to_string());
|
||||
|
||||
path.query_pairs_mut()
|
||||
.append_pair("randao_reveal", &randao_reveal.to_string());
|
||||
|
||||
if let Some(graffiti) = graffiti {
|
||||
path.query_pairs_mut()
|
||||
.append_pair("graffiti", &graffiti.to_string());
|
||||
}
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET validator/attestation_data?slot,committee_index`
|
||||
pub async fn get_validator_attestation_data(
|
||||
&self,
|
||||
slot: Slot,
|
||||
committee_index: CommitteeIndex,
|
||||
) -> Result<GenericResponse<AttestationData>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("attestation_data");
|
||||
|
||||
path.query_pairs_mut()
|
||||
.append_pair("slot", &slot.to_string())
|
||||
.append_pair("committee_index", &committee_index.to_string());
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET validator/attestation_attestation?slot,attestation_data_root`
|
||||
pub async fn get_validator_aggregate_attestation<T: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
attestation_data_root: Hash256,
|
||||
) -> Result<Option<GenericResponse<Attestation<T>>>, Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("aggregate_attestation");
|
||||
|
||||
path.query_pairs_mut()
|
||||
.append_pair("slot", &slot.to_string())
|
||||
.append_pair(
|
||||
"attestation_data_root",
|
||||
&format!("{:?}", attestation_data_root),
|
||||
);
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `POST validator/aggregate_and_proofs`
|
||||
pub async fn post_validator_aggregate_and_proof<T: EthSpec>(
|
||||
&self,
|
||||
aggregate: &SignedAggregateAndProof<T>,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("aggregate_and_proofs");
|
||||
|
||||
self.post(path, aggregate).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `POST validator/beacon_committee_subscriptions`
|
||||
pub async fn post_validator_beacon_committee_subscriptions(
|
||||
&self,
|
||||
subscriptions: &[BeaconCommitteeSubscription],
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path()?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("beacon_committee_subscriptions");
|
||||
|
||||
self.post(path, &subscriptions).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns `Ok(response)` if the response is a `200 OK` response. Otherwise, creates an
|
||||
/// appropriate error message.
|
||||
async fn ok_or_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::ServerMessage(message))
|
||||
} else {
|
||||
Err(Error::StatusCode(status))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user