1. feat(state): Use ReadStateService for RPCs (#3847)
* Use the read-only state service for RPCs * Refactor non-finalized block lookup into Chain * Implement the read-only state block request * Drop the Chain watch channel lock before accessing the finalized state
This commit is contained in:
parent
5950083006
commit
5c62dd62cd
|
@ -55,6 +55,7 @@ pub(crate) mod check;
|
||||||
mod finalized_state;
|
mod finalized_state;
|
||||||
mod non_finalized_state;
|
mod non_finalized_state;
|
||||||
mod pending_utxos;
|
mod pending_utxos;
|
||||||
|
mod read;
|
||||||
|
|
||||||
#[cfg(any(test, feature = "proptest-impl"))]
|
#[cfg(any(test, feature = "proptest-impl"))]
|
||||||
pub mod arbitrary;
|
pub mod arbitrary;
|
||||||
|
@ -443,13 +444,10 @@ impl StateService {
|
||||||
Some(tip.0 - height.0)
|
Some(tip.0 - height.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the block identified by either its `height` or `hash` if it exists
|
/// Return the block identified by either its `height` or `hash`,
|
||||||
/// in the current best chain.
|
/// if it exists in the current best chain.
|
||||||
pub fn best_block(&self, hash_or_height: HashOrHeight) -> Option<Arc<Block>> {
|
pub fn best_block(&self, hash_or_height: HashOrHeight) -> Option<Arc<Block>> {
|
||||||
self.mem
|
read::block(self.mem.best_chain(), self.disk.db(), hash_or_height)
|
||||||
.best_block(hash_or_height)
|
|
||||||
.map(|contextual| contextual.block)
|
|
||||||
.or_else(|| self.disk.db().block(hash_or_height))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the transaction identified by `hash` if it exists in the current
|
/// Return the transaction identified by `hash` if it exists in the current
|
||||||
|
@ -843,13 +841,23 @@ impl Service<Request> for ReadStateService {
|
||||||
Poll::Ready(Ok(()))
|
Poll::Ready(Ok(()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[instrument(name = "read_state", skip(self, req))]
|
#[instrument(name = "read_state", skip(self))]
|
||||||
fn call(&mut self, req: Request) -> Self::Future {
|
fn call(&mut self, req: Request) -> Self::Future {
|
||||||
match req {
|
match req {
|
||||||
// TODO: implement for lightwalletd before using this state in RPC methods
|
|
||||||
|
|
||||||
// Used by get_block RPC.
|
// 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.
|
// Used by get_best_block_hash & get_blockchain_info (#3143) RPCs.
|
||||||
//
|
//
|
||||||
|
|
|
@ -17,7 +17,7 @@ use zebra_chain::{
|
||||||
use crate::{
|
use crate::{
|
||||||
request::ContextuallyValidBlock,
|
request::ContextuallyValidBlock,
|
||||||
service::{check, finalized_state::ZebraDb},
|
service::{check, finalized_state::ZebraDb},
|
||||||
FinalizedBlock, HashOrHeight, PreparedBlock, ValidateContextError,
|
FinalizedBlock, PreparedBlock, ValidateContextError,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod chain;
|
mod chain;
|
||||||
|
@ -304,15 +304,6 @@ impl NonFinalizedState {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the [`ContextuallyValidBlock`] at a given height or hash in the best chain.
|
|
||||||
pub fn best_block(&self, hash_or_height: HashOrHeight) -> Option<ContextuallyValidBlock> {
|
|
||||||
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.
|
/// 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<block::Hash> {
|
pub fn best_hash(&self, height: block::Height) -> Option<block::Hash> {
|
||||||
self.best_chain()?
|
self.best_chain()?
|
||||||
|
|
|
@ -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::{
|
use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
|
@ -23,7 +24,7 @@ use zebra_chain::{
|
||||||
work::difficulty::PartialCumulativeWork,
|
work::difficulty::PartialCumulativeWork,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{service::check, ContextuallyValidBlock, ValidateContextError};
|
use crate::{service::check, ContextuallyValidBlock, HashOrHeight, ValidateContextError};
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Chain {
|
pub struct Chain {
|
||||||
|
@ -317,6 +318,14 @@ impl Chain {
|
||||||
Ok(Some(forked))
|
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.
|
/// Returns the block hash of the tip block.
|
||||||
pub fn non_finalized_tip_hash(&self) -> block::Hash {
|
pub fn non_finalized_tip_hash(&self) -> block::Hash {
|
||||||
self.blocks
|
self.blocks
|
||||||
|
|
|
@ -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<Chain>>,
|
||||||
|
db: &ZebraDb,
|
||||||
|
hash_or_height: HashOrHeight,
|
||||||
|
) -> Option<Arc<Block>> {
|
||||||
|
chain
|
||||||
|
.and_then(|chain| chain.block(hash_or_height))
|
||||||
|
.map(|contextual| contextual.block.clone())
|
||||||
|
.or_else(|| db.block(hash_or_height))
|
||||||
|
}
|
|
@ -106,7 +106,7 @@ impl StartCmd {
|
||||||
info!(?config);
|
info!(?config);
|
||||||
|
|
||||||
info!("initializing node state");
|
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);
|
zebra_state::init(config.state.clone(), config.network.network);
|
||||||
let state = ServiceBuilder::new()
|
let state = ServiceBuilder::new()
|
||||||
.buffer(Self::state_buffer_bound())
|
.buffer(Self::state_buffer_bound())
|
||||||
|
@ -164,7 +164,7 @@ impl StartCmd {
|
||||||
config.rpc,
|
config.rpc,
|
||||||
app_version().to_string(),
|
app_version().to_string(),
|
||||||
mempool.clone(),
|
mempool.clone(),
|
||||||
state.clone(),
|
read_only_state_service,
|
||||||
);
|
);
|
||||||
|
|
||||||
let setup_data = InboundSetupData {
|
let setup_data = InboundSetupData {
|
||||||
|
|
Loading…
Reference in New Issue