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:
teor 2022-03-16 05:50:28 +10:00 committed by GitHub
parent 5950083006
commit 5c62dd62cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 59 additions and 24 deletions

View File

@ -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<Arc<Block>> {
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<Request> 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.
//

View File

@ -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<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.
pub fn best_hash(&self, height: block::Height) -> Option<block::Hash> {
self.best_chain()?

View File

@ -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

View File

@ -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))
}

View File

@ -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 {