## Issue Addressed

NA

## Proposed Changes

Adds a `lighthouse/beacon/states/:state_id/ssz` endpoint to allow us to pull the genesis state from the API.

## Additional Info

NA
This commit is contained in:
Paul Hauner 2020-10-22 06:05:49 +00:00
parent 7f73dccebc
commit b829257cca
8 changed files with 115 additions and 4 deletions

2
Cargo.lock generated
View File

@ -1574,6 +1574,7 @@ dependencies = [
"bytes 0.5.6",
"eth2_keystore",
"eth2_libp2p",
"eth2_ssz",
"hex 0.4.2",
"libsecp256k1",
"procinfo",
@ -2461,6 +2462,7 @@ dependencies = [
"eth1",
"eth2",
"eth2_libp2p",
"eth2_ssz",
"fork_choice",
"hex 0.4.2",
"lazy_static",

View File

@ -24,6 +24,7 @@ lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
lazy_static = "1.4.0"
warp_utils = { path = "../../common/warp_utils" }
slot_clock = { path = "../../common/slot_clock" }
eth2_ssz = { path = "../../consensus/ssz" }
bs58 = "0.3.1"
[dev-dependencies]

View File

@ -28,6 +28,7 @@ use parking_lot::Mutex;
use serde::{Deserialize, Serialize};
use slog::{crit, error, info, trace, warn, Logger};
use slot_clock::SlotClock;
use ssz::Encode;
use state_id::StateId;
use state_processing::per_slot_processing;
use std::borrow::Cow;
@ -41,7 +42,7 @@ use types::{
Hash256, ProposerSlashing, PublicKey, RelativeEpoch, SignedAggregateAndProof,
SignedBeaconBlock, SignedVoluntaryExit, Slot, YamlConfig,
};
use warp::Filter;
use warp::{http::Response, Filter};
use warp_utils::task::{blocking_json_task, blocking_task};
const API_PREFIX: &str = "eth";
@ -1768,7 +1769,7 @@ pub fn serve<T: BeaconChainTypes>(
.and(warp::path::param::<Epoch>())
.and(warp::path("global"))
.and(warp::path::end())
.and(chain_filter)
.and(chain_filter.clone())
.and_then(|epoch: Epoch, chain: Arc<BeaconChain<T>>| {
blocking_json_task(move || {
validator_inclusion::global_validator_inclusion_data(epoch, &chain)
@ -1776,6 +1777,30 @@ pub fn serve<T: BeaconChainTypes>(
})
});
// GET lighthouse/beacon/states/{state_id}/ssz
let get_lighthouse_beacon_states_ssz = warp::path("lighthouse")
.and(warp::path("beacon"))
.and(warp::path("states"))
.and(warp::path::param::<StateId>())
.and(warp::path("ssz"))
.and(warp::path::end())
.and(chain_filter)
.and_then(|state_id: StateId, chain: Arc<BeaconChain<T>>| {
blocking_task(move || {
let state = state_id.state(&chain)?;
Response::builder()
.status(200)
.header("Content-Type", "application/ssz")
.body(state.as_ssz_bytes())
.map_err(|e| {
warp_utils::reject::custom_server_error(format!(
"failed to create response: {}",
e
))
})
})
});
// Define the ultimate set of routes that will be provided to the server.
let routes = warp::get()
.and(
@ -1818,6 +1843,7 @@ pub fn serve<T: BeaconChainTypes>(
.or(get_lighthouse_proto_array.boxed())
.or(get_lighthouse_validator_inclusion_global.boxed())
.or(get_lighthouse_validator_inclusion.boxed())
.or(get_lighthouse_beacon_states_ssz.boxed())
.boxed(),
)
.or(warp::post()

View File

@ -1571,6 +1571,23 @@ impl ApiTester {
self
}
pub async fn test_get_lighthouse_beacon_states_ssz(self) -> Self {
for state_id in self.interesting_state_ids() {
let result = self
.client
.get_lighthouse_beacon_states_ssz(&state_id)
.await
.unwrap();
let mut expected = self.get_state(state_id);
expected.as_mut().map(|state| state.drop_all_caches());
assert_eq!(result, expected, "{:?}", state_id);
}
self
}
}
#[tokio::test(core_threads = 2)]
@ -1871,5 +1888,7 @@ async fn lighthouse_endpoints() {
.test_get_lighthouse_validator_inclusion()
.await
.test_get_lighthouse_validator_inclusion_global()
.await
.test_get_lighthouse_beacon_states_ssz()
.await;
}

View File

@ -177,3 +177,17 @@ See [Validator Inclusion APIs](./validator-inclusion.md).
### `/lighthouse/validator_inclusion/{epoch}/global`
See [Validator Inclusion APIs](./validator-inclusion.md).
### `/lighthouse/beacon/states/{state_id}/ssz`
Obtains a `BeaconState` in SSZ bytes. Useful for obtaining a genesis state.
The `state_id` parameter is identical to that used in the [Standard Eth2.0 API
`beacon/state`
routes](https://ethereum.github.io/eth2.0-APIs/#/Beacon/getStateRoot).
```bash
curl -X GET "http://localhost:5052/lighthouse/beacon/states/0/ssz" | jq
```
*Example omitted for brevity, the body simply contains SSZ bytes.*

View File

@ -21,6 +21,7 @@ libsecp256k1 = "0.3.5"
ring = "0.16.12"
bytes = "0.5.6"
account_utils = { path = "../../common/account_utils" }
eth2_ssz = { path = "../../consensus/ssz" }
[target.'cfg(target_os = "linux")'.dependencies]
psutil = { version = "3.2.0", optional = true }

View File

@ -40,6 +40,8 @@ pub enum Error {
MissingSignatureHeader,
/// The server returned an invalid JSON response.
InvalidJson(serde_json::Error),
/// The server returned an invalid SSZ response.
InvalidSsz(ssz::DecodeError),
}
impl Error {
@ -54,6 +56,7 @@ impl Error {
Error::InvalidSignatureHeader => None,
Error::MissingSignatureHeader => None,
Error::InvalidJson(_) => None,
Error::InvalidSsz(_) => None,
}
}
}

View File

@ -1,11 +1,14 @@
//! This module contains endpoints that are non-standard and only available on Lighthouse servers.
use crate::{
types::{Epoch, EthSpec, GenericResponse, ValidatorId},
BeaconNodeHttpClient, Error,
ok_or_error,
types::{BeaconState, Epoch, EthSpec, GenericResponse, ValidatorId},
BeaconNodeHttpClient, Error, StateId, StatusCode,
};
use proto_array::core::ProtoArray;
use reqwest::IntoUrl;
use serde::{Deserialize, Serialize};
use ssz::Decode;
pub use eth2_libp2p::{types::SyncState, PeerInfo};
@ -143,6 +146,27 @@ impl Health {
}
impl BeaconNodeHttpClient {
/// Perform a HTTP GET request, returning `None` on a 404 error.
async fn get_bytes_opt<U: IntoUrl>(&self, url: U) -> Result<Option<Vec<u8>>, Error> {
let response = self.client.get(url).send().await.map_err(Error::Reqwest)?;
match ok_or_error(response).await {
Ok(resp) => Ok(Some(
resp.bytes()
.await
.map_err(Error::Reqwest)?
.into_iter()
.collect::<Vec<_>>(),
)),
Err(err) => {
if err.status() == Some(StatusCode::NOT_FOUND) {
Ok(None)
} else {
Err(err)
}
}
}
}
/// `GET lighthouse/health`
pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, Error> {
let mut path = self.server.clone();
@ -221,4 +245,25 @@ impl BeaconNodeHttpClient {
self.get(path).await
}
/// `GET lighthouse/beacon/states/{state_id}/ssz`
pub async fn get_lighthouse_beacon_states_ssz<E: EthSpec>(
&self,
state_id: &StateId,
) -> Result<Option<BeaconState<E>>, Error> {
let mut path = self.server.clone();
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("lighthouse")
.push("beacon")
.push("states")
.push(&state_id.to_string())
.push("ssz");
self.get_bytes_opt(path)
.await?
.map(|bytes| BeaconState::from_ssz_bytes(&bytes).map_err(Error::InvalidSsz))
.transpose()
}
}