2020-09-29 03:46:54 +00:00
|
|
|
//! 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;
|
2020-10-02 09:42:19 +00:00
|
|
|
pub mod lighthouse_vc;
|
2021-10-28 01:18:04 +00:00
|
|
|
pub mod mixin;
|
2020-09-29 03:46:54 +00:00
|
|
|
pub mod types;
|
|
|
|
|
2021-10-28 01:18:04 +00:00
|
|
|
use self::mixin::{RequestAccept, ResponseForkName, ResponseOptional};
|
2021-08-06 00:47:31 +00:00
|
|
|
use self::types::{Error as ResponseError, *};
|
2021-10-28 01:18:04 +00:00
|
|
|
use ::types::map_fork_name_with;
|
2020-12-04 00:18:58 +00:00
|
|
|
use futures::Stream;
|
|
|
|
use futures_util::StreamExt;
|
Rename eth2_libp2p to lighthouse_network (#2702)
## Description
The `eth2_libp2p` crate was originally named and designed to incorporate a simple libp2p integration into lighthouse. Since its origins the crates purpose has expanded dramatically. It now houses a lot more sophistication that is specific to lighthouse and no longer just a libp2p integration.
As of this writing it currently houses the following high-level lighthouse-specific logic:
- Lighthouse's implementation of the eth2 RPC protocol and specific encodings/decodings
- Integration and handling of ENRs with respect to libp2p and eth2
- Lighthouse's discovery logic, its integration with discv5 and logic about searching and handling peers.
- Lighthouse's peer manager - This is a large module handling various aspects of Lighthouse's network, such as peer scoring, handling pings and metadata, connection maintenance and recording, etc.
- Lighthouse's peer database - This is a collection of information stored for each individual peer which is specific to lighthouse. We store connection state, sync state, last seen ips and scores etc. The data stored for each peer is designed for various elements of the lighthouse code base such as syncing and the http api.
- Gossipsub scoring - This stores a collection of gossipsub 1.1 scoring mechanisms that are continuously analyssed and updated based on the ethereum 2 networks and how Lighthouse performs on these networks.
- Lighthouse specific types for managing gossipsub topics, sync status and ENR fields
- Lighthouse's network HTTP API metrics - A collection of metrics for lighthouse network monitoring
- Lighthouse's custom configuration of all networking protocols, RPC, gossipsub, discovery, identify and libp2p.
Therefore it makes sense to rename the crate to be more akin to its current purposes, simply that it manages the majority of Lighthouse's network stack. This PR renames this crate to `lighthouse_network`
Co-authored-by: Paul Hauner <paul@paulhauner.com>
2021-10-19 00:30:39 +00:00
|
|
|
use lighthouse_network::PeerId;
|
2020-12-04 00:18:58 +00:00
|
|
|
pub use reqwest;
|
2021-10-28 01:18:04 +00:00
|
|
|
use reqwest::{IntoUrl, RequestBuilder, Response};
|
2020-12-04 00:18:58 +00:00
|
|
|
pub use reqwest::{StatusCode, Url};
|
2021-05-04 01:59:51 +00:00
|
|
|
use sensitive_url::SensitiveUrl;
|
2020-09-29 03:46:54 +00:00
|
|
|
use serde::{de::DeserializeOwned, Serialize};
|
|
|
|
use std::convert::TryFrom;
|
|
|
|
use std::fmt;
|
2020-12-04 00:18:58 +00:00
|
|
|
use std::iter::Iterator;
|
2021-07-12 01:47:48 +00:00
|
|
|
use std::time::Duration;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
pub const V1: EndpointVersion = EndpointVersion(1);
|
|
|
|
pub const V2: EndpointVersion = EndpointVersion(2);
|
|
|
|
|
2021-10-28 01:18:04 +00:00
|
|
|
pub const CONSENSUS_VERSION_HEADER: &str = "Eth-Consensus-Version";
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
#[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),
|
2020-11-09 23:13:56 +00:00
|
|
|
/// The server returned an error message with an array of errors.
|
|
|
|
ServerIndexedMessage(IndexedErrorMessage),
|
2020-09-29 03:46:54 +00:00
|
|
|
/// 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`.
|
2021-05-04 01:59:51 +00:00
|
|
|
InvalidUrl(SensitiveUrl),
|
2020-10-02 09:42:19 +00:00
|
|
|
/// The supplied validator client secret is invalid.
|
|
|
|
InvalidSecret(String),
|
|
|
|
/// The server returned a response with an invalid signature. It may be an impostor.
|
|
|
|
InvalidSignatureHeader,
|
|
|
|
/// The server returned a response without a signature header. It may be an impostor.
|
|
|
|
MissingSignatureHeader,
|
|
|
|
/// The server returned an invalid JSON response.
|
|
|
|
InvalidJson(serde_json::Error),
|
2020-12-04 00:18:58 +00:00
|
|
|
/// The server returned an invalid server-sent event.
|
|
|
|
InvalidServerSentEvent(String),
|
2020-10-22 06:05:49 +00:00
|
|
|
/// The server returned an invalid SSZ response.
|
|
|
|
InvalidSsz(ssz::DecodeError),
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 01:18:04 +00:00
|
|
|
impl From<reqwest::Error> for Error {
|
|
|
|
fn from(error: reqwest::Error) -> Self {
|
|
|
|
Error::Reqwest(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
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(),
|
2020-11-09 23:13:56 +00:00
|
|
|
Error::ServerIndexedMessage(msg) => StatusCode::try_from(msg.code).ok(),
|
2020-09-29 03:46:54 +00:00
|
|
|
Error::StatusCode(status) => Some(*status),
|
|
|
|
Error::InvalidUrl(_) => None,
|
2020-10-02 09:42:19 +00:00
|
|
|
Error::InvalidSecret(_) => None,
|
|
|
|
Error::InvalidSignatureHeader => None,
|
|
|
|
Error::MissingSignatureHeader => None,
|
|
|
|
Error::InvalidJson(_) => None,
|
2020-12-04 00:18:58 +00:00
|
|
|
Error::InvalidServerSentEvent(_) => None,
|
2020-10-22 06:05:49 +00:00
|
|
|
Error::InvalidSsz(_) => None,
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for Error {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "{:?}", self)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
/// A struct to define a variety of different timeouts for different validator tasks to ensure
|
|
|
|
/// proper fallback behaviour.
|
|
|
|
#[derive(Clone)]
|
|
|
|
pub struct Timeouts {
|
|
|
|
pub attestation: Duration,
|
|
|
|
pub attester_duties: Duration,
|
2021-07-31 03:50:52 +00:00
|
|
|
pub liveness: Duration,
|
2021-07-12 01:47:48 +00:00
|
|
|
pub proposal: Duration,
|
|
|
|
pub proposer_duties: Duration,
|
2021-08-06 00:47:31 +00:00
|
|
|
pub sync_duties: Duration,
|
2021-07-12 01:47:48 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Timeouts {
|
|
|
|
pub fn set_all(timeout: Duration) -> Self {
|
|
|
|
Timeouts {
|
|
|
|
attestation: timeout,
|
|
|
|
attester_duties: timeout,
|
2021-07-31 03:50:52 +00:00
|
|
|
liveness: timeout,
|
2021-07-12 01:47:48 +00:00
|
|
|
proposal: timeout,
|
|
|
|
proposer_duties: timeout,
|
2021-08-06 00:47:31 +00:00
|
|
|
sync_duties: timeout,
|
2021-07-12 01:47:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// 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,
|
2021-05-04 01:59:51 +00:00
|
|
|
server: SensitiveUrl,
|
2021-07-12 01:47:48 +00:00
|
|
|
timeouts: Timeouts,
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
|
2020-12-18 09:17:03 +00:00
|
|
|
impl fmt::Display for BeaconNodeHttpClient {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
self.server.fmt(f)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl AsRef<str> for BeaconNodeHttpClient {
|
|
|
|
fn as_ref(&self) -> &str {
|
2021-05-04 01:59:51 +00:00
|
|
|
self.server.as_ref()
|
2020-12-18 09:17:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
impl BeaconNodeHttpClient {
|
2021-07-12 01:47:48 +00:00
|
|
|
pub fn new(server: SensitiveUrl, timeouts: Timeouts) -> Self {
|
2020-09-29 03:46:54 +00:00
|
|
|
Self {
|
|
|
|
client: reqwest::Client::new(),
|
|
|
|
server,
|
2021-07-12 01:47:48 +00:00
|
|
|
timeouts,
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
pub fn from_components(
|
|
|
|
server: SensitiveUrl,
|
|
|
|
client: reqwest::Client,
|
|
|
|
timeouts: Timeouts,
|
|
|
|
) -> Self {
|
|
|
|
Self {
|
|
|
|
client,
|
|
|
|
server,
|
|
|
|
timeouts,
|
|
|
|
}
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
/// Return the path with the standard `/eth/vX` prefix applied.
|
|
|
|
fn eth_path(&self, version: EndpointVersion) -> Result<Url, Error> {
|
2021-05-04 01:59:51 +00:00
|
|
|
let mut path = self.server.full.clone();
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("eth")
|
2021-08-06 00:47:31 +00:00
|
|
|
.push(&version.to_string());
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
Ok(path)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform a HTTP GET request.
|
|
|
|
async fn get<T: DeserializeOwned, U: IntoUrl>(&self, url: U) -> Result<T, Error> {
|
2021-10-28 01:18:04 +00:00
|
|
|
let response = self.get_response(url, |b| b).await?;
|
|
|
|
Ok(response.json().await?)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform an HTTP GET request, returning the `Response` for processing.
|
|
|
|
pub async fn get_response<U: IntoUrl>(
|
|
|
|
&self,
|
|
|
|
url: U,
|
|
|
|
builder: impl FnOnce(RequestBuilder) -> RequestBuilder,
|
|
|
|
) -> Result<Response, Error> {
|
|
|
|
let response = builder(self.client.get(url)).send().await?;
|
|
|
|
ok_or_error(response).await
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
/// Perform a HTTP GET request with a custom timeout.
|
|
|
|
async fn get_with_timeout<T: DeserializeOwned, U: IntoUrl>(
|
|
|
|
&self,
|
|
|
|
url: U,
|
|
|
|
timeout: Duration,
|
|
|
|
) -> Result<T, Error> {
|
|
|
|
let response = self
|
2021-10-28 01:18:04 +00:00
|
|
|
.get_response(url, |builder| builder.timeout(timeout))
|
|
|
|
.await?;
|
|
|
|
Ok(response.json().await?)
|
2021-07-12 01:47:48 +00:00
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// 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> {
|
2021-10-28 01:18:04 +00:00
|
|
|
match self.get_response(url, |b| b).await.optional()? {
|
|
|
|
Some(response) => Ok(Some(response.json().await?)),
|
|
|
|
None => Ok(None),
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
/// Perform a HTTP GET request with a custom timeout, returning `None` on a 404 error.
|
|
|
|
async fn get_opt_with_timeout<T: DeserializeOwned, U: IntoUrl>(
|
|
|
|
&self,
|
|
|
|
url: U,
|
|
|
|
timeout: Duration,
|
|
|
|
) -> Result<Option<T>, Error> {
|
2021-10-28 01:18:04 +00:00
|
|
|
let opt_response = self
|
|
|
|
.get_response(url, |b| b.timeout(timeout))
|
2021-07-12 01:47:48 +00:00
|
|
|
.await
|
2021-10-28 01:18:04 +00:00
|
|
|
.optional()?;
|
|
|
|
match opt_response {
|
|
|
|
Some(response) => Ok(Some(response.json().await?)),
|
|
|
|
None => Ok(None),
|
2021-07-12 01:47:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-06 03:01:46 +00:00
|
|
|
/// Perform a HTTP GET request using an 'accept' header, returning `None` on a 404 error.
|
|
|
|
pub async fn get_bytes_opt_accept_header<U: IntoUrl>(
|
|
|
|
&self,
|
|
|
|
url: U,
|
|
|
|
accept_header: Accept,
|
|
|
|
) -> Result<Option<Vec<u8>>, Error> {
|
2021-10-28 01:18:04 +00:00
|
|
|
let opt_response = self
|
|
|
|
.get_response(url, |b| b.accept(accept_header))
|
2021-01-06 03:01:46 +00:00
|
|
|
.await
|
2021-10-28 01:18:04 +00:00
|
|
|
.optional()?;
|
|
|
|
match opt_response {
|
|
|
|
Some(resp) => Ok(Some(resp.bytes().await?.into_iter().collect::<Vec<_>>())),
|
|
|
|
None => Ok(None),
|
2021-01-06 03:01:46 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// Perform a HTTP POST request.
|
|
|
|
async fn post<T: Serialize, U: IntoUrl>(&self, url: U, body: &T) -> Result<(), Error> {
|
2021-09-22 00:37:28 +00:00
|
|
|
self.post_generic(url, body, None).await?;
|
2020-09-29 03:46:54 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-09-22 00:37:28 +00:00
|
|
|
/// Perform a HTTP POST request, returning a JSON response.
|
|
|
|
async fn post_with_response<T: Serialize, U: IntoUrl, R: DeserializeOwned>(
|
|
|
|
&self,
|
|
|
|
url: U,
|
|
|
|
body: &T,
|
|
|
|
) -> Result<R, Error> {
|
|
|
|
self.post_generic(url, body, None)
|
|
|
|
.await?
|
|
|
|
.json()
|
|
|
|
.await
|
|
|
|
.map_err(Error::Reqwest)
|
|
|
|
}
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
/// Perform a HTTP POST request with a custom timeout.
|
|
|
|
async fn post_with_timeout<T: Serialize, U: IntoUrl>(
|
|
|
|
&self,
|
|
|
|
url: U,
|
|
|
|
body: &T,
|
|
|
|
timeout: Duration,
|
|
|
|
) -> Result<(), Error> {
|
2021-09-22 00:37:28 +00:00
|
|
|
self.post_generic(url, body, Some(timeout)).await?;
|
2021-07-12 01:47:48 +00:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Perform a HTTP POST request with a custom timeout, returning a JSON response.
|
|
|
|
async fn post_with_timeout_and_response<T: DeserializeOwned, U: IntoUrl, V: Serialize>(
|
2020-11-09 23:13:56 +00:00
|
|
|
&self,
|
|
|
|
url: U,
|
|
|
|
body: &V,
|
2021-07-12 01:47:48 +00:00
|
|
|
timeout: Duration,
|
2020-11-09 23:13:56 +00:00
|
|
|
) -> Result<T, Error> {
|
2021-09-22 00:37:28 +00:00
|
|
|
self.post_generic(url, body, Some(timeout))
|
2020-11-09 23:13:56 +00:00
|
|
|
.await?
|
|
|
|
.json()
|
|
|
|
.await
|
|
|
|
.map_err(Error::Reqwest)
|
|
|
|
}
|
|
|
|
|
2021-09-22 00:37:28 +00:00
|
|
|
/// Generic POST function supporting arbitrary responses and timeouts.
|
|
|
|
async fn post_generic<T: Serialize, U: IntoUrl>(
|
|
|
|
&self,
|
|
|
|
url: U,
|
|
|
|
body: &T,
|
|
|
|
timeout: Option<Duration>,
|
|
|
|
) -> Result<Response, Error> {
|
|
|
|
let mut builder = self.client.post(url);
|
|
|
|
if let Some(timeout) = timeout {
|
|
|
|
builder = builder.timeout(timeout);
|
|
|
|
}
|
2021-10-28 01:18:04 +00:00
|
|
|
let response = builder.json(body).send().await?;
|
2021-09-22 00:37:28 +00:00
|
|
|
ok_or_error(response).await
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// `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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:13:56 +00:00
|
|
|
/// `GET beacon/states/{state_id}/validator_balances?id`
|
|
|
|
///
|
|
|
|
/// Returns `Ok(None)` on a 404 error.
|
|
|
|
pub async fn get_beacon_states_validator_balances(
|
|
|
|
&self,
|
|
|
|
state_id: StateId,
|
|
|
|
ids: Option<&[ValidatorId]>,
|
|
|
|
) -> Result<Option<GenericResponse<Vec<ValidatorBalanceData>>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-11-09 23:13:56 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("beacon")
|
|
|
|
.push("states")
|
|
|
|
.push(&state_id.to_string())
|
|
|
|
.push("validator_balances");
|
|
|
|
|
|
|
|
if let Some(ids) = ids {
|
|
|
|
let id_string = ids
|
|
|
|
.iter()
|
|
|
|
.map(|i| i.to_string())
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(",");
|
|
|
|
path.query_pairs_mut().append_pair("id", &id_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.get_opt(path).await
|
|
|
|
}
|
|
|
|
|
2020-10-29 05:13:04 +00:00
|
|
|
/// `GET beacon/states/{state_id}/validators?id,status`
|
2020-09-29 03:46:54 +00:00
|
|
|
///
|
|
|
|
/// Returns `Ok(None)` on a 404 error.
|
|
|
|
pub async fn get_beacon_states_validators(
|
|
|
|
&self,
|
|
|
|
state_id: StateId,
|
2020-10-29 05:13:04 +00:00
|
|
|
ids: Option<&[ValidatorId]>,
|
|
|
|
statuses: Option<&[ValidatorStatus]>,
|
2020-09-29 03:46:54 +00:00
|
|
|
) -> Result<Option<GenericResponse<Vec<ValidatorData>>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("beacon")
|
|
|
|
.push("states")
|
|
|
|
.push(&state_id.to_string())
|
|
|
|
.push("validators");
|
|
|
|
|
2020-10-29 05:13:04 +00:00
|
|
|
if let Some(ids) = ids {
|
|
|
|
let id_string = ids
|
|
|
|
.iter()
|
|
|
|
.map(|i| i.to_string())
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(",");
|
|
|
|
path.query_pairs_mut().append_pair("id", &id_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(statuses) = statuses {
|
|
|
|
let status_string = statuses
|
|
|
|
.iter()
|
|
|
|
.map(|i| i.to_string())
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(",");
|
|
|
|
path.query_pairs_mut().append_pair("status", &status_string);
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
self.get_opt(path).await
|
|
|
|
}
|
|
|
|
|
2020-11-18 23:31:39 +00:00
|
|
|
/// `GET beacon/states/{state_id}/committees?slot,index,epoch`
|
2020-09-29 03:46:54 +00:00
|
|
|
///
|
|
|
|
/// Returns `Ok(None)` on a 404 error.
|
|
|
|
pub async fn get_beacon_states_committees(
|
|
|
|
&self,
|
|
|
|
state_id: StateId,
|
|
|
|
slot: Option<Slot>,
|
|
|
|
index: Option<u64>,
|
2020-11-18 23:31:39 +00:00
|
|
|
epoch: Option<Epoch>,
|
2020-09-29 03:46:54 +00:00
|
|
|
) -> Result<Option<GenericResponse<Vec<CommitteeData>>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("beacon")
|
|
|
|
.push("states")
|
|
|
|
.push(&state_id.to_string())
|
2020-11-18 23:31:39 +00:00
|
|
|
.push("committees");
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
|
2020-11-18 23:31:39 +00:00
|
|
|
if let Some(epoch) = epoch {
|
|
|
|
path.query_pairs_mut()
|
|
|
|
.append_pair("epoch", &epoch.to_string());
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
self.get_opt(path).await
|
|
|
|
}
|
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
/// `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
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// `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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("beacon")
|
|
|
|
.push("blocks");
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
self.post_with_timeout(path, block, self.timeouts.proposal)
|
|
|
|
.await?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2021-10-28 01:18:04 +00:00
|
|
|
/// Path for `v2/beacon/blocks`
|
|
|
|
pub fn get_beacon_blocks_path(&self, block_id: BlockId) -> Result<Url, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
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());
|
2021-10-28 01:18:04 +00:00
|
|
|
Ok(path)
|
|
|
|
}
|
2021-08-06 00:47:31 +00:00
|
|
|
|
2021-10-28 01:18:04 +00:00
|
|
|
/// `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<ForkVersionedResponse<SignedBeaconBlock<T>>>, Error> {
|
|
|
|
let path = self.get_beacon_blocks_path(block_id)?;
|
|
|
|
let response = match self.get_response(path, |b| b).await.optional()? {
|
|
|
|
Some(res) => res,
|
|
|
|
None => return Ok(None),
|
|
|
|
};
|
|
|
|
|
|
|
|
// If present, use the fork provided in the headers to decode the block. Gracefully handle
|
|
|
|
// missing and malformed fork names by falling back to regular deserialisation.
|
|
|
|
let (block, version) = match response.fork_name_from_header() {
|
|
|
|
Ok(Some(fork_name)) => {
|
|
|
|
map_fork_name_with!(fork_name, SignedBeaconBlock, {
|
|
|
|
let ForkVersionedResponse { version, data } = response.json().await?;
|
|
|
|
(data, version)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
Ok(None) | Err(_) => {
|
|
|
|
let ForkVersionedResponse { version, data } = response.json().await?;
|
|
|
|
(data, version)
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Ok(Some(ForkVersionedResponse {
|
|
|
|
version,
|
|
|
|
data: block,
|
|
|
|
}))
|
2021-08-06 00:47:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// `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)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-02-24 04:15:14 +00:00
|
|
|
/// `GET beacon/blocks` as SSZ
|
|
|
|
///
|
|
|
|
/// Returns `Ok(None)` on a 404 error.
|
|
|
|
pub async fn get_beacon_blocks_ssz<T: EthSpec>(
|
|
|
|
&self,
|
|
|
|
block_id: BlockId,
|
2021-07-09 06:15:32 +00:00
|
|
|
spec: &ChainSpec,
|
2021-02-24 04:15:14 +00:00
|
|
|
) -> Result<Option<SignedBeaconBlock<T>>, Error> {
|
2021-10-28 01:18:04 +00:00
|
|
|
let path = self.get_beacon_blocks_path(block_id)?;
|
2021-02-24 04:15:14 +00:00
|
|
|
|
|
|
|
self.get_bytes_opt_accept_header(path, Accept::Ssz)
|
|
|
|
.await?
|
2021-07-09 06:15:32 +00:00
|
|
|
.map(|bytes| SignedBeaconBlock::from_ssz_bytes(&bytes, spec).map_err(Error::InvalidSsz))
|
2021-02-24 04:15:14 +00:00
|
|
|
.transpose()
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// `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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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,
|
2020-11-18 23:31:39 +00:00
|
|
|
attestations: &[Attestation<T>],
|
2020-09-29 03:46:54 +00:00
|
|
|
) -> Result<(), Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("beacon")
|
|
|
|
.push("pool")
|
|
|
|
.push("attestations");
|
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
self.post_with_timeout(path, &attestations, self.timeouts.attestation)
|
|
|
|
.await?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2020-11-18 23:31:39 +00:00
|
|
|
/// `GET beacon/pool/attestations?slot,committee_index`
|
2020-09-29 03:46:54 +00:00
|
|
|
pub async fn get_beacon_pool_attestations<T: EthSpec>(
|
|
|
|
&self,
|
2020-11-18 23:31:39 +00:00
|
|
|
slot: Option<Slot>,
|
|
|
|
committee_index: Option<u64>,
|
2020-09-29 03:46:54 +00:00
|
|
|
) -> Result<GenericResponse<Vec<Attestation<T>>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("beacon")
|
|
|
|
.push("pool")
|
|
|
|
.push("attestations");
|
|
|
|
|
2020-11-18 23:31:39 +00:00
|
|
|
if let Some(slot) = slot {
|
|
|
|
path.query_pairs_mut()
|
|
|
|
.append_pair("slot", &slot.to_string());
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(index) = committee_index {
|
|
|
|
path.query_pairs_mut()
|
|
|
|
.append_pair("committee_index", &index.to_string());
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("beacon")
|
|
|
|
.push("pool")
|
|
|
|
.push("voluntary_exits");
|
|
|
|
|
|
|
|
self.get(path).await
|
|
|
|
}
|
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
/// `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(())
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// `GET config/fork_schedule`
|
|
|
|
pub async fn get_config_fork_schedule(&self) -> Result<GenericResponse<Vec<Fork>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("config")
|
|
|
|
.push("fork_schedule");
|
|
|
|
|
|
|
|
self.get(path).await
|
|
|
|
}
|
|
|
|
|
2020-10-02 09:42:19 +00:00
|
|
|
/// `GET config/spec`
|
2021-07-09 06:15:32 +00:00
|
|
|
pub async fn get_config_spec(&self) -> Result<GenericResponse<ConfigAndPreset>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("node")
|
|
|
|
.push("version");
|
|
|
|
|
|
|
|
self.get(path).await
|
|
|
|
}
|
|
|
|
|
2020-10-22 02:59:42 +00:00
|
|
|
/// `GET node/identity`
|
|
|
|
pub async fn get_node_identity(&self) -> Result<GenericResponse<IdentityData>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-10-22 02:59:42 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("node")
|
|
|
|
.push("identity");
|
|
|
|
|
|
|
|
self.get(path).await
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// `GET node/syncing`
|
|
|
|
pub async fn get_node_syncing(&self) -> Result<GenericResponse<SyncingData>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("node")
|
|
|
|
.push("syncing");
|
|
|
|
|
|
|
|
self.get(path).await
|
2020-10-22 02:59:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// `GET node/health`
|
|
|
|
pub async fn get_node_health(&self) -> Result<StatusCode, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-10-22 02:59:42 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("node")
|
|
|
|
.push("health");
|
|
|
|
|
2021-10-28 01:18:04 +00:00
|
|
|
let status = self.client.get(path).send().await?.status();
|
2020-10-22 02:59:42 +00:00
|
|
|
if status == StatusCode::OK || status == StatusCode::PARTIAL_CONTENT {
|
|
|
|
Ok(status)
|
|
|
|
} else {
|
|
|
|
Err(Error::StatusCode(status))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `GET node/peers/{peer_id}`
|
|
|
|
pub async fn get_node_peers_by_id(
|
|
|
|
&self,
|
|
|
|
peer_id: PeerId,
|
|
|
|
) -> Result<GenericResponse<PeerData>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-10-22 02:59:42 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("node")
|
|
|
|
.push("peers")
|
|
|
|
.push(&peer_id.to_string());
|
|
|
|
|
|
|
|
self.get(path).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `GET node/peers`
|
2020-11-13 02:02:41 +00:00
|
|
|
pub async fn get_node_peers(
|
|
|
|
&self,
|
|
|
|
states: Option<&[PeerState]>,
|
|
|
|
directions: Option<&[PeerDirection]>,
|
|
|
|
) -> Result<PeersData, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-10-22 02:59:42 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("node")
|
|
|
|
.push("peers");
|
|
|
|
|
2020-11-13 02:02:41 +00:00
|
|
|
if let Some(states) = states {
|
|
|
|
let state_string = states
|
|
|
|
.iter()
|
|
|
|
.map(|i| i.to_string())
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(",");
|
|
|
|
path.query_pairs_mut().append_pair("state", &state_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(directions) = directions {
|
|
|
|
let dir_string = directions
|
|
|
|
.iter()
|
|
|
|
.map(|i| i.to_string())
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(",");
|
|
|
|
path.query_pairs_mut().append_pair("direction", &dir_string);
|
|
|
|
}
|
|
|
|
|
|
|
|
self.get(path).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `GET node/peer_count`
|
|
|
|
pub async fn get_node_peer_count(&self) -> Result<GenericResponse<PeerCount>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-11-13 02:02:41 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("node")
|
|
|
|
.push("peer_count");
|
|
|
|
|
2020-10-22 02:59:42 +00:00
|
|
|
self.get(path).await
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
|
2021-10-28 01:18:04 +00:00
|
|
|
/// URL path for `v2/debug/beacon/states/{state_id}`.
|
|
|
|
pub fn get_debug_beacon_states_path(&self, state_id: StateId) -> Result<Url, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
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());
|
2021-10-28 01:18:04 +00:00
|
|
|
Ok(path)
|
|
|
|
}
|
2021-08-06 00:47:31 +00:00
|
|
|
|
2021-10-28 01:18:04 +00:00
|
|
|
/// `GET v2/debug/beacon/states/{state_id}`
|
|
|
|
pub async fn get_debug_beacon_states<T: EthSpec>(
|
|
|
|
&self,
|
|
|
|
state_id: StateId,
|
|
|
|
) -> Result<Option<ForkVersionedResponse<BeaconState<T>>>, Error> {
|
|
|
|
let path = self.get_debug_beacon_states_path(state_id)?;
|
2021-08-06 00:47:31 +00:00
|
|
|
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)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2021-01-06 03:01:46 +00:00
|
|
|
/// `GET debug/beacon/states/{state_id}`
|
|
|
|
/// `-H "accept: application/octet-stream"`
|
|
|
|
pub async fn get_debug_beacon_states_ssz<T: EthSpec>(
|
|
|
|
&self,
|
|
|
|
state_id: StateId,
|
2021-07-09 06:15:32 +00:00
|
|
|
spec: &ChainSpec,
|
2021-01-06 03:01:46 +00:00
|
|
|
) -> Result<Option<BeaconState<T>>, Error> {
|
2021-10-28 01:18:04 +00:00
|
|
|
let path = self.get_debug_beacon_states_path(state_id)?;
|
2021-01-06 03:01:46 +00:00
|
|
|
|
|
|
|
self.get_bytes_opt_accept_header(path, Accept::Ssz)
|
|
|
|
.await?
|
2021-07-09 06:15:32 +00:00
|
|
|
.map(|bytes| BeaconState::from_ssz_bytes(&bytes, spec).map_err(Error::InvalidSsz))
|
2021-01-06 03:01:46 +00:00
|
|
|
.transpose()
|
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// `GET debug/beacon/heads`
|
|
|
|
pub async fn get_debug_beacon_heads(
|
|
|
|
&self,
|
|
|
|
) -> Result<GenericResponse<Vec<ChainHeadData>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("debug")
|
|
|
|
.push("beacon")
|
|
|
|
.push("heads");
|
|
|
|
|
|
|
|
self.get(path).await
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `GET validator/duties/proposer/{epoch}`
|
|
|
|
pub async fn get_validator_duties_proposer(
|
|
|
|
&self,
|
|
|
|
epoch: Epoch,
|
2020-12-04 00:18:58 +00:00
|
|
|
) -> Result<DutiesResponse<Vec<ProposerData>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("validator")
|
|
|
|
.push("duties")
|
|
|
|
.push("proposer")
|
|
|
|
.push(&epoch.to_string());
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
self.get_with_timeout(path, self.timeouts.proposer_duties)
|
|
|
|
.await
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
/// `GET v2/validator/blocks/{slot}`
|
2020-09-29 03:46:54 +00:00
|
|
|
pub async fn get_validator_blocks<T: EthSpec>(
|
|
|
|
&self,
|
|
|
|
slot: Slot,
|
2020-12-18 09:17:03 +00:00
|
|
|
randao_reveal: &SignatureBytes,
|
2020-09-29 03:46:54 +00:00
|
|
|
graffiti: Option<&Graffiti>,
|
2021-08-06 00:47:31 +00:00
|
|
|
) -> Result<ForkVersionedResponse<BeaconBlock<T>>, Error> {
|
|
|
|
let mut path = self.eth_path(V2)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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());
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
self.get_with_timeout(path, self.timeouts.attestation).await
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
/// `GET validator/aggregate_attestation?slot,attestation_data_root`
|
2020-09-29 03:46:54 +00:00
|
|
|
pub async fn get_validator_aggregate_attestation<T: EthSpec>(
|
|
|
|
&self,
|
|
|
|
slot: Slot,
|
|
|
|
attestation_data_root: Hash256,
|
|
|
|
) -> Result<Option<GenericResponse<Attestation<T>>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
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),
|
|
|
|
);
|
|
|
|
|
2021-07-12 01:47:48 +00:00
|
|
|
self.get_opt_with_timeout(path, self.timeouts.attestation)
|
|
|
|
.await
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
/// `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
|
|
|
|
}
|
|
|
|
|
2021-07-31 03:50:52 +00:00
|
|
|
/// `POST lighthouse/liveness`
|
|
|
|
pub async fn post_lighthouse_liveness(
|
|
|
|
&self,
|
|
|
|
ids: &[u64],
|
|
|
|
epoch: Epoch,
|
|
|
|
) -> Result<GenericResponse<Vec<LivenessResponseData>>, Error> {
|
|
|
|
let mut path = self.server.full.clone();
|
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("lighthouse")
|
|
|
|
.push("liveness");
|
|
|
|
|
|
|
|
self.post_with_timeout_and_response(
|
|
|
|
path,
|
|
|
|
&LivenessRequestData {
|
|
|
|
indices: ids.to_vec(),
|
|
|
|
epoch,
|
|
|
|
},
|
|
|
|
self.timeouts.liveness,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
2020-11-09 23:13:56 +00:00
|
|
|
/// `POST validator/duties/attester/{epoch}`
|
|
|
|
pub async fn post_validator_duties_attester(
|
|
|
|
&self,
|
|
|
|
epoch: Epoch,
|
|
|
|
indices: &[u64],
|
2020-12-04 00:18:58 +00:00
|
|
|
) -> Result<DutiesResponse<Vec<AttesterData>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-11-09 23:13:56 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("validator")
|
|
|
|
.push("duties")
|
|
|
|
.push("attester")
|
|
|
|
.push(&epoch.to_string());
|
|
|
|
|
2022-01-21 06:07:20 +00:00
|
|
|
self.post_with_timeout_and_response(
|
|
|
|
path,
|
|
|
|
&ValidatorIndexDataRef(indices),
|
|
|
|
self.timeouts.attester_duties,
|
|
|
|
)
|
|
|
|
.await
|
2020-11-09 23:13:56 +00:00
|
|
|
}
|
|
|
|
|
2020-09-29 03:46:54 +00:00
|
|
|
/// `POST validator/aggregate_and_proofs`
|
|
|
|
pub async fn post_validator_aggregate_and_proof<T: EthSpec>(
|
|
|
|
&self,
|
2020-11-09 23:13:56 +00:00
|
|
|
aggregates: &[SignedAggregateAndProof<T>],
|
2020-09-29 03:46:54 +00:00
|
|
|
) -> Result<(), Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("validator")
|
|
|
|
.push("aggregate_and_proofs");
|
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
self.post_with_timeout(path, &aggregates, self.timeouts.attestation)
|
|
|
|
.await?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
/// `POST validator/beacon_committee_subscriptions`
|
|
|
|
pub async fn post_validator_beacon_committee_subscriptions(
|
|
|
|
&self,
|
|
|
|
subscriptions: &[BeaconCommitteeSubscription],
|
|
|
|
) -> Result<(), Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("validator")
|
|
|
|
.push("beacon_committee_subscriptions");
|
|
|
|
|
|
|
|
self.post(path, &subscriptions).await?;
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
2020-12-04 00:18:58 +00:00
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
/// `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(())
|
|
|
|
}
|
|
|
|
|
2020-12-04 00:18:58 +00:00
|
|
|
/// `GET events?topics`
|
|
|
|
pub async fn get_events<T: EthSpec>(
|
|
|
|
&self,
|
|
|
|
topic: &[EventTopic],
|
|
|
|
) -> Result<impl Stream<Item = Result<EventKind<T>, Error>>, Error> {
|
2021-08-06 00:47:31 +00:00
|
|
|
let mut path = self.eth_path(V1)?;
|
2020-12-04 00:18:58 +00:00
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("events");
|
|
|
|
|
|
|
|
let topic_string = topic
|
|
|
|
.iter()
|
|
|
|
.map(|i| i.to_string())
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.join(",");
|
|
|
|
path.query_pairs_mut().append_pair("topics", &topic_string);
|
|
|
|
|
|
|
|
Ok(self
|
|
|
|
.client
|
|
|
|
.get(path)
|
|
|
|
.send()
|
2021-10-28 01:18:04 +00:00
|
|
|
.await?
|
2020-12-04 00:18:58 +00:00
|
|
|
.bytes_stream()
|
|
|
|
.map(|next| match next {
|
|
|
|
Ok(bytes) => EventKind::from_sse_bytes(bytes.as_ref()),
|
|
|
|
Err(e) => Err(Error::Reqwest(e)),
|
|
|
|
}))
|
|
|
|
}
|
2020-09-29 03:46:54 +00:00
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
/// `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)?;
|
2020-09-29 03:46:54 +00:00
|
|
|
|
2021-08-06 00:47:31 +00:00
|
|
|
path.path_segments_mut()
|
|
|
|
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
|
|
|
.push("validator")
|
|
|
|
.push("duties")
|
|
|
|
.push("sync")
|
|
|
|
.push(&epoch.to_string());
|
|
|
|
|
2022-01-21 06:07:20 +00:00
|
|
|
self.post_with_timeout_and_response(
|
|
|
|
path,
|
|
|
|
&ValidatorIndexDataRef(indices),
|
|
|
|
self.timeouts.sync_duties,
|
|
|
|
)
|
|
|
|
.await
|
2020-09-29 03:46:54 +00:00
|
|
|
}
|
|
|
|
}
|
2020-11-09 23:13:56 +00:00
|
|
|
|
|
|
|
/// Returns `Ok(response)` if the response is a `200 OK` response. Otherwise, creates an
|
2021-08-06 00:47:31 +00:00
|
|
|
/// appropriate error message.
|
|
|
|
async fn ok_or_error(response: Response) -> Result<Response, Error> {
|
2020-11-09 23:13:56 +00:00
|
|
|
let status = response.status();
|
|
|
|
|
|
|
|
if status == StatusCode::OK {
|
|
|
|
Ok(response)
|
|
|
|
} else if let Ok(message) = response.json().await {
|
2021-08-06 00:47:31 +00:00
|
|
|
match message {
|
|
|
|
ResponseError::Message(message) => Err(Error::ServerMessage(message)),
|
|
|
|
ResponseError::Indexed(indexed) => Err(Error::ServerIndexedMessage(indexed)),
|
|
|
|
}
|
2020-11-09 23:13:56 +00:00
|
|
|
} else {
|
|
|
|
Err(Error::StatusCode(status))
|
|
|
|
}
|
|
|
|
}
|