Skip to content

Commit

Permalink
Fetch state by slot during checkpoint-sync
Browse files Browse the repository at this point in the history
  • Loading branch information
Tumas committed Jun 20, 2024
1 parent c3d0e5d commit 0d6a345
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 90 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 4 additions & 5 deletions fork_choice_control/src/checkpoint_sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::sync::Arc;

use anyhow::{bail, Result};
use helper_functions::misc;
use http_api_utils::BlockId;
use http_api_utils::{BlockId, StateId};
use log::info;
use mime::APPLICATION_OCTET_STREAM;
use reqwest::{header::ACCEPT, Client, StatusCode, Url};
Expand Down Expand Up @@ -50,9 +50,8 @@ pub async fn load_finalized_from_remote<P: Preset>(

let slot = block.message().slot();
let block_root = block.message().hash_tree_root();
let state_root = block.message().state_root();

let state = fetch_state(config, client, url, state_root)
let state = fetch_state(config, client, url, StateId::Slot(slot))
.await?
.ok_or(Error::MissingPostState { block_root })?;

Expand All @@ -76,9 +75,9 @@ async fn fetch_state<P: Preset>(
config: &Config,
client: &Client,
url: &Url,
state_root: H256,
state_id: StateId,
) -> Result<Option<Arc<BeaconState<P>>>> {
let url = url.join(&format!("/eth/v2/debug/beacon/states/{state_root:?}"))?;
let url = url.join(&format!("/eth/v2/debug/beacon/states/{state_id}"))?;

fetch(config, client, url).await
}
Expand Down
4 changes: 2 additions & 2 deletions http_api/src/extractors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use axum::{
use axum_extra::extract::Query;
use builder_api::unphased::containers::SignedValidatorRegistrationV1;
use eth2_libp2p::PeerId;
use http_api_utils::BlockId;
use http_api_utils::{BlockId, StateId};
use p2p::{BeaconCommitteeSubscription, SyncCommitteeSubscription};
use serde::{de::DeserializeOwned, Deserialize};
use serde_json::Value;
Expand All @@ -39,7 +39,7 @@ use types::{
};
use validator::ValidatorProposerData;

use crate::{error::Error, state_id::StateId, validator_status::ValidatorId};
use crate::{error::Error, validator_status::ValidatorId};

// This has multiple `FromRequest` impls to make error messages more specific.
// They all use `FromStr`, whereas the one for `Path` uses `DeserializeOwned`.
Expand Down
32 changes: 18 additions & 14 deletions http_api/src/standard.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ use futures::{
};
use genesis::AnchorCheckpointProvider;
use helper_functions::{accessors, misc};
use http_api_utils::BlockId;
use http_api_utils::{BlockId, StateId};
use itertools::{izip, Either, Itertools as _};
use liveness_tracker::ApiToLiveness;
use log::{debug, info, warn};
Expand Down Expand Up @@ -93,7 +93,7 @@ use crate::{
full_config::FullConfig,
misc::{APIBlock, BackSyncedStatus, BroadcastValidation, SignedAPIBlock, SyncedStatus},
response::{EthResponse, JsonOrSsz},
state_id::StateId,
state_id,
validator_status::{ValidatorId, ValidatorStatus},
};

Expand Down Expand Up @@ -454,7 +454,7 @@ pub async fn expected_withdrawals<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let proposal_slot = query.proposal_slot.unwrap_or_else(|| state.slot() + 1);

Expand Down Expand Up @@ -492,7 +492,7 @@ pub async fn state_root<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let root = state.hash_tree_root();

Expand All @@ -511,7 +511,7 @@ pub async fn state_fork<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

Ok(EthResponse::json(state.fork())
.execution_optimistic(optimistic)
Expand All @@ -528,7 +528,7 @@ pub async fn state_finality_checkpoints<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let response = StateFinalityCheckpointsResponse {
previous_justified: state.previous_justified_checkpoint(),
Expand All @@ -552,7 +552,7 @@ pub async fn state_validators<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let validators = izip!(
0..,
Expand Down Expand Up @@ -599,7 +599,7 @@ pub async fn state_validator<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let validator_index = validator_id
.validator_index(&state)
Expand Down Expand Up @@ -639,7 +639,7 @@ pub async fn state_validator_balances<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let balances = izip!(
0..,
Expand Down Expand Up @@ -672,7 +672,7 @@ pub async fn state_committees<P: Preset, W: Wait>(
value: mut state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let state_epoch = misc::compute_epoch_at_slot::<P>(state.slot());
let epoch = query.epoch.unwrap_or(state_epoch);
Expand Down Expand Up @@ -740,7 +740,7 @@ pub async fn state_sync_committees<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let Some(state) = state.post_altair() else {
return Ok(EthResponse::json(StateSyncCommitteeResponse::default())
Expand Down Expand Up @@ -796,7 +796,7 @@ pub async fn state_randao<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

// If `epoch` is in the future, return the RANDAO mix for the current epoch.
// This matches how RANDAO mixes are updated during epoch transitions.
Expand Down Expand Up @@ -1576,7 +1576,7 @@ pub async fn beacon_state<P: Preset, W: Wait>(
value: state,
optimistic,
finalized,
} = state_id.state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(&state_id, &controller, &anchor_checkpoint_provider)?;

let version = state.phase();

Expand Down Expand Up @@ -1834,7 +1834,11 @@ pub async fn validator_sync_committee_duties<P: Preset, W: Wait>(
optimistic,
// `duties` responses are not supposed to contain a `finalized` field.
finalized: _,
} = StateId::Slot(start_slot).state(&controller, &anchor_checkpoint_provider)?;
} = state_id::state(
&StateId::Slot(start_slot),
&controller,
&anchor_checkpoint_provider,
)?;

let Some(state) = state.post_altair() else {
return Ok(EthResponse::json(vec![]).execution_optimistic(optimistic));
Expand Down
88 changes: 19 additions & 69 deletions http_api/src/state_id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,77 +3,27 @@ use std::sync::Arc;
use eth1_api::ApiController;
use fork_choice_control::Wait;
use genesis::AnchorCheckpointProvider;
use parse_display::FromStr;
use types::{
combined::BeaconState,
nonstandard::WithStatus,
phase0::primitives::{Slot, H256},
preset::Preset,
};
use http_api_utils::StateId;
use types::{combined::BeaconState, nonstandard::WithStatus, preset::Preset};

use crate::error::Error;

#[cfg(test)]
use parse_display::Display;

#[derive(FromStr)]
#[display(style = "lowercase")]
#[cfg_attr(test, derive(Clone, Copy, PartialEq, Eq, Debug, Display))]
pub enum StateId {
Head,
Genesis,
Finalized,
Justified,
#[display("{0}")]
Slot(Slot),
#[display("{0:?}")]
Root(H256),
}

impl StateId {
pub fn state<P: Preset, W: Wait>(
self,
controller: &ApiController<P, W>,
anchor_checkpoint_provider: &AnchorCheckpointProvider<P>,
) -> Result<WithStatus<Arc<BeaconState<P>>>, Error> {
match self {
Self::Head => Some(controller.head_state()),
Self::Genesis => anchor_checkpoint_provider
.checkpoint()
.genesis()
.map(|checkpoint| checkpoint.state)
.map(WithStatus::valid_and_finalized),
Self::Finalized => Some(controller.last_finalized_state()),
Self::Justified => Some(controller.justified_state()?),
Self::Slot(slot) => controller.state_at_slot(slot)?,
Self::Root(root) => controller.state_by_state_root(root)?,
}
.ok_or(Error::StateNotFound)
}
}

#[cfg(test)]
mod tests {
use hex_literal::hex;
use test_case::test_case;

use super::*;

#[test_case("head", StateId::Head)]
#[test_case("genesis", StateId::Genesis)]
#[test_case("finalized", StateId::Finalized)]
#[test_case("justified", StateId::Justified)]
#[test_case("12", StateId::Slot(12))]
#[test_case(
"0x0000000000000000000000000000000000000000000000000000000000000000",
StateId::Root(H256::zero())
)]
#[test_case(
"0x286a9f59df6017029975682ba803d67efbb9daec7a012c193025a8e6e1e8f22e",
StateId::Root(H256(hex!("286a9f59df6017029975682ba803d67efbb9daec7a012c193025a8e6e1e8f22e")))
)]
fn state_id_string_round_trip(string: &str, state_id: StateId) {
assert_eq!(string.parse(), Ok(state_id));
assert_eq!(state_id.to_string(), string);
pub fn state<P: Preset, W: Wait>(
state_id: &StateId,
controller: &ApiController<P, W>,
anchor_checkpoint_provider: &AnchorCheckpointProvider<P>,
) -> Result<WithStatus<Arc<BeaconState<P>>>, Error> {
match state_id {
StateId::Head => Some(controller.head_state()),
StateId::Genesis => anchor_checkpoint_provider
.checkpoint()
.genesis()
.map(|checkpoint| checkpoint.state)
.map(WithStatus::valid_and_finalized),
StateId::Finalized => Some(controller.last_finalized_state()),
StateId::Justified => Some(controller.justified_state()?),
StateId::Slot(slot) => controller.state_at_slot(*slot)?,
StateId::Root(root) => controller.state_by_state_root(*root)?,
}
.ok_or(Error::StateNotFound)
}
1 change: 1 addition & 0 deletions http_api_utils/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,5 @@ tracing = { workspace = true }
types = { workspace = true }

[dev-dependencies]
hex-literal = { workspace = true }
test-case = { workspace = true }
2 changes: 2 additions & 0 deletions http_api_utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub use block_id::BlockId;
pub use helpers::extend_router_with_middleware;
pub use misc::{ApiMetrics, Direction};
pub use state_id::StateId;

pub mod logging;
pub mod middleware;
Expand All @@ -9,3 +10,4 @@ mod block_id;
mod error;
mod helpers;
mod misc;
mod state_id;
42 changes: 42 additions & 0 deletions http_api_utils/src/state_id.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
use parse_display::{Display, FromStr};
use types::phase0::primitives::{Slot, H256};

#[derive(Display, FromStr)]
#[display(style = "lowercase")]
#[cfg_attr(test, derive(Clone, Copy, PartialEq, Eq, Debug))]
pub enum StateId {
Head,
Genesis,
Finalized,
Justified,
#[display("{0}")]
Slot(Slot),
#[display("{0:?}")]
Root(H256),
}

#[cfg(test)]
mod tests {
use hex_literal::hex;
use test_case::test_case;

use super::*;

#[test_case("head", StateId::Head)]
#[test_case("genesis", StateId::Genesis)]
#[test_case("finalized", StateId::Finalized)]
#[test_case("justified", StateId::Justified)]
#[test_case("12", StateId::Slot(12))]
#[test_case(
"0x0000000000000000000000000000000000000000000000000000000000000000",
StateId::Root(H256::zero())
)]
#[test_case(
"0x286a9f59df6017029975682ba803d67efbb9daec7a012c193025a8e6e1e8f22e",
StateId::Root(H256(hex!("286a9f59df6017029975682ba803d67efbb9daec7a012c193025a8e6e1e8f22e")))
)]
fn state_id_string_round_trip(string: &str, state_id: StateId) {
assert_eq!(string.parse(), Ok(state_id));
assert_eq!(state_id.to_string(), string);
}
}

0 comments on commit 0d6a345

Please sign in to comment.