diff --git a/zebra-state/src/service.rs b/zebra-state/src/service.rs index 2fe4cabc..ccfe64e3 100644 --- a/zebra-state/src/service.rs +++ b/zebra-state/src/service.rs @@ -55,6 +55,7 @@ pub(crate) mod check; mod finalized_state; mod non_finalized_state; mod pending_utxos; +mod read; #[cfg(any(test, feature = "proptest-impl"))] pub mod arbitrary; @@ -443,13 +444,10 @@ impl StateService { Some(tip.0 - height.0) } - /// Return the block identified by either its `height` or `hash` if it exists - /// in the current best chain. + /// Return the block identified by either its `height` or `hash`, + /// if it exists in the current best chain. pub fn best_block(&self, hash_or_height: HashOrHeight) -> Option> { - self.mem - .best_block(hash_or_height) - .map(|contextual| contextual.block) - .or_else(|| self.disk.db().block(hash_or_height)) + read::block(self.mem.best_chain(), self.disk.db(), hash_or_height) } /// Return the transaction identified by `hash` if it exists in the current @@ -843,13 +841,23 @@ impl Service for ReadStateService { Poll::Ready(Ok(())) } - #[instrument(name = "read_state", skip(self, req))] + #[instrument(name = "read_state", skip(self))] fn call(&mut self, req: Request) -> Self::Future { match req { - // TODO: implement for lightwalletd before using this state in RPC methods - // Used by get_block RPC. - Request::Block(_hash_or_height) => unimplemented!("ReadStateService doesn't Block yet"), + Request::Block(hash_or_height) => { + let state = self.clone(); + + async move { + Ok(read::block( + state.best_chain_receiver.borrow().clone().as_ref(), + &state.db, + hash_or_height, + )) + .map(Response::Block) + } + .boxed() + } // Used by get_best_block_hash & get_blockchain_info (#3143) RPCs. // diff --git a/zebra-state/src/service/non_finalized_state.rs b/zebra-state/src/service/non_finalized_state.rs index 2ac84a0b..14b09c31 100644 --- a/zebra-state/src/service/non_finalized_state.rs +++ b/zebra-state/src/service/non_finalized_state.rs @@ -17,7 +17,7 @@ use zebra_chain::{ use crate::{ request::ContextuallyValidBlock, service::{check, finalized_state::ZebraDb}, - FinalizedBlock, HashOrHeight, PreparedBlock, ValidateContextError, + FinalizedBlock, PreparedBlock, ValidateContextError, }; mod chain; @@ -304,15 +304,6 @@ impl NonFinalizedState { None } - /// Returns the [`ContextuallyValidBlock`] at a given height or hash in the best chain. - pub fn best_block(&self, hash_or_height: HashOrHeight) -> Option { - let best_chain = self.best_chain()?; - let height = - hash_or_height.height_or_else(|hash| best_chain.height_by_hash.get(&hash).cloned())?; - - best_chain.blocks.get(&height).map(Clone::clone) - } - /// Returns the hash for a given `block::Height` if it is present in the best chain. pub fn best_hash(&self, height: block::Height) -> Option { self.best_chain()? diff --git a/zebra-state/src/service/non_finalized_state/chain.rs b/zebra-state/src/service/non_finalized_state/chain.rs index 2b8891c3..63ba91b1 100644 --- a/zebra-state/src/service/non_finalized_state/chain.rs +++ b/zebra-state/src/service/non_finalized_state/chain.rs @@ -1,4 +1,5 @@ -//! Chain that is a part of the non-finalized state. +//! [`Chain`] implements a single non-finalized blockchain, +//! starting at the finalized tip. use std::{ cmp::Ordering, @@ -23,7 +24,7 @@ use zebra_chain::{ work::difficulty::PartialCumulativeWork, }; -use crate::{service::check, ContextuallyValidBlock, ValidateContextError}; +use crate::{service::check, ContextuallyValidBlock, HashOrHeight, ValidateContextError}; #[derive(Debug, Clone)] pub struct Chain { @@ -317,6 +318,14 @@ impl Chain { Ok(Some(forked)) } + /// Returns the [`ContextuallyValidBlock`] at a given height or hash in this chain. + pub fn block(&self, hash_or_height: HashOrHeight) -> Option<&ContextuallyValidBlock> { + let height = + hash_or_height.height_or_else(|hash| self.height_by_hash.get(&hash).cloned())?; + + self.blocks.get(&height) + } + /// Returns the block hash of the tip block. pub fn non_finalized_tip_hash(&self) -> block::Hash { self.blocks diff --git a/zebra-state/src/service/read.rs b/zebra-state/src/service/read.rs new file mode 100644 index 00000000..069cf198 --- /dev/null +++ b/zebra-state/src/service/read.rs @@ -0,0 +1,27 @@ +//! Shared state reading code. +//! +//! Used by [`StateService`] and [`ReadStateService`] +//! to read from the best [`Chain`] in the [`NonFinalizedState`], +//! and the database in the [`FinalizedState`]. + +use std::sync::Arc; + +use zebra_chain::block::Block; + +use crate::{ + service::{finalized_state::ZebraDb, non_finalized_state::Chain}, + HashOrHeight, +}; + +/// Return the block identified by either its `height` or `hash` if it exists +/// in the non-finalized `chain` or finalized `db`. +pub(crate) fn block( + chain: Option<&Arc>, + db: &ZebraDb, + hash_or_height: HashOrHeight, +) -> Option> { + chain + .and_then(|chain| chain.block(hash_or_height)) + .map(|contextual| contextual.block.clone()) + .or_else(|| db.block(hash_or_height)) +} diff --git a/zebrad/src/commands/start.rs b/zebrad/src/commands/start.rs index d775b644..d6f118bd 100644 --- a/zebrad/src/commands/start.rs +++ b/zebrad/src/commands/start.rs @@ -106,7 +106,7 @@ impl StartCmd { info!(?config); info!("initializing node state"); - let (state_service, _read_only_state_service, latest_chain_tip, chain_tip_change) = + let (state_service, read_only_state_service, latest_chain_tip, chain_tip_change) = zebra_state::init(config.state.clone(), config.network.network); let state = ServiceBuilder::new() .buffer(Self::state_buffer_bound()) @@ -164,7 +164,7 @@ impl StartCmd { config.rpc, app_version().to_string(), mempool.clone(), - state.clone(), + read_only_state_service, ); let setup_data = InboundSetupData {