mirror of
https://github.com/sigp/lighthouse.git
synced 2026-04-18 13:28:33 +00:00
Merge branch 'unstable' into vc-fallback
This commit is contained in:
@@ -351,6 +351,19 @@ impl BeaconNodeHttpClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Perform a HTTP POST request with a custom timeout and consensus header.
|
||||
async fn post_with_timeout_and_consensus_header<T: Serialize, U: IntoUrl>(
|
||||
&self,
|
||||
url: U,
|
||||
body: &T,
|
||||
timeout: Duration,
|
||||
fork_name: ForkName,
|
||||
) -> Result<(), Error> {
|
||||
self.post_generic_with_consensus_version(url, body, Some(timeout), fork_name)
|
||||
.await?;
|
||||
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>(
|
||||
&self,
|
||||
@@ -381,25 +394,6 @@ impl BeaconNodeHttpClient {
|
||||
ok_or_error(response).await
|
||||
}
|
||||
|
||||
/// Generic POST function supporting arbitrary responses and timeouts.
|
||||
/// Does not include Content-Type application/json in the request header.
|
||||
async fn post_generic_json_without_content_type_header<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);
|
||||
}
|
||||
|
||||
let serialized_body = serde_json::to_vec(body).map_err(Error::InvalidJson)?;
|
||||
|
||||
let response = builder.body(serialized_body).send().await?;
|
||||
ok_or_error(response).await
|
||||
}
|
||||
|
||||
/// Generic POST function supporting arbitrary responses and timeouts.
|
||||
async fn post_generic_with_consensus_version<T: Serialize, U: IntoUrl>(
|
||||
&self,
|
||||
@@ -420,6 +414,23 @@ impl BeaconNodeHttpClient {
|
||||
ok_or_error(response).await
|
||||
}
|
||||
|
||||
/// Generic POST function that includes octet-stream content type header.
|
||||
async fn post_generic_with_ssz_header<T: Serialize, U: IntoUrl>(
|
||||
&self,
|
||||
url: U,
|
||||
body: &T,
|
||||
) -> Result<Response, Error> {
|
||||
let builder = self.client.post(url);
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
headers.insert(
|
||||
"Content-Type",
|
||||
HeaderValue::from_static("application/octet-stream"),
|
||||
);
|
||||
let response = builder.headers(headers).json(body).send().await?;
|
||||
ok_or_error(response).await
|
||||
}
|
||||
|
||||
/// Generic POST function supporting arbitrary responses and timeouts.
|
||||
async fn post_generic_with_consensus_version_and_ssz_body<T: Into<Body>, U: IntoUrl>(
|
||||
&self,
|
||||
@@ -548,6 +559,26 @@ impl BeaconNodeHttpClient {
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// TESTING ONLY: This request should fail with a 415 response code.
|
||||
pub async fn post_beacon_states_validator_balances_with_ssz_header(
|
||||
&self,
|
||||
state_id: StateId,
|
||||
ids: Vec<ValidatorId>,
|
||||
) -> Result<Response, 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("validator_balances");
|
||||
|
||||
let request = ValidatorBalancesRequestBody { ids };
|
||||
|
||||
self.post_generic_with_ssz_header(path, &request).await
|
||||
}
|
||||
|
||||
/// `POST beacon/states/{state_id}/validator_balances`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
@@ -1196,10 +1227,10 @@ impl BeaconNodeHttpClient {
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `GET beacon/blocks/{block_id}/attestations`
|
||||
/// `GET v1/beacon/blocks/{block_id}/attestations`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_blocks_attestations<E: EthSpec>(
|
||||
pub async fn get_beacon_blocks_attestations_v1<E: EthSpec>(
|
||||
&self,
|
||||
block_id: BlockId,
|
||||
) -> Result<Option<ExecutionOptimisticFinalizedResponse<Vec<Attestation<E>>>>, Error> {
|
||||
@@ -1215,8 +1246,28 @@ impl BeaconNodeHttpClient {
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `POST beacon/pool/attestations`
|
||||
pub async fn post_beacon_pool_attestations<E: EthSpec>(
|
||||
/// `GET v2/beacon/blocks/{block_id}/attestations`
|
||||
///
|
||||
/// Returns `Ok(None)` on a 404 error.
|
||||
pub async fn get_beacon_blocks_attestations_v2<E: EthSpec>(
|
||||
&self,
|
||||
block_id: BlockId,
|
||||
) -> Result<Option<ExecutionOptimisticFinalizedForkVersionedResponse<Vec<Attestation<E>>>>, 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())
|
||||
.push("attestations");
|
||||
|
||||
self.get_opt(path).await
|
||||
}
|
||||
|
||||
/// `POST v1/beacon/pool/attestations`
|
||||
pub async fn post_beacon_pool_attestations_v1<E: EthSpec>(
|
||||
&self,
|
||||
attestations: &[Attestation<E>],
|
||||
) -> Result<(), Error> {
|
||||
@@ -1234,8 +1285,33 @@ impl BeaconNodeHttpClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET beacon/pool/attestations?slot,committee_index`
|
||||
pub async fn get_beacon_pool_attestations<E: EthSpec>(
|
||||
/// `POST v2/beacon/pool/attestations`
|
||||
pub async fn post_beacon_pool_attestations_v2<E: EthSpec>(
|
||||
&self,
|
||||
attestations: &[Attestation<E>],
|
||||
fork_name: ForkName,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("attestations");
|
||||
|
||||
self.post_with_timeout_and_consensus_header(
|
||||
path,
|
||||
&attestations,
|
||||
self.timeouts.attestation,
|
||||
fork_name,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET v1/beacon/pool/attestations?slot,committee_index`
|
||||
pub async fn get_beacon_pool_attestations_v1<E: EthSpec>(
|
||||
&self,
|
||||
slot: Option<Slot>,
|
||||
committee_index: Option<u64>,
|
||||
@@ -1261,8 +1337,35 @@ impl BeaconNodeHttpClient {
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `POST beacon/pool/attester_slashings`
|
||||
pub async fn post_beacon_pool_attester_slashings<E: EthSpec>(
|
||||
/// `GET v2/beacon/pool/attestations?slot,committee_index`
|
||||
pub async fn get_beacon_pool_attestations_v2<E: EthSpec>(
|
||||
&self,
|
||||
slot: Option<Slot>,
|
||||
committee_index: Option<u64>,
|
||||
) -> Result<ForkVersionedResponse<Vec<Attestation<E>>>, Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("attestations");
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `POST v1/beacon/pool/attester_slashings`
|
||||
pub async fn post_beacon_pool_attester_slashings_v1<E: EthSpec>(
|
||||
&self,
|
||||
slashing: &AttesterSlashing<E>,
|
||||
) -> Result<(), Error> {
|
||||
@@ -1274,14 +1377,33 @@ impl BeaconNodeHttpClient {
|
||||
.push("pool")
|
||||
.push("attester_slashings");
|
||||
|
||||
self.post_generic_json_without_content_type_header(path, slashing, None)
|
||||
self.post_generic(path, slashing, None).await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `POST v2/beacon/pool/attester_slashings`
|
||||
pub async fn post_beacon_pool_attester_slashings_v2<E: EthSpec>(
|
||||
&self,
|
||||
slashing: &AttesterSlashing<E>,
|
||||
fork_name: ForkName,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("beacon")
|
||||
.push("pool")
|
||||
.push("attester_slashings");
|
||||
|
||||
self.post_generic_with_consensus_version(path, slashing, None, fork_name)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `GET beacon/pool/attester_slashings`
|
||||
pub async fn get_beacon_pool_attester_slashings<E: EthSpec>(
|
||||
/// `GET v1/beacon/pool/attester_slashings`
|
||||
pub async fn get_beacon_pool_attester_slashings_v1<E: EthSpec>(
|
||||
&self,
|
||||
) -> Result<GenericResponse<Vec<AttesterSlashing<E>>>, Error> {
|
||||
let mut path = self.eth_path(V1)?;
|
||||
@@ -1295,6 +1417,21 @@ impl BeaconNodeHttpClient {
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET v2/beacon/pool/attester_slashings`
|
||||
pub async fn get_beacon_pool_attester_slashings_v2<E: EthSpec>(
|
||||
&self,
|
||||
) -> Result<ForkVersionedResponse<Vec<AttesterSlashing<E>>>, Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
|
||||
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,
|
||||
@@ -2184,8 +2321,8 @@ impl BeaconNodeHttpClient {
|
||||
self.get_with_timeout(path, self.timeouts.attestation).await
|
||||
}
|
||||
|
||||
/// `GET validator/aggregate_attestation?slot,attestation_data_root`
|
||||
pub async fn get_validator_aggregate_attestation<E: EthSpec>(
|
||||
/// `GET v1/validator/aggregate_attestation?slot,attestation_data_root`
|
||||
pub async fn get_validator_aggregate_attestation_v1<E: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
attestation_data_root: Hash256,
|
||||
@@ -2208,6 +2345,32 @@ impl BeaconNodeHttpClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET v2/validator/aggregate_attestation?slot,attestation_data_root,committee_index`
|
||||
pub async fn get_validator_aggregate_attestation_v2<E: EthSpec>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
attestation_data_root: Hash256,
|
||||
committee_index: CommitteeIndex,
|
||||
) -> Result<Option<ForkVersionedResponse<Attestation<E>>>, Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
|
||||
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),
|
||||
)
|
||||
.append_pair("committee_index", &committee_index.to_string());
|
||||
|
||||
self.get_opt_with_timeout(path, self.timeouts.attestation)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET validator/sync_committee_contribution`
|
||||
pub async fn get_validator_sync_committee_contribution<E: EthSpec>(
|
||||
&self,
|
||||
@@ -2303,8 +2466,8 @@ impl BeaconNodeHttpClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// `POST validator/aggregate_and_proofs`
|
||||
pub async fn post_validator_aggregate_and_proof<E: EthSpec>(
|
||||
/// `POST v1/validator/aggregate_and_proofs`
|
||||
pub async fn post_validator_aggregate_and_proof_v1<E: EthSpec>(
|
||||
&self,
|
||||
aggregates: &[SignedAggregateAndProof<E>],
|
||||
) -> Result<(), Error> {
|
||||
@@ -2321,6 +2484,30 @@ impl BeaconNodeHttpClient {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `POST v2/validator/aggregate_and_proofs`
|
||||
pub async fn post_validator_aggregate_and_proof_v2<E: EthSpec>(
|
||||
&self,
|
||||
aggregates: &[SignedAggregateAndProof<E>],
|
||||
fork_name: ForkName,
|
||||
) -> Result<(), Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
|
||||
path.path_segments_mut()
|
||||
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
|
||||
.push("validator")
|
||||
.push("aggregate_and_proofs");
|
||||
|
||||
self.post_with_timeout_and_consensus_header(
|
||||
path,
|
||||
&aggregates,
|
||||
self.timeouts.attestation,
|
||||
fork_name,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// `POST validator/beacon_committee_subscriptions`
|
||||
pub async fn post_validator_beacon_committee_subscriptions(
|
||||
&self,
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
use super::{types::*, PK_LEN, SECRET_PREFIX};
|
||||
use super::types::*;
|
||||
use crate::Error;
|
||||
use account_utils::ZeroizeString;
|
||||
use bytes::Bytes;
|
||||
use libsecp256k1::{Message, PublicKey, Signature};
|
||||
use reqwest::{
|
||||
header::{HeaderMap, HeaderValue},
|
||||
IntoUrl,
|
||||
};
|
||||
use ring::digest::{digest, SHA256};
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use serde::{de::DeserializeOwned, Serialize};
|
||||
use std::fmt::{self, Display};
|
||||
@@ -24,8 +21,7 @@ use types::graffiti::GraffitiString;
|
||||
pub struct ValidatorClientHttpClient {
|
||||
client: reqwest::Client,
|
||||
server: SensitiveUrl,
|
||||
secret: Option<ZeroizeString>,
|
||||
server_pubkey: Option<PublicKey>,
|
||||
api_token: Option<ZeroizeString>,
|
||||
authorization_header: AuthorizationHeader,
|
||||
}
|
||||
|
||||
@@ -46,45 +42,13 @@ impl Display for AuthorizationHeader {
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse an API token and return a secp256k1 public key.
|
||||
///
|
||||
/// If the token does not start with the Lighthouse token prefix then `Ok(None)` will be returned.
|
||||
/// An error will be returned if the token looks like a Lighthouse token but doesn't correspond to a
|
||||
/// valid public key.
|
||||
pub fn parse_pubkey(secret: &str) -> Result<Option<PublicKey>, Error> {
|
||||
let secret = if !secret.starts_with(SECRET_PREFIX) {
|
||||
return Ok(None);
|
||||
} else {
|
||||
&secret[SECRET_PREFIX.len()..]
|
||||
};
|
||||
|
||||
serde_utils::hex::decode(secret)
|
||||
.map_err(|e| Error::InvalidSecret(format!("invalid hex: {:?}", e)))
|
||||
.and_then(|bytes| {
|
||||
if bytes.len() != PK_LEN {
|
||||
return Err(Error::InvalidSecret(format!(
|
||||
"expected {} bytes not {}",
|
||||
PK_LEN,
|
||||
bytes.len()
|
||||
)));
|
||||
}
|
||||
|
||||
let mut arr = [0; PK_LEN];
|
||||
arr.copy_from_slice(&bytes);
|
||||
PublicKey::parse_compressed(&arr)
|
||||
.map_err(|e| Error::InvalidSecret(format!("invalid secp256k1 pubkey: {:?}", e)))
|
||||
})
|
||||
.map(Some)
|
||||
}
|
||||
|
||||
impl ValidatorClientHttpClient {
|
||||
/// Create a new client pre-initialised with an API token.
|
||||
pub fn new(server: SensitiveUrl, secret: String) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
client: reqwest::Client::new(),
|
||||
server,
|
||||
server_pubkey: parse_pubkey(&secret)?,
|
||||
secret: Some(secret.into()),
|
||||
api_token: Some(secret.into()),
|
||||
authorization_header: AuthorizationHeader::Bearer,
|
||||
})
|
||||
}
|
||||
@@ -96,8 +60,7 @@ impl ValidatorClientHttpClient {
|
||||
Ok(Self {
|
||||
client: reqwest::Client::new(),
|
||||
server,
|
||||
secret: None,
|
||||
server_pubkey: None,
|
||||
api_token: None,
|
||||
authorization_header: AuthorizationHeader::Omit,
|
||||
})
|
||||
}
|
||||
@@ -110,15 +73,14 @@ impl ValidatorClientHttpClient {
|
||||
Ok(Self {
|
||||
client,
|
||||
server,
|
||||
server_pubkey: parse_pubkey(&secret)?,
|
||||
secret: Some(secret.into()),
|
||||
api_token: Some(secret.into()),
|
||||
authorization_header: AuthorizationHeader::Bearer,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a reference to this client's API token, if any.
|
||||
pub fn api_token(&self) -> Option<&ZeroizeString> {
|
||||
self.secret.as_ref()
|
||||
self.api_token.as_ref()
|
||||
}
|
||||
|
||||
/// Read an API token from the specified `path`, stripping any trailing whitespace.
|
||||
@@ -128,19 +90,11 @@ impl ValidatorClientHttpClient {
|
||||
}
|
||||
|
||||
/// Add an authentication token to use when making requests.
|
||||
///
|
||||
/// If the token is Lighthouse-like, a pubkey derivation will be attempted. In the case
|
||||
/// of failure the token will still be stored, and the client can continue to be used to
|
||||
/// communicate with non-Lighthouse nodes.
|
||||
pub fn add_auth_token(&mut self, token: ZeroizeString) -> Result<(), Error> {
|
||||
let pubkey_res = parse_pubkey(token.as_str());
|
||||
|
||||
self.secret = Some(token);
|
||||
self.api_token = Some(token);
|
||||
self.authorization_header = AuthorizationHeader::Bearer;
|
||||
|
||||
pubkey_res.map(|opt_pubkey| {
|
||||
self.server_pubkey = opt_pubkey;
|
||||
})
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set to `false` to disable sending the `Authorization` header on requests.
|
||||
@@ -160,49 +114,17 @@ impl ValidatorClientHttpClient {
|
||||
self.authorization_header = AuthorizationHeader::Basic;
|
||||
}
|
||||
|
||||
async fn signed_body(&self, response: Response) -> Result<Bytes, Error> {
|
||||
let server_pubkey = self.server_pubkey.as_ref().ok_or(Error::NoServerPubkey)?;
|
||||
let sig = response
|
||||
.headers()
|
||||
.get("Signature")
|
||||
.ok_or(Error::MissingSignatureHeader)?
|
||||
.to_str()
|
||||
.map_err(|_| Error::InvalidSignatureHeader)?
|
||||
.to_string();
|
||||
|
||||
let body = response.bytes().await.map_err(Error::from)?;
|
||||
|
||||
let message =
|
||||
Message::parse_slice(digest(&SHA256, &body).as_ref()).expect("sha256 is 32 bytes");
|
||||
|
||||
serde_utils::hex::decode(&sig)
|
||||
.ok()
|
||||
.and_then(|bytes| {
|
||||
let sig = Signature::parse_der(&bytes).ok()?;
|
||||
Some(libsecp256k1::verify(&message, &sig, server_pubkey))
|
||||
})
|
||||
.filter(|is_valid| *is_valid)
|
||||
.ok_or(Error::InvalidSignatureHeader)?;
|
||||
|
||||
Ok(body)
|
||||
}
|
||||
|
||||
async fn signed_json<T: DeserializeOwned>(&self, response: Response) -> Result<T, Error> {
|
||||
let body = self.signed_body(response).await?;
|
||||
serde_json::from_slice(&body).map_err(Error::InvalidJson)
|
||||
}
|
||||
|
||||
fn headers(&self) -> Result<HeaderMap, Error> {
|
||||
let mut headers = HeaderMap::new();
|
||||
|
||||
if self.authorization_header == AuthorizationHeader::Basic
|
||||
|| self.authorization_header == AuthorizationHeader::Bearer
|
||||
{
|
||||
let secret = self.secret.as_ref().ok_or(Error::NoToken)?;
|
||||
let auth_header_token = self.api_token().ok_or(Error::NoToken)?;
|
||||
let header_value = HeaderValue::from_str(&format!(
|
||||
"{} {}",
|
||||
self.authorization_header,
|
||||
secret.as_str()
|
||||
auth_header_token.as_str()
|
||||
))
|
||||
.map_err(|e| {
|
||||
Error::InvalidSecret(format!("secret is invalid as a header value: {}", e))
|
||||
@@ -240,7 +162,8 @@ impl ValidatorClientHttpClient {
|
||||
|
||||
async fn get<T: DeserializeOwned, U: IntoUrl>(&self, url: U) -> Result<T, Error> {
|
||||
let response = self.get_response(url).await?;
|
||||
self.signed_json(response).await
|
||||
let body = response.bytes().await.map_err(Error::from)?;
|
||||
serde_json::from_slice(&body).map_err(Error::InvalidJson)
|
||||
}
|
||||
|
||||
async fn delete<U: IntoUrl>(&self, url: U) -> Result<(), Error> {
|
||||
@@ -263,7 +186,14 @@ impl ValidatorClientHttpClient {
|
||||
/// 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> {
|
||||
match self.get_response(url).await {
|
||||
Ok(resp) => self.signed_json(resp).await.map(Option::Some),
|
||||
Ok(resp) => {
|
||||
let body = resp.bytes().await.map(Option::Some)?;
|
||||
if let Some(body) = body {
|
||||
serde_json::from_slice(&body).map_err(Error::InvalidJson)
|
||||
} else {
|
||||
Ok(None)
|
||||
}
|
||||
}
|
||||
Err(err) => {
|
||||
if err.status() == Some(StatusCode::NOT_FOUND) {
|
||||
Ok(None)
|
||||
@@ -297,7 +227,8 @@ impl ValidatorClientHttpClient {
|
||||
body: &T,
|
||||
) -> Result<V, Error> {
|
||||
let response = self.post_with_raw_response(url, body).await?;
|
||||
self.signed_json(response).await
|
||||
let body = response.bytes().await.map_err(Error::from)?;
|
||||
serde_json::from_slice(&body).map_err(Error::InvalidJson)
|
||||
}
|
||||
|
||||
async fn post_with_unsigned_response<T: Serialize, U: IntoUrl, V: DeserializeOwned>(
|
||||
@@ -319,8 +250,7 @@ impl ValidatorClientHttpClient {
|
||||
.send()
|
||||
.await
|
||||
.map_err(Error::from)?;
|
||||
let response = ok_or_error(response).await?;
|
||||
self.signed_body(response).await?;
|
||||
ok_or_error(response).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,3 @@
|
||||
pub mod http_client;
|
||||
pub mod std_types;
|
||||
pub mod types;
|
||||
|
||||
/// The number of bytes in the secp256k1 public key used as the authorization token for the VC API.
|
||||
pub const PK_LEN: usize = 33;
|
||||
|
||||
/// The prefix for the secp256k1 public key when it is used as the authorization token for the VC
|
||||
/// API.
|
||||
pub const SECRET_PREFIX: &str = "api-token-";
|
||||
|
||||
@@ -780,6 +780,8 @@ pub struct ValidatorAttestationDataQuery {
|
||||
pub struct ValidatorAggregateAttestationQuery {
|
||||
pub attestation_data_root: Hash256,
|
||||
pub slot: Slot,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub committee_index: Option<CommitteeIndex>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Hash)]
|
||||
@@ -969,6 +971,11 @@ pub struct SseHead {
|
||||
pub execution_optimistic: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
|
||||
pub struct BlockGossip {
|
||||
pub slot: Slot,
|
||||
pub block: Hash256,
|
||||
}
|
||||
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
pub struct SseChainReorg {
|
||||
pub slot: Slot,
|
||||
@@ -1098,6 +1105,7 @@ pub enum EventKind<E: EthSpec> {
|
||||
ProposerSlashing(Box<ProposerSlashing>),
|
||||
AttesterSlashing(Box<AttesterSlashing<E>>),
|
||||
BlsToExecutionChange(Box<SignedBlsToExecutionChange>),
|
||||
BlockGossip(Box<BlockGossip>),
|
||||
}
|
||||
|
||||
impl<E: EthSpec> EventKind<E> {
|
||||
@@ -1120,6 +1128,7 @@ impl<E: EthSpec> EventKind<E> {
|
||||
EventKind::ProposerSlashing(_) => "proposer_slashing",
|
||||
EventKind::AttesterSlashing(_) => "attester_slashing",
|
||||
EventKind::BlsToExecutionChange(_) => "bls_to_execution_change",
|
||||
EventKind::BlockGossip(_) => "block_gossip",
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1215,6 +1224,9 @@ impl<E: EthSpec> EventKind<E> {
|
||||
ServerError::InvalidServerSentEvent(format!("Bls To Execution Change: {:?}", e))
|
||||
})?,
|
||||
)),
|
||||
"block_gossip" => Ok(EventKind::BlockGossip(serde_json::from_str(data).map_err(
|
||||
|e| ServerError::InvalidServerSentEvent(format!("Block Gossip: {:?}", e)),
|
||||
)?)),
|
||||
_ => Err(ServerError::InvalidServerSentEvent(
|
||||
"Could not parse event tag".to_string(),
|
||||
)),
|
||||
@@ -1249,6 +1261,7 @@ pub enum EventTopic {
|
||||
AttesterSlashing,
|
||||
ProposerSlashing,
|
||||
BlsToExecutionChange,
|
||||
BlockGossip,
|
||||
}
|
||||
|
||||
impl FromStr for EventTopic {
|
||||
@@ -1273,6 +1286,7 @@ impl FromStr for EventTopic {
|
||||
"attester_slashing" => Ok(EventTopic::AttesterSlashing),
|
||||
"proposer_slashing" => Ok(EventTopic::ProposerSlashing),
|
||||
"bls_to_execution_change" => Ok(EventTopic::BlsToExecutionChange),
|
||||
"block_gossip" => Ok(EventTopic::BlockGossip),
|
||||
_ => Err("event topic cannot be parsed.".to_string()),
|
||||
}
|
||||
}
|
||||
@@ -1298,6 +1312,7 @@ impl fmt::Display for EventTopic {
|
||||
EventTopic::AttesterSlashing => write!(f, "attester_slashing"),
|
||||
EventTopic::ProposerSlashing => write!(f, "proposer_slashing"),
|
||||
EventTopic::BlsToExecutionChange => write!(f, "bls_to_execution_change"),
|
||||
EventTopic::BlockGossip => write!(f, "block_gossip"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user