Skip to content

Commit

Permalink
Ensure skip slot states are loaded
Browse files Browse the repository at this point in the history
  • Loading branch information
paulhauner committed Sep 5, 2020
1 parent 036f3ec commit c552b0a
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 22 deletions.
12 changes: 12 additions & 0 deletions beacon_node/beacon_chain/src/beacon_chain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,18 @@ impl<T: BeaconChainTypes> BeaconChain<T> {
}
}

/// Returns the block at the given slot, if any. Only returns blocks in the canonical chain.
///
/// ## Errors
///
/// May return a database error.
pub fn state_root_at_slot(&self, slot: Slot) -> Result<Option<Hash256>, Error> {
process_results(self.rev_iter_state_roots()?, |mut iter| {
iter.find(|(_, this_slot)| *this_slot == slot)
.map(|(root, _)| root)
})
}

/// Returns the block root at the given slot, if any. Only returns roots in the canonical chain.
///
/// ## Errors
Expand Down
29 changes: 20 additions & 9 deletions beacon_node/http_api/src/state_id.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use crate::BlockId;
use beacon_chain::{BeaconChain, BeaconChainTypes};
use eth2::types::{BlockId as CoreBlockId, StateId as CoreStateId};
use eth2::types::StateId as CoreStateId;
use std::str::FromStr;
use types::{BeaconState, Fork, Hash256};
use types::{BeaconState, EthSpec, Fork, Hash256};

pub struct StateId(CoreStateId);

Expand All @@ -11,21 +10,33 @@ impl StateId {
&self,
chain: &BeaconChain<T>,
) -> Result<Hash256, warp::Rejection> {
let block = match &self.0 {
let slot = match &self.0 {
CoreStateId::Head => {
return chain
.head_info()
.map(|head| head.state_root)
.map_err(crate::reject::beacon_chain_error)
}
CoreStateId::Genesis => return Ok(chain.genesis_state_root),
CoreStateId::Finalized => BlockId(CoreBlockId::Finalized).block(chain),
CoreStateId::Justified => BlockId(CoreBlockId::Justified).block(chain),
CoreStateId::Slot(slot) => BlockId(CoreBlockId::Slot(*slot)).block(chain),
CoreStateId::Finalized => chain.head_info().map(|head| {
head.finalized_checkpoint
.epoch
.start_slot(T::EthSpec::slots_per_epoch())
}),
CoreStateId::Justified => chain.head_info().map(|head| {
head.current_justified_checkpoint
.epoch
.start_slot(T::EthSpec::slots_per_epoch())
}),
CoreStateId::Slot(slot) => Ok(*slot),
CoreStateId::Root(root) => return Ok(*root),
}?;
}
.map_err(crate::reject::beacon_chain_error)?;

Ok(block.state_root())
chain
.state_root_at_slot(slot)
.map_err(crate::reject::beacon_chain_error)?
.ok_or_else(|| warp::reject::not_found())
}

