Ssz state (#1749)
## 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:
parent
7f73dccebc
commit
b829257cca
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -1574,6 +1574,7 @@ dependencies = [
|
|||||||
"bytes 0.5.6",
|
"bytes 0.5.6",
|
||||||
"eth2_keystore",
|
"eth2_keystore",
|
||||||
"eth2_libp2p",
|
"eth2_libp2p",
|
||||||
|
"eth2_ssz",
|
||||||
"hex 0.4.2",
|
"hex 0.4.2",
|
||||||
"libsecp256k1",
|
"libsecp256k1",
|
||||||
"procinfo",
|
"procinfo",
|
||||||
@ -2461,6 +2462,7 @@ dependencies = [
|
|||||||
"eth1",
|
"eth1",
|
||||||
"eth2",
|
"eth2",
|
||||||
"eth2_libp2p",
|
"eth2_libp2p",
|
||||||
|
"eth2_ssz",
|
||||||
"fork_choice",
|
"fork_choice",
|
||||||
"hex 0.4.2",
|
"hex 0.4.2",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
@ -24,6 +24,7 @@ lighthouse_metrics = { path = "../../common/lighthouse_metrics" }
|
|||||||
lazy_static = "1.4.0"
|
lazy_static = "1.4.0"
|
||||||
warp_utils = { path = "../../common/warp_utils" }
|
warp_utils = { path = "../../common/warp_utils" }
|
||||||
slot_clock = { path = "../../common/slot_clock" }
|
slot_clock = { path = "../../common/slot_clock" }
|
||||||
|
eth2_ssz = { path = "../../consensus/ssz" }
|
||||||
bs58 = "0.3.1"
|
bs58 = "0.3.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
@ -28,6 +28,7 @@ use parking_lot::Mutex;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use slog::{crit, error, info, trace, warn, Logger};
|
use slog::{crit, error, info, trace, warn, Logger};
|
||||||
use slot_clock::SlotClock;
|
use slot_clock::SlotClock;
|
||||||
|
use ssz::Encode;
|
||||||
use state_id::StateId;
|
use state_id::StateId;
|
||||||
use state_processing::per_slot_processing;
|
use state_processing::per_slot_processing;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
@ -41,7 +42,7 @@ use types::{
|
|||||||
Hash256, ProposerSlashing, PublicKey, RelativeEpoch, SignedAggregateAndProof,
|
Hash256, ProposerSlashing, PublicKey, RelativeEpoch, SignedAggregateAndProof,
|
||||||
SignedBeaconBlock, SignedVoluntaryExit, Slot, YamlConfig,
|
SignedBeaconBlock, SignedVoluntaryExit, Slot, YamlConfig,
|
||||||
};
|
};
|
||||||
use warp::Filter;
|
use warp::{http::Response, Filter};
|
||||||
use warp_utils::task::{blocking_json_task, blocking_task};
|
use warp_utils::task::{blocking_json_task, blocking_task};
|
||||||
|
|
||||||
const API_PREFIX: &str = "eth";
|
const API_PREFIX: &str = "eth";
|
||||||
@ -1768,7 +1769,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
.and(warp::path::param::<Epoch>())
|
.and(warp::path::param::<Epoch>())
|
||||||
.and(warp::path("global"))
|
.and(warp::path("global"))
|
||||||
.and(warp::path::end())
|
.and(warp::path::end())
|
||||||
.and(chain_filter)
|
.and(chain_filter.clone())
|
||||||
.and_then(|epoch: Epoch, chain: Arc<BeaconChain<T>>| {
|
.and_then(|epoch: Epoch, chain: Arc<BeaconChain<T>>| {
|
||||||
blocking_json_task(move || {
|
blocking_json_task(move || {
|
||||||
validator_inclusion::global_validator_inclusion_data(epoch, &chain)
|
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.
|
// Define the ultimate set of routes that will be provided to the server.
|
||||||
let routes = warp::get()
|
let routes = warp::get()
|
||||||
.and(
|
.and(
|
||||||
@ -1818,6 +1843,7 @@ pub fn serve<T: BeaconChainTypes>(
|
|||||||
.or(get_lighthouse_proto_array.boxed())
|
.or(get_lighthouse_proto_array.boxed())
|
||||||
.or(get_lighthouse_validator_inclusion_global.boxed())
|
.or(get_lighthouse_validator_inclusion_global.boxed())
|
||||||
.or(get_lighthouse_validator_inclusion.boxed())
|
.or(get_lighthouse_validator_inclusion.boxed())
|
||||||
|
.or(get_lighthouse_beacon_states_ssz.boxed())
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.or(warp::post()
|
.or(warp::post()
|
||||||
|
@ -1571,6 +1571,23 @@ impl ApiTester {
|
|||||||
|
|
||||||
self
|
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)]
|
#[tokio::test(core_threads = 2)]
|
||||||
@ -1871,5 +1888,7 @@ async fn lighthouse_endpoints() {
|
|||||||
.test_get_lighthouse_validator_inclusion()
|
.test_get_lighthouse_validator_inclusion()
|
||||||
.await
|
.await
|
||||||
.test_get_lighthouse_validator_inclusion_global()
|
.test_get_lighthouse_validator_inclusion_global()
|
||||||
|
.await
|
||||||
|
.test_get_lighthouse_beacon_states_ssz()
|
||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
@ -177,3 +177,17 @@ See [Validator Inclusion APIs](./validator-inclusion.md).
|
|||||||
### `/lighthouse/validator_inclusion/{epoch}/global`
|
### `/lighthouse/validator_inclusion/{epoch}/global`
|
||||||
|
|
||||||
See [Validator Inclusion APIs](./validator-inclusion.md).
|
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.*
|
||||||
|
@ -21,6 +21,7 @@ libsecp256k1 = "0.3.5"
|
|||||||
ring = "0.16.12"
|
ring = "0.16.12"
|
||||||
bytes = "0.5.6"
|
bytes = "0.5.6"
|
||||||
account_utils = { path = "../../common/account_utils" }
|
account_utils = { path = "../../common/account_utils" }
|
||||||
|
eth2_ssz = { path = "../../consensus/ssz" }
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
psutil = { version = "3.2.0", optional = true }
|
psutil = { version = "3.2.0", optional = true }
|
||||||
|
@ -40,6 +40,8 @@ pub enum Error {
|
|||||||
MissingSignatureHeader,
|
MissingSignatureHeader,
|
||||||
/// The server returned an invalid JSON response.
|
/// The server returned an invalid JSON response.
|
||||||
InvalidJson(serde_json::Error),
|
InvalidJson(serde_json::Error),
|
||||||
|
/// The server returned an invalid SSZ response.
|
||||||
|
InvalidSsz(ssz::DecodeError),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
@ -54,6 +56,7 @@ impl Error {
|
|||||||
Error::InvalidSignatureHeader => None,
|
Error::InvalidSignatureHeader => None,
|
||||||
Error::MissingSignatureHeader => None,
|
Error::MissingSignatureHeader => None,
|
||||||
Error::InvalidJson(_) => None,
|
Error::InvalidJson(_) => None,
|
||||||
|
Error::InvalidSsz(_) => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,14 @@
|
|||||||
//! This module contains endpoints that are non-standard and only available on Lighthouse servers.
|
//! This module contains endpoints that are non-standard and only available on Lighthouse servers.
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
types::{Epoch, EthSpec, GenericResponse, ValidatorId},
|
ok_or_error,
|
||||||
BeaconNodeHttpClient, Error,
|
types::{BeaconState, Epoch, EthSpec, GenericResponse, ValidatorId},
|
||||||
|
BeaconNodeHttpClient, Error, StateId, StatusCode,
|
||||||
};
|
};
|
||||||
use proto_array::core::ProtoArray;
|
use proto_array::core::ProtoArray;
|
||||||
|
use reqwest::IntoUrl;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use ssz::Decode;
|
||||||
|
|
||||||
pub use eth2_libp2p::{types::SyncState, PeerInfo};
|
pub use eth2_libp2p::{types::SyncState, PeerInfo};
|
||||||
|
|
||||||
@ -143,6 +146,27 @@ impl Health {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl BeaconNodeHttpClient {
|
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`
|
/// `GET lighthouse/health`
|
||||||
pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, Error> {
|
pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, Error> {
|
||||||
let mut path = self.server.clone();
|
let mut path = self.server.clone();
|
||||||
@ -221,4 +245,25 @@ impl BeaconNodeHttpClient {
|
|||||||
|
|
||||||
self.get(path).await
|
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()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user