pub fn fork<T: BeaconChainTypes>(
Expand Down
114 changes: 101 additions & 13 deletions beacon_node/http_api/tests/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,28 @@ use http_api::Context;
use std::sync::Arc;
use store::config::StoreConfig;
use tokio::sync::oneshot;
use types::{test_utils::generate_deterministic_keypairs, MainnetEthSpec};
use types::{test_utils::generate_deterministic_keypairs, EthSpec, MainnetEthSpec, Slot};

type E = MainnetEthSpec;

const VALIDATOR_COUNT: usize = 24;
const CHAIN_LENGTH: usize = 32 * 6;
const SLOTS_PER_EPOCH: u64 = 32;
const CHAIN_LENGTH: u64 = SLOTS_PER_EPOCH * 5;
const JUSTIFIED_EPOCH: u64 = 4;
const FINALIZED_EPOCH: u64 = 3;

/// Skipping the slots around the epoch boundary allows us to check that we're obtaining states
/// from skipped slots for the finalized and justified checkpoints (instead of the state from the
/// block that those roots point to).
const SKIPPED_SLOTS: &[u64] = &[
JUSTIFIED_EPOCH * SLOTS_PER_EPOCH - 1,
JUSTIFIED_EPOCH * SLOTS_PER_EPOCH,
FINALIZED_EPOCH * SLOTS_PER_EPOCH - 1,
FINALIZED_EPOCH * SLOTS_PER_EPOCH,
];

pub struct ApiTester {
chain: Arc<BeaconChain<HarnessType<MainnetEthSpec>>>,
chain: Arc<BeaconChain<HarnessType<E>>>,
client: BeaconNodeClient,
_server_shutdown: oneshot::Sender<()>,
}
Expand All @@ -28,22 +43,45 @@ impl ApiTester {

harness.advance_slot();

harness.extend_chain(
CHAIN_LENGTH,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
for _ in 0..CHAIN_LENGTH {
let slot = harness.chain.slot().unwrap().as_u64();

if !SKIPPED_SLOTS.contains(&slot) {
harness.extend_chain(
1,
BlockStrategy::OnCanonicalHead,
AttestationStrategy::AllValidators,
);
}

harness.advance_slot();
}

let chain = Arc::new(harness.chain);

assert_eq!(
chain.head_info().unwrap().finalized_checkpoint.epoch,
3,
"precondition: finality"
);
assert_eq!(
chain
.head_info()
.unwrap()
.current_justified_checkpoint
.epoch,
4,
"precondition: justification"
);

let context = Arc::new(Context {
chain: Some(chain.clone()),
listen_address: [127, 0, 0, 1],
listen_port: 0,
});
let ctx = context.clone();
let (listening_socket, server, server_shutdown) = http_api::serve(ctx).unwrap();
dbg!(listening_socket);

tokio::spawn(async { server.await });

let client = BeaconNodeClient::new(format!(
Expand All @@ -64,16 +102,66 @@ impl ApiTester {

let expected = match state_id {
StateId::Head => self.chain.head_info().unwrap().state_root,
_ => todo!(),
StateId::Genesis => self.chain.genesis_state_root,
StateId::Finalized => {
let finalized_slot = self
.chain
.head_info()
.unwrap()
.finalized_checkpoint
.epoch
.start_slot(E::slots_per_epoch());

self.chain
.state_root_at_slot(finalized_slot)
.unwrap()
.unwrap()
}
StateId::Justified => {
let justified_slot = self
.chain
.head_info()
.unwrap()
.current_justified_checkpoint
.epoch
.start_slot(E::slots_per_epoch());

self.chain
.state_root_at_slot(justified_slot)
.unwrap()
.unwrap()
}
StateId::Slot(slot) => self.chain.state_root_at_slot(slot).unwrap().unwrap(),
StateId::Root(root) => root,
};

assert_eq!(result.data.root, expected);
assert_eq!(result.data.root, expected, "{:?}", state_id);

self
}
}

#[tokio::test(core_threads = 2)]
async fn my_test() {
ApiTester::new().test_beacon_state_root(StateId::Head).await;
async fn beacon_state_root() {
ApiTester::new()
.test_beacon_state_root(StateId::Head)
.await
.test_beacon_state_root(StateId::Genesis)
.await
.test_beacon_state_root(StateId::Finalized)
.await
.test_beacon_state_root(StateId::Justified)
.await
.test_beacon_state_root(StateId::Slot(Slot::new(0)))
.await
.test_beacon_state_root(StateId::Slot(Slot::new(32)))
.await
.test_beacon_state_root(StateId::Slot(Slot::from(SKIPPED_SLOTS[0])))
.await
.test_beacon_state_root(StateId::Slot(Slot::from(SKIPPED_SLOTS[1])))
.await
.test_beacon_state_root(StateId::Slot(Slot::from(SKIPPED_SLOTS[2])))
.await
.test_beacon_state_root(StateId::Slot(Slot::from(SKIPPED_SLOTS[3])))
.await;
}

0 comments on commit c552b0a

Please sign in to comment.