Skip to main content

zebra_state/
service.rs

1//! [`tower::Service`]s for Zebra's cached chain state.
2//!
3//! Zebra provides cached state access via two main services:
4//! - [`StateService`]: a read-write service that writes blocks to the state,
5//!   and redirects most read requests to the [`ReadStateService`].
6//! - [`ReadStateService`]: a read-only service that answers from the most
7//!   recent committed block.
8//!
9//! Most users should prefer [`ReadStateService`], unless they need to write blocks to the state.
10//!
11//! Zebra also provides access to the best chain tip via:
12//! - [`LatestChainTip`]: a read-only channel that contains the latest committed
13//!   tip.
14//! - [`ChainTipChange`]: a read-only channel that can asynchronously await
15//!   chain tip changes.
16
17use std::{
18    collections::HashMap,
19    future::Future,
20    pin::Pin,
21    sync::Arc,
22    task::{Context, Poll},
23    time::{Duration, Instant},
24};
25
26use futures::future::FutureExt;
27use tokio::sync::oneshot;
28use tower::{util::BoxService, Service, ServiceExt};
29use tracing::{instrument, Instrument, Span};
30
31#[cfg(any(test, feature = "proptest-impl"))]
32use tower::buffer::Buffer;
33
34use zebra_chain::{
35    block::{self, CountedHeader, HeightDiff},
36    diagnostic::CodeTimer,
37    parameters::{Network, NetworkUpgrade},
38    serialization::ZcashSerialize,
39    subtree::NoteCommitmentSubtreeIndex,
40};
41
42use crate::{
43    constants::{
44        MAX_FIND_BLOCK_HASHES_RESULTS, MAX_FIND_BLOCK_HEADERS_RESULTS, MAX_LEGACY_CHAIN_BLOCKS,
45    },
46    error::{CommitBlockError, CommitCheckpointVerifiedError, InvalidateError, ReconsiderError},
47    request::TimedSpan,
48    response::NonFinalizedBlocksListener,
49    service::{
50        block_iter::any_ancestor_blocks,
51        chain_tip::{ChainTipBlock, ChainTipChange, ChainTipSender, LatestChainTip},
52        finalized_state::{FinalizedState, ZebraDb},
53        non_finalized_state::{Chain, NonFinalizedState},
54        pending_utxos::PendingUtxos,
55        queued_blocks::QueuedBlocks,
56        read::find,
57        watch_receiver::WatchReceiver,
58    },
59    AwaitUtxoError, BoxError, CheckpointVerifiedBlock, CommitSemanticallyVerifiedError, Config,
60    KnownBlock, ReadRequest, ReadResponse, Request, Response, SemanticallyVerifiedBlock,
61    StateInitError,
62};
63
64pub mod block_iter;
65pub mod chain_tip;
66pub mod watch_receiver;
67
68pub mod check;
69
70pub(crate) mod finalized_state;
71pub(crate) mod non_finalized_state;
72mod pending_utxos;
73mod queued_blocks;
74pub(crate) mod read;
75mod traits;
76mod write;
77
78#[cfg(any(test, feature = "proptest-impl"))]
79pub mod arbitrary;
80
81#[cfg(test)]
82mod tests;
83
84pub use finalized_state::{OutputLocation, TransactionIndex, TransactionLocation};
85use write::NonFinalizedWriteMessage;
86
87use self::queued_blocks::{QueuedCheckpointVerified, QueuedSemanticallyVerified, SentHashes};
88
89pub use self::traits::{ReadState, State};
90
91/// A read-write service for Zebra's cached blockchain state.
92///
93/// This service modifies and provides access to:
94/// - the non-finalized state: the most recent blocks, up to
95///   [`MAX_BLOCK_REORG_HEIGHT`](crate::MAX_BLOCK_REORG_HEIGHT) of them.
96///   Zebra allows chain forks in the non-finalized state,
97///   stores it in memory, and re-downloads it when restarted.
98/// - the finalized state: older blocks that have many confirmations.
99///   Zebra stores the single best chain in the finalized state,
100///   and re-loads it from disk when restarted.
101///
102/// Read requests to this service are buffered, then processed concurrently.
103/// Block write requests are buffered, then queued, then processed in order by a separate task.
104///
105/// Most state users can get faster read responses using the [`ReadStateService`],
106/// because its requests do not share a [`tower::buffer::Buffer`] with block write requests.
107///
108/// To quickly get the latest block, use [`LatestChainTip`] or [`ChainTipChange`].
109/// They can read the latest block directly, without queueing any requests.
110#[derive(Debug)]
111pub(crate) struct StateService {
112    // Configuration
113    //
114    /// The configured Zcash network.
115    network: Network,
116
117    /// The height that we start storing UTXOs from finalized blocks.
118    ///
119    /// This height should be lower than the last few checkpoints,
120    /// so the full verifier can verify UTXO spends from those blocks,
121    /// even if they haven't been committed to the finalized state yet.
122    full_verifier_utxo_lookahead: block::Height,
123
124    // Queued Blocks
125    //
126    /// Queued blocks for the [`NonFinalizedState`] that arrived out of order.
127    /// These blocks are awaiting their parent blocks before they can do contextual verification.
128    non_finalized_state_queued_blocks: QueuedBlocks,
129
130    /// Queued blocks for the [`FinalizedState`] that arrived out of order.
131    /// These blocks are awaiting their parent blocks before they can do contextual verification.
132    ///
133    /// Indexed by their parent block hash.
134    finalized_state_queued_blocks: HashMap<block::Hash, QueuedCheckpointVerified>,
135
136    /// Channels to send blocks to the block write task.
137    block_write_sender: write::BlockWriteSender,
138
139    /// The [`block::Hash`] of the most recent block sent on
140    /// `finalized_block_write_sender` or `non_finalized_block_write_sender`.
141    ///
142    /// On startup, this is:
143    /// - the finalized tip, if there are stored blocks, or
144    /// - the genesis block's parent hash, if the database is empty.
145    ///
146    /// If `invalid_block_write_reset_receiver` gets a reset, this is:
147    /// - the hash of the last valid committed block (the parent of the invalid block).
148    finalized_block_write_last_sent_hash: block::Hash,
149
150    /// A set of block hashes that have been sent to the block write task.
151    /// Hashes of blocks below the finalized tip height are periodically pruned.
152    non_finalized_block_write_sent_hashes: SentHashes,
153
154    /// If an invalid block is sent on `finalized_block_write_sender`
155    /// or `non_finalized_block_write_sender`,
156    /// this channel gets the [`block::Hash`] of the valid tip.
157    //
158    // TODO: add tests for finalized and non-finalized resets (#2654)
159    invalid_block_write_reset_receiver: tokio::sync::mpsc::UnboundedReceiver<block::Hash>,
160
161    /// Receives the hash of every non-finalized block that the write task
162    /// rejected, so the corresponding entry can be removed from
163    /// `non_finalized_block_write_sent_hashes`.
164    ///
165    /// Without this, a rejected same-hash block locks out a later honest
166    /// re-delivery of a block at the same hash as a "duplicate" until restart
167    /// or reorg.
168    non_finalized_rejected_receiver: tokio::sync::mpsc::UnboundedReceiver<block::Hash>,
169
170    // Pending UTXO Request Tracking
171    //
172    /// The set of outpoints with pending requests for their associated transparent::Output.
173    pending_utxos: PendingUtxos,
174
175    /// Instant tracking the last time `pending_utxos` was pruned.
176    last_prune: Instant,
177
178    // Updating Concurrently Readable State
179    //
180    /// A cloneable [`ReadStateService`], used to answer concurrent read requests.
181    ///
182    /// TODO: move users of read [`Request`]s to [`ReadStateService`], and remove `read_service`.
183    read_service: ReadStateService,
184
185    // Metrics
186    //
187    /// A metric tracking the maximum height that's currently in `finalized_state_queued_blocks`
188    ///
189    /// Set to `f64::NAN` if `finalized_state_queued_blocks` is empty, because grafana shows NaNs
190    /// as a break in the graph.
191    max_finalized_queue_height: f64,
192}
193
194/// A read-only service for accessing Zebra's cached blockchain state.
195///
196/// This service provides read-only access to:
197/// - the non-finalized state: the most recent blocks, up to
198///   [`MAX_BLOCK_REORG_HEIGHT`](crate::MAX_BLOCK_REORG_HEIGHT) of them.
199/// - the finalized state: older blocks that have many confirmations.
200///
201/// Requests to this service are processed in parallel,
202/// ignoring any blocks queued by the read-write [`StateService`].
203///
204/// This quick response behavior is better for most state users.
205/// It allows other async tasks to make progress while concurrently reading data from disk.
206#[derive(Clone, Debug)]
207pub struct ReadStateService {
208    // Configuration
209    //
210    /// The configured Zcash network.
211    network: Network,
212
213    // Shared Concurrently Readable State
214    //
215    /// A watch channel with a cached copy of the [`NonFinalizedState`].
216    ///
217    /// This state is only updated between requests,
218    /// so it might include some block data that is also on `disk`.
219    non_finalized_state_receiver: WatchReceiver<NonFinalizedState>,
220
221    /// The shared inner on-disk database for the finalized state.
222    ///
223    /// RocksDB allows reads and writes via a shared reference,
224    /// but [`ZebraDb`] doesn't expose any write methods or types.
225    ///
226    /// This chain is updated concurrently with requests,
227    /// so it might include some block data that is also in `best_mem`.
228    db: ZebraDb,
229
230    /// A shared handle to a task that writes blocks to the [`NonFinalizedState`] or [`FinalizedState`],
231    /// once the queues have received all their parent blocks.
232    ///
233    /// Used to check for panics when writing blocks.
234    block_write_task: Option<Arc<std::thread::JoinHandle<()>>>,
235}
236
237impl Drop for StateService {
238    fn drop(&mut self) {
239        // The state service owns the state, tasks, and channels,
240        // so dropping it should shut down everything.
241
242        // Close the channels (non-blocking)
243        // This makes the block write thread exit the next time it checks the channels.
244        // We want to do this here so we get any errors or panics from the block write task before it shuts down.
245        self.invalid_block_write_reset_receiver.close();
246        self.non_finalized_rejected_receiver.close();
247
248        std::mem::drop(self.block_write_sender.finalized.take());
249        std::mem::drop(self.block_write_sender.non_finalized.take());
250
251        self.clear_finalized_block_queue(CommitBlockError::WriteTaskExited);
252        self.clear_non_finalized_block_queue(CommitBlockError::WriteTaskExited);
253
254        // Log database metrics before shutting down
255        info!("dropping the state: logging database metrics");
256        self.log_db_metrics();
257
258        // Then drop self.read_service, which checks the block write task for panics,
259        // and tries to shut down the database.
260    }
261}
262
263impl Drop for ReadStateService {
264    fn drop(&mut self) {
265        // The read state service shares the state,
266        // so dropping it should check if we can shut down.
267
268        // TODO: move this into a try_shutdown() method
269        if let Some(block_write_task) = self.block_write_task.take() {
270            if let Some(block_write_task_handle) = Arc::into_inner(block_write_task) {
271                // We're the last database user, so we can tell it to shut down (blocking):
272                // - flushes the database to disk, and
273                // - drops the database, which cleans up any database tasks correctly.
274                self.db.shutdown(true);
275
276                // We are the last state with a reference to this thread, so we can
277                // wait until the block write task finishes, then check for panics (blocking).
278                // (We'd also like to abort the thread, but std::thread::JoinHandle can't do that.)
279
280                // This log is verbose during tests.
281                #[cfg(not(test))]
282                info!("waiting for the block write task to finish");
283                #[cfg(test)]
284                debug!("waiting for the block write task to finish");
285
286                // TODO: move this into a check_for_panics() method
287                if let Err(thread_panic) = block_write_task_handle.join() {
288                    std::panic::resume_unwind(thread_panic);
289                } else {
290                    debug!("shutting down the state because the block write task has finished");
291                }
292            }
293        } else {
294            // Even if we're not the last database user, try shutting it down.
295            //
296            // TODO: rename this to try_shutdown()?
297            self.db.shutdown(false);
298        }
299    }
300}
301
302impl StateService {
303    const PRUNE_INTERVAL: Duration = Duration::from_secs(30);
304
305    /// Creates a new state service for the state `config` and `network`.
306    ///
307    /// Uses the `max_checkpoint_height` and `checkpoint_verify_concurrency_limit`
308    /// to work out when it is near the final checkpoint.
309    ///
310    /// Returns the read-write and read-only state services,
311    /// and read-only watch channels for its best chain tip.
312    pub async fn new(
313        config: Config,
314        network: &Network,
315        max_checkpoint_height: block::Height,
316        checkpoint_verify_concurrency_limit: usize,
317    ) -> (Self, ReadStateService, LatestChainTip, ChainTipChange) {
318        let (finalized_state, finalized_tip, timer) = {
319            let config = config.clone();
320            let network = network.clone();
321            tokio::task::spawn_blocking(move || {
322                let timer = CodeTimer::start();
323                let finalized_state = FinalizedState::new(
324                    &config,
325                    &network,
326                    #[cfg(feature = "elasticsearch")]
327                    true,
328                )
329                .expect(
330                    "opening the read-write finalized state database failed; check that the \
331                     state cache directory is writable and not locked by another Zebra instance, \
332                     and that there is free disk space",
333                );
334                timer.finish_desc("opening finalized state database");
335
336                let timer = CodeTimer::start();
337                let finalized_tip = finalized_state.db.tip_block();
338
339                (finalized_state, finalized_tip, timer)
340            })
341            .await
342            .expect("failed to join blocking task")
343        };
344
345        // # Correctness
346        //
347        // The state service must set the finalized block write sender to `None`
348        // if there are blocks in the restored non-finalized state that are above
349        // the max checkpoint height so that non-finalized blocks can be written, otherwise,
350        // Zebra will be unable to commit semantically verified blocks, and its chain sync will stall.
351        //
352        // The state service must not set the finalized block write sender to `None` if there
353        // aren't blocks in the restored non-finalized state that are above the max checkpoint height,
354        // otherwise, unless checkpoint sync is disabled in the zebra-consensus configuration,
355        // Zebra will be unable to commit checkpoint verified blocks, and its chain sync will stall.
356        let finalized_tip_height = finalized_tip
357            .as_ref()
358            .map(|tip| tip.coinbase_height().expect("valid block must have height"));
359        let is_finalized_tip_past_max_checkpoint =
360            finalized_tip_height.is_some_and(|tip_height| tip_height >= max_checkpoint_height);
361        let backup_dir_path = config.non_finalized_state_backup_dir(network);
362
363        if backup_dir_path.is_some() && !is_finalized_tip_past_max_checkpoint {
364            tracing::info!(
365                ?finalized_tip_height,
366                ?max_checkpoint_height,
367                "not restoring the non-finalized state backup, because the finalized tip is absent \
368                 or below the max checkpoint height: Zebra will re-download and re-verify the \
369                 blocks above its finalized tip"
370            );
371        }
372        let skip_backup_task = config.debug_skip_non_finalized_state_backup_task;
373        let (non_finalized_state, non_finalized_state_sender, non_finalized_state_receiver) =
374            NonFinalizedState::new(network)
375                .with_backup(
376                    backup_dir_path.clone(),
377                    &finalized_state.db,
378                    is_finalized_tip_past_max_checkpoint,
379                    config.debug_skip_non_finalized_state_backup_task,
380                )
381                .await;
382
383        let non_finalized_block_write_sent_hashes = SentHashes::new(&non_finalized_state);
384        let initial_tip = non_finalized_state
385            .best_tip_block()
386            .map(|cv_block| cv_block.block.clone())
387            .or(finalized_tip)
388            .map(CheckpointVerifiedBlock::from)
389            .map(ChainTipBlock::from);
390
391        tracing::info!(chain_tip = ?initial_tip.as_ref().map(|tip| (tip.hash, tip.height)), "loaded Zebra state cache");
392
393        let (chain_tip_sender, latest_chain_tip, chain_tip_change) =
394            ChainTipSender::new(initial_tip, network);
395
396        let finalized_state_for_writing = finalized_state.clone();
397        let should_use_finalized_block_write_sender = non_finalized_state.is_chain_set_empty();
398        let sync_backup_dir_path = backup_dir_path.filter(|_| skip_backup_task);
399        let (
400            block_write_sender,
401            invalid_block_write_reset_receiver,
402            non_finalized_rejected_receiver,
403            block_write_task,
404        ) = write::BlockWriteSender::spawn(
405            finalized_state_for_writing,
406            non_finalized_state,
407            chain_tip_sender,
408            non_finalized_state_sender,
409            should_use_finalized_block_write_sender,
410            sync_backup_dir_path,
411        );
412
413        let read_service = ReadStateService::new(
414            &finalized_state,
415            block_write_task,
416            non_finalized_state_receiver,
417        );
418
419        let full_verifier_utxo_lookahead = max_checkpoint_height
420            - HeightDiff::try_from(checkpoint_verify_concurrency_limit)
421                .expect("fits in HeightDiff");
422        let full_verifier_utxo_lookahead =
423            full_verifier_utxo_lookahead.unwrap_or(block::Height::MIN);
424        let non_finalized_state_queued_blocks = QueuedBlocks::default();
425        let pending_utxos = PendingUtxos::default();
426
427        let finalized_block_write_last_sent_hash =
428            tokio::task::spawn_blocking(move || finalized_state.db.finalized_tip_hash())
429                .await
430                .expect("failed to join blocking task");
431
432        let state = Self {
433            network: network.clone(),
434            full_verifier_utxo_lookahead,
435            non_finalized_state_queued_blocks,
436            finalized_state_queued_blocks: HashMap::new(),
437            block_write_sender,
438            finalized_block_write_last_sent_hash,
439            non_finalized_block_write_sent_hashes,
440            invalid_block_write_reset_receiver,
441            non_finalized_rejected_receiver,
442            pending_utxos,
443            last_prune: Instant::now(),
444            read_service: read_service.clone(),
445            max_finalized_queue_height: f64::NAN,
446        };
447        timer.finish_desc("initializing state service");
448
449        tracing::info!("starting legacy chain check");
450        let timer = CodeTimer::start();
451
452        if let (Some(tip), Some(nu5_activation_height)) = (
453            {
454                let read_state = state.read_service.clone();
455                tokio::task::spawn_blocking(move || read_state.best_tip())
456                    .await
457                    .expect("task should not panic")
458            },
459            NetworkUpgrade::Nu5.activation_height(network),
460        ) {
461            if let Err(error) = check::legacy_chain(
462                nu5_activation_height,
463                any_ancestor_blocks(
464                    &state.read_service.latest_non_finalized_state(),
465                    &state.read_service.db,
466                    tip.1,
467                ),
468                &state.network,
469                MAX_LEGACY_CHAIN_BLOCKS,
470            ) {
471                let legacy_db_path = state.read_service.db.path().to_path_buf();
472                panic!(
473                    "Cached state contains a legacy chain.\n\
474                     An outdated Zebra version did not know about a recent network upgrade,\n\
475                     so it followed a legacy chain using outdated consensus branch rules.\n\
476                     Hint: Delete your database, and restart Zebra to do a full sync.\n\
477                     Database path: {legacy_db_path:?}\n\
478                     Error: {error:?}",
479                );
480            }
481        }
482
483        tracing::info!("cached state consensus branch is valid: no legacy chain found");
484        timer.finish_desc("legacy chain check");
485
486        // Spawn a background task to periodically export RocksDB metrics to Prometheus
487        let db_for_metrics = read_service.db.clone();
488        tokio::spawn(async move {
489            let mut interval = tokio::time::interval(Duration::from_secs(30));
490            loop {
491                interval.tick().await;
492                db_for_metrics.export_metrics();
493            }
494        });
495
496        (state, read_service, latest_chain_tip, chain_tip_change)
497    }
498
499    /// Call read only state service to log rocksdb database metrics.
500    pub fn log_db_metrics(&self) {
501        self.read_service.db.print_db_metrics();
502    }
503
504    /// Queue a checkpoint verified block for verification and storage in the finalized state.
505    ///
506    /// Returns a channel receiver that provides the result of the block commit.
507    fn queue_and_commit_to_finalized_state(
508        &mut self,
509        checkpoint_verified: CheckpointVerifiedBlock,
510    ) -> oneshot::Receiver<Result<block::Hash, CommitCheckpointVerifiedError>> {
511        // # Correctness & Performance
512        //
513        // This method must not block, access the database, or perform CPU-intensive tasks,
514        // because it is called directly from the tokio executor's Future threads.
515
516        let queued_prev_hash = checkpoint_verified.block.header.previous_block_hash;
517        let queued_height = checkpoint_verified.height;
518
519        // If we're close to the final checkpoint, make the block's UTXOs available for
520        // semantic block verification, even when it is in the channel.
521        if self.is_close_to_final_checkpoint(queued_height) {
522            self.non_finalized_block_write_sent_hashes
523                .add_finalized(&checkpoint_verified)
524        }
525
526        let (rsp_tx, rsp_rx) = oneshot::channel();
527        let queued = (checkpoint_verified, rsp_tx);
528
529        if self.block_write_sender.finalized.is_some() {
530            // We're still committing checkpoint verified blocks
531            if let Some(duplicate_queued) = self
532                .finalized_state_queued_blocks
533                .insert(queued_prev_hash, queued)
534            {
535                Self::send_checkpoint_verified_block_error(
536                    duplicate_queued,
537                    CommitBlockError::new_duplicate(
538                        Some(queued_prev_hash.into()),
539                        KnownBlock::Queue,
540                    ),
541                );
542            }
543
544            self.drain_finalized_queue_and_commit();
545        } else {
546            // We've finished committing checkpoint verified blocks to the finalized state,
547            // so drop any repeated queued blocks, and return an error.
548            //
549            // TODO: track the latest sent height, and drop any blocks under that height
550            //       every time we send some blocks (like QueuedSemanticallyVerifiedBlocks)
551            Self::send_checkpoint_verified_block_error(
552                queued,
553                CommitBlockError::new_duplicate(None, KnownBlock::Finalized),
554            );
555
556            self.clear_finalized_block_queue(CommitBlockError::new_duplicate(
557                None,
558                KnownBlock::Finalized,
559            ));
560        }
561
562        if self.finalized_state_queued_blocks.is_empty() {
563            self.max_finalized_queue_height = f64::NAN;
564        } else if self.max_finalized_queue_height.is_nan()
565            || self.max_finalized_queue_height < queued_height.0 as f64
566        {
567            // if there are still blocks in the queue, then either:
568            //   - the new block was lower than the old maximum, and there was a gap before it,
569            //     so the maximum is still the same (and we skip this code), or
570            //   - the new block is higher than the old maximum, and there is at least one gap
571            //     between the finalized tip and the new maximum
572            self.max_finalized_queue_height = queued_height.0 as f64;
573        }
574
575        metrics::gauge!("state.checkpoint.queued.max.height").set(self.max_finalized_queue_height);
576        metrics::gauge!("state.checkpoint.queued.block.count")
577            .set(self.finalized_state_queued_blocks.len() as f64);
578
579        rsp_rx
580    }
581
582    /// Finds finalized state queue blocks to be committed to the state in order,
583    /// removes them from the queue, and sends them to the block commit task.
584    ///
585    /// After queueing a finalized block, this method checks whether the newly
586    /// queued block (and any of its descendants) can be committed to the state.
587    ///
588    /// Returns an error if the block commit channel has been closed.
589    pub fn drain_finalized_queue_and_commit(&mut self) {
590        use tokio::sync::mpsc::error::{SendError, TryRecvError};
591
592        // # Correctness & Performance
593        //
594        // This method must not block, access the database, or perform CPU-intensive tasks,
595        // because it is called directly from the tokio executor's Future threads.
596
597        // If a block failed, we need to start again from a valid tip.
598        match self.invalid_block_write_reset_receiver.try_recv() {
599            Ok(reset_tip_hash) => self.finalized_block_write_last_sent_hash = reset_tip_hash,
600            Err(TryRecvError::Disconnected) => {
601                info!("Block commit task closed the block reset channel. Is Zebra shutting down?");
602                return;
603            }
604            // There are no errors, so we can just use the last block hash we sent
605            Err(TryRecvError::Empty) => {}
606        }
607
608        while let Some(queued_block) = self
609            .finalized_state_queued_blocks
610            .remove(&self.finalized_block_write_last_sent_hash)
611        {
612            let last_sent_finalized_block_height = queued_block.0.height;
613
614            self.finalized_block_write_last_sent_hash = queued_block.0.hash;
615
616            // If we've finished sending finalized blocks, ignore any repeated blocks.
617            // (Blocks can be repeated after a syncer reset.)
618            if let Some(finalized_block_write_sender) = &self.block_write_sender.finalized {
619                let send_result = finalized_block_write_sender.send(queued_block);
620
621                // If the receiver is closed, we can't send any more blocks.
622                if let Err(SendError(queued)) = send_result {
623                    // If Zebra is shutting down, drop blocks and return an error.
624                    Self::send_checkpoint_verified_block_error(
625                        queued,
626                        CommitBlockError::WriteTaskExited,
627                    );
628
629                    self.clear_finalized_block_queue(CommitBlockError::WriteTaskExited);
630                } else {
631                    metrics::gauge!("state.checkpoint.sent.block.height")
632                        .set(last_sent_finalized_block_height.0 as f64);
633                };
634            }
635        }
636    }
637
638    /// Drains every hash queued on `non_finalized_rejected_receiver` and
639    /// removes it from `non_finalized_block_write_sent_hashes`.
640    ///
641    /// This closes the lockout window where a rejected block keeps its hash
642    /// recorded as "sent", so a subsequent honest re-delivery of a block at
643    /// the same hash is not short-circuited as a false "duplicate".
644    ///
645    /// # Correctness & Performance
646    ///
647    /// Like the other drain methods on `StateService`, this must not block,
648    /// access the database, or perform CPU-intensive work, because it is
649    /// called directly from the tokio executor's Future threads.
650    fn drain_non_finalized_rejected_hashes(&mut self) {
651        use tokio::sync::mpsc::error::TryRecvError;
652
653        loop {
654            match self.non_finalized_rejected_receiver.try_recv() {
655                Ok(hash) => {
656                    self.non_finalized_block_write_sent_hashes.remove(&hash);
657                }
658                Err(TryRecvError::Empty) => break,
659                Err(TryRecvError::Disconnected) => {
660                    info!(
661                        "Block commit task closed the non-finalized rejected hash channel. \
662                         Is Zebra shutting down?"
663                    );
664                    break;
665                }
666            }
667        }
668    }
669
670    /// Drops all finalized state queue blocks, and sends an error on their result channels.
671    fn clear_finalized_block_queue(
672        &mut self,
673        error: impl Into<CommitCheckpointVerifiedError> + Clone,
674    ) {
675        for (_hash, queued) in self.finalized_state_queued_blocks.drain() {
676            Self::send_checkpoint_verified_block_error(queued, error.clone());
677        }
678    }
679
680    /// Send an error on a `QueuedCheckpointVerified` block's result channel, and drop the block
681    fn send_checkpoint_verified_block_error(
682        queued: QueuedCheckpointVerified,
683        error: impl Into<CommitCheckpointVerifiedError>,
684    ) {
685        let (finalized, rsp_tx) = queued;
686
687        // The block sender might have already given up on this block,
688        // so ignore any channel send errors.
689        let _ = rsp_tx.send(Err(error.into()));
690        std::mem::drop(finalized);
691    }
692
693    /// Drops all non-finalized state queue blocks, and sends an error on their result channels.
694    fn clear_non_finalized_block_queue(
695        &mut self,
696        error: impl Into<CommitSemanticallyVerifiedError> + Clone,
697    ) {
698        for (_hash, queued) in self.non_finalized_state_queued_blocks.drain() {
699            Self::send_semantically_verified_block_error(queued, error.clone());
700        }
701    }
702
703    /// Send an error on a `QueuedSemanticallyVerified` block's result channel, and drop the block
704    fn send_semantically_verified_block_error(
705        queued: QueuedSemanticallyVerified,
706        error: impl Into<CommitSemanticallyVerifiedError>,
707    ) {
708        let (finalized, rsp_tx) = queued;
709
710        // The block sender might have already given up on this block,
711        // so ignore any channel send errors.
712        let _ = rsp_tx.send(Err(error.into()));
713        std::mem::drop(finalized);
714    }
715
716    /// Queue a semantically verified block for contextual verification and check if any queued
717    /// blocks are ready to be verified and committed to the state.
718    ///
719    /// This function encodes the logic for [committing non-finalized blocks][1]
720    /// in RFC0005.
721    ///
722    /// [1]: https://zebra.zfnd.org/dev/rfcs/0005-state-updates.html#committing-non-finalized-blocks
723    #[instrument(level = "debug", skip(self, semantically_verified))]
724    fn queue_and_commit_to_non_finalized_state(
725        &mut self,
726        semantically_verified: SemanticallyVerifiedBlock,
727    ) -> oneshot::Receiver<Result<block::Hash, CommitSemanticallyVerifiedError>> {
728        tracing::debug!(block = %semantically_verified.block, "queueing block for contextual verification");
729        let parent_hash = semantically_verified.block.header.previous_block_hash;
730
731        // Drop hashes of any blocks the write task has rejected before checking
732        // the SentHashes membership below. Without this, a rejected same-hash
733        // block would lock out a later honest re-delivery of a block at the
734        // same hash as a false "duplicate".
735        self.drain_non_finalized_rejected_hashes();
736
737        if self
738            .non_finalized_block_write_sent_hashes
739            .contains(&semantically_verified.hash)
740        {
741            let (rsp_tx, rsp_rx) = oneshot::channel();
742            let _ = rsp_tx.send(Err(CommitBlockError::new_duplicate(
743                Some(semantically_verified.hash.into()),
744                KnownBlock::WriteChannel,
745            )
746            .into()));
747            return rsp_rx;
748        }
749
750        if self
751            .read_service
752            .db
753            .contains_height(semantically_verified.height)
754        {
755            let (rsp_tx, rsp_rx) = oneshot::channel();
756            let _ = rsp_tx.send(Err(CommitBlockError::new_duplicate(
757                Some(semantically_verified.height.into()),
758                KnownBlock::Finalized,
759            )
760            .into()));
761            return rsp_rx;
762        }
763
764        // [`Request::CommitSemanticallyVerifiedBlock`] contract: a request to commit a block which
765        // has been queued but not yet committed to the state fails the older request and replaces
766        // it with the newer request.
767        let rsp_rx = if let Some((_, old_rsp_tx)) = self
768            .non_finalized_state_queued_blocks
769            .get_mut(&semantically_verified.hash)
770        {
771            tracing::debug!("replacing older queued request with new request");
772            let (mut rsp_tx, rsp_rx) = oneshot::channel();
773            std::mem::swap(old_rsp_tx, &mut rsp_tx);
774            let _ = rsp_tx.send(Err(CommitBlockError::new_duplicate(
775                Some(semantically_verified.hash.into()),
776                KnownBlock::Queue,
777            )
778            .into()));
779            rsp_rx
780        } else {
781            let (rsp_tx, rsp_rx) = oneshot::channel();
782            self.non_finalized_state_queued_blocks
783                .queue((semantically_verified, rsp_tx));
784            rsp_rx
785        };
786
787        // We've finished sending checkpoint verified blocks when:
788        // - we've sent the verified block for the last checkpoint, and
789        // - it has been successfully written to disk.
790        //
791        // We detect the last checkpoint by looking for non-finalized blocks
792        // that are a child of the last block we sent.
793        //
794        // TODO: configure the state with the last checkpoint hash instead?
795        if self.block_write_sender.finalized.is_some()
796            && self
797                .non_finalized_state_queued_blocks
798                .has_queued_children(self.finalized_block_write_last_sent_hash)
799            && self.read_service.db.finalized_tip_hash()
800                == self.finalized_block_write_last_sent_hash
801        {
802            // Tell the block write task to stop committing checkpoint verified blocks to the finalized state,
803            // and move on to committing semantically verified blocks to the non-finalized state.
804            std::mem::drop(self.block_write_sender.finalized.take());
805            // Remove any checkpoint-verified block hashes from `non_finalized_block_write_sent_hashes`.
806            self.non_finalized_block_write_sent_hashes = SentHashes::default();
807            // Mark `SentHashes` as usable by the `can_fork_chain_at()` method.
808            self.non_finalized_block_write_sent_hashes
809                .can_fork_chain_at_hashes = true;
810            // Send blocks from non-finalized queue
811            self.send_ready_non_finalized_queued(self.finalized_block_write_last_sent_hash);
812            // We've finished committing checkpoint verified blocks to finalized state, so drop any repeated queued blocks.
813            self.clear_finalized_block_queue(CommitBlockError::new_duplicate(
814                None,
815                KnownBlock::Finalized,
816            ));
817        } else if !self.can_fork_chain_at(&parent_hash) {
818            tracing::trace!("unready to verify, returning early");
819        } else if self.block_write_sender.finalized.is_none() {
820            // Wait until block commit task is ready to write non-finalized blocks before dequeuing them
821            self.send_ready_non_finalized_queued(parent_hash);
822
823            let finalized_tip_height = self.read_service.db.finalized_tip_height().expect(
824                "Finalized state must have at least one block before committing non-finalized state",
825            );
826
827            self.non_finalized_state_queued_blocks
828                .prune_by_height(finalized_tip_height);
829
830            self.non_finalized_block_write_sent_hashes
831                .prune_by_height(finalized_tip_height);
832        }
833
834        rsp_rx
835    }
836
837    /// Returns `true` if `hash` is a valid previous block hash for new non-finalized blocks.
838    fn can_fork_chain_at(&self, hash: &block::Hash) -> bool {
839        self.non_finalized_block_write_sent_hashes
840            .can_fork_chain_at(hash)
841            || &self.read_service.db.finalized_tip_hash() == hash
842    }
843
844    /// Returns `true` if `queued_height` is near the final checkpoint.
845    ///
846    /// The semantic block verifier needs access to UTXOs from checkpoint verified blocks
847    /// near the final checkpoint, so that it can verify blocks that spend those UTXOs.
848    ///
849    /// If it doesn't have the required UTXOs, some blocks will time out,
850    /// but succeed after a syncer restart.
851    fn is_close_to_final_checkpoint(&self, queued_height: block::Height) -> bool {
852        queued_height >= self.full_verifier_utxo_lookahead
853    }
854
855    /// Sends all queued blocks whose parents have recently arrived starting from `new_parent`
856    /// in breadth-first ordering to the block write task which will attempt to validate and commit them
857    #[tracing::instrument(level = "debug", skip(self, new_parent))]
858    fn send_ready_non_finalized_queued(&mut self, new_parent: block::Hash) {
859        use tokio::sync::mpsc::error::SendError;
860        if let Some(non_finalized_block_write_sender) = &self.block_write_sender.non_finalized {
861            let mut new_parents: Vec<block::Hash> = vec![new_parent];
862
863            while let Some(parent_hash) = new_parents.pop() {
864                let queued_children = self
865                    .non_finalized_state_queued_blocks
866                    .dequeue_children(parent_hash);
867
868                for queued_child in queued_children {
869                    let (SemanticallyVerifiedBlock { hash, .. }, _) = queued_child;
870
871                    self.non_finalized_block_write_sent_hashes
872                        .add(&queued_child.0);
873                    let send_result = non_finalized_block_write_sender.send(queued_child.into());
874
875                    if let Err(SendError(NonFinalizedWriteMessage::Commit(queued))) = send_result {
876                        // If Zebra is shutting down, drop blocks and return an error.
877                        Self::send_semantically_verified_block_error(
878                            queued,
879                            CommitBlockError::WriteTaskExited,
880                        );
881
882                        self.clear_non_finalized_block_queue(CommitBlockError::WriteTaskExited);
883
884                        return;
885                    };
886
887                    new_parents.push(hash);
888                }
889            }
890
891            self.non_finalized_block_write_sent_hashes.finish_batch();
892        };
893    }
894
895    /// Return the tip of the current best chain.
896    pub fn best_tip(&self) -> Option<(block::Height, block::Hash)> {
897        self.read_service.best_tip()
898    }
899
900    fn send_invalidate_block(
901        &self,
902        hash: block::Hash,
903    ) -> oneshot::Receiver<Result<block::Hash, InvalidateError>> {
904        let (rsp_tx, rsp_rx) = oneshot::channel();
905
906        let Some(sender) = &self.block_write_sender.non_finalized else {
907            let _ = rsp_tx.send(Err(InvalidateError::ProcessingCheckpointedBlocks));
908            return rsp_rx;
909        };
910
911        if let Err(tokio::sync::mpsc::error::SendError(error)) =
912            sender.send(NonFinalizedWriteMessage::Invalidate { hash, rsp_tx })
913        {
914            let NonFinalizedWriteMessage::Invalidate { rsp_tx, .. } = error else {
915                unreachable!("should return the same Invalidate message could not be sent");
916            };
917
918            let _ = rsp_tx.send(Err(InvalidateError::SendInvalidateRequestFailed));
919        }
920
921        rsp_rx
922    }
923
924    fn send_reconsider_block(
925        &self,
926        hash: block::Hash,
927    ) -> oneshot::Receiver<Result<Vec<block::Hash>, ReconsiderError>> {
928        let (rsp_tx, rsp_rx) = oneshot::channel();
929
930        let Some(sender) = &self.block_write_sender.non_finalized else {
931            let _ = rsp_tx.send(Err(ReconsiderError::CheckpointCommitInProgress));
932            return rsp_rx;
933        };
934
935        if let Err(tokio::sync::mpsc::error::SendError(error)) =
936            sender.send(NonFinalizedWriteMessage::Reconsider { hash, rsp_tx })
937        {
938            let NonFinalizedWriteMessage::Reconsider { rsp_tx, .. } = error else {
939                unreachable!("should return the same Reconsider message could not be sent");
940            };
941
942            let _ = rsp_tx.send(Err(ReconsiderError::ReconsiderSendFailed));
943        }
944
945        rsp_rx
946    }
947
948    /// Assert some assumptions about the semantically verified `block` before it is queued.
949    fn assert_block_can_be_validated(&self, block: &SemanticallyVerifiedBlock) {
950        // required by `Request::CommitSemanticallyVerifiedBlock` call
951        assert!(
952            block.height > self.network.mandatory_checkpoint_height(),
953            "invalid semantically verified block height: the canopy checkpoint is mandatory, pre-canopy \
954            blocks, and the canopy activation block, must be committed to the state as finalized \
955            blocks"
956        );
957    }
958
959    fn known_sent_hash(&self, hash: &block::Hash) -> Option<KnownBlock> {
960        self.non_finalized_block_write_sent_hashes
961            .contains(hash)
962            .then_some(KnownBlock::WriteChannel)
963    }
964}
965
966impl ReadStateService {
967    /// Creates a new read-only state service, using the provided finalized state and
968    /// block write task handle.
969    ///
970    /// Returns the newly created service,
971    /// and a watch channel for updating the shared recent non-finalized chain.
972    pub(crate) fn new(
973        finalized_state: &FinalizedState,
974        block_write_task: Option<Arc<std::thread::JoinHandle<()>>>,
975        non_finalized_state_receiver: WatchReceiver<NonFinalizedState>,
976    ) -> Self {
977        let read_service = Self {
978            network: finalized_state.network(),
979            db: finalized_state.db.clone(),
980            non_finalized_state_receiver,
981            block_write_task,
982        };
983
984        tracing::debug!("created new read-only state service");
985
986        read_service
987    }
988
989    /// Return the tip of the current best chain.
990    pub fn best_tip(&self) -> Option<(block::Height, block::Hash)> {
991        read::best_tip(&self.latest_non_finalized_state(), &self.db)
992    }
993
994    /// Gets a clone of the latest non-finalized state from the `non_finalized_state_receiver`
995    fn latest_non_finalized_state(&self) -> NonFinalizedState {
996        self.non_finalized_state_receiver.cloned_watch_data()
997    }
998
999    /// Gets a clone of the latest, best non-finalized chain from the `non_finalized_state_receiver`
1000    fn latest_best_chain(&self) -> Option<Arc<Chain>> {
1001        self.non_finalized_state_receiver
1002            .borrow_mapped(|non_finalized_state| non_finalized_state.best_chain().cloned())
1003    }
1004
1005    /// Test-only access to the inner database.
1006    /// Can be used to modify the database without doing any consensus checks.
1007    #[cfg(any(test, feature = "proptest-impl"))]
1008    pub fn db(&self) -> &ZebraDb {
1009        &self.db
1010    }
1011
1012    /// Logs rocksdb metrics using the read only state service.
1013    pub fn log_db_metrics(&self) {
1014        self.db.print_db_metrics();
1015    }
1016}
1017
1018impl Service<Request> for StateService {
1019    type Response = Response;
1020    type Error = BoxError;
1021    type Future =
1022        Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
1023
1024    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1025        // Check for panics in the block write task
1026        let poll = self.read_service.poll_ready(cx);
1027
1028        // Prune outdated UTXO requests
1029        let now = Instant::now();
1030
1031        if self.last_prune + Self::PRUNE_INTERVAL < now {
1032            let tip = self.best_tip();
1033            let old_len = self.pending_utxos.len();
1034
1035            self.pending_utxos.prune();
1036            self.last_prune = now;
1037
1038            let new_len = self.pending_utxos.len();
1039            let prune_count = old_len
1040                .checked_sub(new_len)
1041                .expect("prune does not add any utxo requests");
1042            if prune_count > 0 {
1043                tracing::debug!(
1044                    ?old_len,
1045                    ?new_len,
1046                    ?prune_count,
1047                    ?tip,
1048                    "pruned utxo requests"
1049                );
1050            } else {
1051                tracing::debug!(len = ?old_len, ?tip, "no utxo requests needed pruning");
1052            }
1053        }
1054
1055        poll
1056    }
1057
1058    #[instrument(name = "state", skip(self, req))]
1059    fn call(&mut self, req: Request) -> Self::Future {
1060        req.count_metric();
1061        let span = Span::current();
1062
1063        match req {
1064            // Uses non_finalized_state_queued_blocks and pending_utxos in the StateService
1065            // Accesses shared writeable state in the StateService, NonFinalizedState, and ZebraDb.
1066            //
1067            // The expected error type for this request is `CommitSemanticallyVerifiedError`.
1068            Request::CommitSemanticallyVerifiedBlock(semantically_verified) => {
1069                let timer = CodeTimer::start();
1070                self.assert_block_can_be_validated(&semantically_verified);
1071
1072                self.pending_utxos
1073                    .check_against_ordered(&semantically_verified.new_outputs);
1074
1075                // # Performance
1076                //
1077                // Allow other async tasks to make progress while blocks are being verified
1078                // and written to disk. But wait for the blocks to finish committing,
1079                // so that `StateService` multi-block queries always observe a consistent state.
1080                //
1081                // Since each block is spawned into its own task,
1082                // there shouldn't be any other code running in the same task,
1083                // so we don't need to worry about blocking it:
1084                // https://docs.rs/tokio/latest/tokio/task/fn.block_in_place.html
1085
1086                let rsp_rx = tokio::task::block_in_place(move || {
1087                    span.in_scope(|| {
1088                        self.queue_and_commit_to_non_finalized_state(semantically_verified)
1089                    })
1090                });
1091
1092                // TODO:
1093                //   - check for panics in the block write task here,
1094                //     as well as in poll_ready()
1095
1096                // The work is all done, the future just waits on a channel for the result
1097                timer.finish_desc("CommitSemanticallyVerifiedBlock");
1098
1099                // Await the channel response, flatten the result, map receive errors to
1100                // `CommitSemanticallyVerifiedError::WriteTaskExited`.
1101                // Then flatten the nested Result and convert any errors to a BoxError.
1102                let span = Span::current();
1103                async move {
1104                    rsp_rx
1105                        .await
1106                        .map_err(|_recv_error| CommitBlockError::WriteTaskExited.into())
1107                        .and_then(|result| result)
1108                        .map_err(BoxError::from)
1109                        .map(Response::Committed)
1110                }
1111                .instrument(span)
1112                .boxed()
1113            }
1114
1115            // Uses finalized_state_queued_blocks and pending_utxos in the StateService.
1116            // Accesses shared writeable state in the StateService.
1117            //
1118            // The expected error type for this request is `CommitCheckpointVerifiedError`.
1119            Request::CommitCheckpointVerifiedBlock(finalized) => {
1120                let timer = CodeTimer::start();
1121                // # Consensus
1122                //
1123                // A semantic block verification could have called AwaitUtxo
1124                // before this checkpoint verified block arrived in the state.
1125                // So we need to check for pending UTXO requests sent by running
1126                // semantic block verifications.
1127                //
1128                // This check is redundant for most checkpoint verified blocks,
1129                // because semantic verification can only succeed near the final
1130                // checkpoint, when all the UTXOs are available for the verifying block.
1131                //
1132                // (Checkpoint block UTXOs are verified using block hash checkpoints
1133                // and transaction merkle tree block header commitments.)
1134                self.pending_utxos
1135                    .check_against_ordered(&finalized.new_outputs);
1136
1137                // # Performance
1138                //
1139                // This method doesn't block, access the database, or perform CPU-intensive tasks,
1140                // so we can run it directly in the tokio executor's Future threads.
1141                let rsp_rx = self.queue_and_commit_to_finalized_state(finalized);
1142
1143                // TODO:
1144                //   - check for panics in the block write task here,
1145                //     as well as in poll_ready()
1146
1147                // The work is all done, the future just waits on a channel for the result
1148                timer.finish_desc("CommitCheckpointVerifiedBlock");
1149
1150                // Await the channel response, flatten the result, map receive errors to
1151                // `CommitCheckpointVerifiedError::WriteTaskExited`.
1152                // Then flatten the nested Result and convert any errors to a BoxError.
1153                async move {
1154                    rsp_rx
1155                        .await
1156                        .map_err(|_recv_error| CommitBlockError::WriteTaskExited.into())
1157                        .and_then(|result| result)
1158                        .map_err(BoxError::from)
1159                        .map(Response::Committed)
1160                }
1161                .instrument(span)
1162                .boxed()
1163            }
1164
1165            // Uses pending_utxos and non_finalized_state_queued_blocks in the StateService.
1166            // If the UTXO isn't in the queued blocks, runs concurrently using the ReadStateService.
1167            //
1168            // The expected error type for this request is `AwaitUtxoError`.
1169            Request::AwaitUtxo(outpoint) => {
1170                let timer = CodeTimer::start();
1171                // Prepare the AwaitUtxo future from PendingUtxos.
1172                let response_fut = self.pending_utxos.queue(outpoint);
1173                // Only instrument `response_fut`, the ReadStateService already
1174                // instruments its requests with the same span.
1175
1176                let response_fut = response_fut
1177                    .map(|result| result.map_err(BoxError::from))
1178                    .instrument(span)
1179                    .boxed();
1180
1181                // Check the non-finalized block queue outside the returned future,
1182                // so we can access mutable state fields.
1183                if let Some(utxo) = self.non_finalized_state_queued_blocks.utxo(&outpoint) {
1184                    self.pending_utxos.respond(&outpoint, utxo);
1185
1186                    // We're finished, the returned future gets the UTXO from the respond() channel.
1187                    timer.finish_desc("AwaitUtxo/queued-non-finalized");
1188
1189                    return response_fut;
1190                }
1191
1192                // Check the sent non-finalized blocks
1193                self.drain_non_finalized_rejected_hashes();
1194
1195                if let Some(utxo) = self.non_finalized_block_write_sent_hashes.utxo(&outpoint) {
1196                    self.pending_utxos.respond(&outpoint, utxo);
1197
1198                    // We're finished, the returned future gets the UTXO from the respond() channel.
1199                    timer.finish_desc("AwaitUtxo/sent-non-finalized");
1200
1201                    return response_fut;
1202                }
1203
1204                // We ignore any UTXOs in FinalizedState.finalized_state_queued_blocks,
1205                // because it is only used during checkpoint verification.
1206                //
1207                // This creates a rare race condition, but it doesn't seem to happen much in practice.
1208                // See #5126 for details.
1209
1210                // Manually send a request to the ReadStateService,
1211                // to get UTXOs from any non-finalized chain or the finalized chain.
1212                let read_service = self.read_service.clone();
1213
1214                // Run the request in an async block, so we can await the response.
1215                async move {
1216                    let req = ReadRequest::AnyChainUtxo(outpoint);
1217
1218                    let rsp = read_service
1219                        .oneshot(req)
1220                        .await
1221                        .map_err(AwaitUtxoError::ReadStateFailed)?;
1222
1223                    // Optional TODO:
1224                    //  - make pending_utxos.respond() async using a channel,
1225                    //    so we can respond to all waiting requests here
1226                    //
1227                    // This change is not required for correctness, because:
1228                    // - any waiting requests should have returned when the block was sent to the state
1229                    // - otherwise, the request returns immediately if:
1230                    //   - the block is in the non-finalized queue, or
1231                    //   - the block is in any non-finalized chain or the finalized state
1232                    //
1233                    // And if the block is in the finalized queue,
1234                    // that's rare enough that a retry is ok.
1235                    if let ReadResponse::AnyChainUtxo(Some(utxo)) = rsp {
1236                        // We got a UTXO, so we replace the response future with the result own.
1237                        timer.finish_desc("AwaitUtxo/any-chain");
1238
1239                        return Ok(Response::Utxo(utxo));
1240                    }
1241
1242                    // We're finished, but the returned future is waiting on the respond() channel.
1243                    timer.finish_desc("AwaitUtxo/waiting");
1244
1245                    response_fut.await
1246                }
1247                .boxed()
1248            }
1249
1250            // Used by sync, inbound, and block verifier to check if a block is already in the state
1251            // before downloading or validating it.
1252            Request::KnownBlock(hash) => {
1253                let timer = CodeTimer::start();
1254
1255                self.drain_non_finalized_rejected_hashes();
1256
1257                let known_sent_hash = self.known_sent_hash(&hash);
1258                let known_queued = self
1259                    .non_finalized_state_queued_blocks
1260                    .has(hash)
1261                    .then_some(KnownBlock::Queue);
1262                let read_service = self.read_service.clone();
1263
1264                async move {
1265                    if let Some(loc) = known_sent_hash.or(known_queued) {
1266                        return Ok(Response::KnownBlock(Some(loc)));
1267                    };
1268
1269                    let response = read::non_finalized_state_contains_block_hash(
1270                        &read_service.latest_non_finalized_state(),
1271                        hash,
1272                    )
1273                    // TODO: Move this to a blocking task, perhaps by moving some of this logic to the ReadStateService.
1274                    .or_else(|| read::finalized_state_contains_block_hash(&read_service.db, hash));
1275
1276                    timer.finish_desc("Request::KnownBlock");
1277
1278                    Ok(Response::KnownBlock(response))
1279                }
1280                .boxed()
1281            }
1282
1283            // The expected error type for this request is `InvalidateError`
1284            Request::InvalidateBlock(block_hash) => {
1285                let rsp_rx = tokio::task::block_in_place(move || {
1286                    span.in_scope(|| self.send_invalidate_block(block_hash))
1287                });
1288
1289                // Await the channel response, flatten the result, map receive errors to
1290                // `InvalidateError::InvalidateRequestDropped`.
1291                // Then flatten the nested Result and convert any errors to a BoxError.
1292                let span = Span::current();
1293                async move {
1294                    rsp_rx
1295                        .await
1296                        .map_err(|_recv_error| InvalidateError::InvalidateRequestDropped)
1297                        .and_then(|result| result)
1298                        .map_err(BoxError::from)
1299                        .map(Response::Invalidated)
1300                }
1301                .instrument(span)
1302                .boxed()
1303            }
1304
1305            // The expected error type for this request is `ReconsiderError`
1306            Request::ReconsiderBlock(block_hash) => {
1307                let rsp_rx = tokio::task::block_in_place(move || {
1308                    span.in_scope(|| self.send_reconsider_block(block_hash))
1309                });
1310
1311                // Await the channel response, flatten the result, map receive errors to
1312                // `ReconsiderError::ReconsiderResponseDropped`.
1313                // Then flatten the nested Result and convert any errors to a BoxError.
1314                let span = Span::current();
1315                async move {
1316                    rsp_rx
1317                        .await
1318                        .map_err(|_recv_error| ReconsiderError::ReconsiderResponseDropped)
1319                        .and_then(|result| result)
1320                        .map_err(BoxError::from)
1321                        .map(Response::Reconsidered)
1322                }
1323                .instrument(span)
1324                .boxed()
1325            }
1326
1327            // Runs concurrently using the ReadStateService
1328            Request::Tip
1329            | Request::Depth(_)
1330            | Request::BestChainNextMedianTimePast
1331            | Request::BestChainBlockHash(_)
1332            | Request::BlockLocator
1333            | Request::Transaction(_)
1334            | Request::AnyChainTransaction(_)
1335            | Request::UnspentBestChainUtxo(_)
1336            | Request::Block(_)
1337            | Request::AnyChainBlock(_)
1338            | Request::BlockAndSize(_)
1339            | Request::BlockHeader(_)
1340            | Request::FindBlockHashes { .. }
1341            | Request::FindBlockHeaders { .. }
1342            | Request::CheckBestChainTipNullifiersAndAnchors(_)
1343            | Request::CheckBlockProposalValidity(_) => {
1344                // Redirect the request to the concurrent ReadStateService
1345                let read_service = self.read_service.clone();
1346
1347                async move {
1348                    let req = req
1349                        .try_into()
1350                        .expect("ReadRequest conversion should not fail");
1351
1352                    let rsp = read_service.oneshot(req).await?;
1353                    let rsp = rsp.try_into().expect("Response conversion should not fail");
1354
1355                    Ok(rsp)
1356                }
1357                .boxed()
1358            }
1359        }
1360    }
1361}
1362
1363impl Service<ReadRequest> for ReadStateService {
1364    type Response = ReadResponse;
1365    type Error = BoxError;
1366    type Future =
1367        Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + 'static>>;
1368
1369    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
1370        // Check for panics in the block write task
1371        //
1372        // TODO: move into a check_for_panics() method
1373        let block_write_task = self.block_write_task.take();
1374
1375        if let Some(block_write_task) = block_write_task {
1376            if block_write_task.is_finished() {
1377                if let Some(block_write_task) = Arc::into_inner(block_write_task) {
1378                    // We are the last state with a reference to this task, so we can propagate any panics
1379                    if let Err(thread_panic) = block_write_task.join() {
1380                        std::panic::resume_unwind(thread_panic);
1381                    }
1382                }
1383            } else {
1384                // It hasn't finished, so we need to put it back
1385                self.block_write_task = Some(block_write_task);
1386            }
1387        }
1388
1389        self.db.check_for_panics();
1390
1391        Poll::Ready(Ok(()))
1392    }
1393
1394    #[instrument(name = "read_state", skip(self, req))]
1395    fn call(&mut self, req: ReadRequest) -> Self::Future {
1396        req.count_metric();
1397        let timer = CodeTimer::start_desc(req.variant_name());
1398        let span = Span::current();
1399        let timed_span = TimedSpan::new(timer, span);
1400        let state = self.clone();
1401
1402        if let ReadRequest::NonFinalizedBlocksListener { known_chain_tips } = req {
1403            // The non-finalized blocks listener is used to notify the state service
1404            // about new blocks that have been added to the non-finalized state.
1405            let non_finalized_blocks_listener = NonFinalizedBlocksListener::spawn(
1406                self.non_finalized_state_receiver.clone(),
1407                known_chain_tips,
1408            );
1409
1410            return async move {
1411                Ok(ReadResponse::NonFinalizedBlocksListener(
1412                    non_finalized_blocks_listener,
1413                ))
1414            }
1415            .boxed();
1416        };
1417
1418        let request_handler = move || match req {
1419            // Used by the `getblockchaininfo` RPC.
1420            ReadRequest::UsageInfo => Ok(ReadResponse::UsageInfo(state.db.size())),
1421
1422            // Used by the StateService.
1423            ReadRequest::Tip => Ok(ReadResponse::Tip(read::tip(
1424                state.latest_best_chain(),
1425                &state.db,
1426            ))),
1427
1428            // Used by `getblockchaininfo` RPC method.
1429            ReadRequest::TipPoolValues => {
1430                let (tip_height, tip_hash, value_balance) =
1431                    read::tip_with_value_balance(state.latest_best_chain(), &state.db)?
1432                        .ok_or(BoxError::from("no chain tip available yet"))?;
1433
1434                Ok(ReadResponse::TipPoolValues {
1435                    tip_height,
1436                    tip_hash,
1437                    value_balance,
1438                })
1439            }
1440
1441            // Used by getblock
1442            ReadRequest::BlockInfo(hash_or_height) => Ok(ReadResponse::BlockInfo(
1443                read::block_info(state.latest_best_chain(), &state.db, hash_or_height),
1444            )),
1445
1446            // Used by the StateService.
1447            ReadRequest::Depth(hash) => Ok(ReadResponse::Depth(read::depth(
1448                state.latest_best_chain(),
1449                &state.db,
1450                hash,
1451            ))),
1452
1453            // Used by the StateService.
1454            ReadRequest::BestChainNextMedianTimePast => {
1455                Ok(ReadResponse::BestChainNextMedianTimePast(
1456                    read::next_median_time_past(&state.latest_non_finalized_state(), &state.db)?,
1457                ))
1458            }
1459
1460            // Used by the get_block (raw) RPC and the StateService.
1461            ReadRequest::Block(hash_or_height) => Ok(ReadResponse::Block(read::block(
1462                state.latest_best_chain(),
1463                &state.db,
1464                hash_or_height,
1465            ))),
1466
1467            ReadRequest::AnyChainBlock(hash_or_height) => Ok(ReadResponse::Block(read::any_block(
1468                state.latest_non_finalized_state().chain_iter(),
1469                &state.db,
1470                hash_or_height,
1471            ))),
1472
1473            // Used by the get_block (raw) RPC and the StateService.
1474            ReadRequest::BlockAndSize(hash_or_height) => Ok(ReadResponse::BlockAndSize(
1475                read::block_and_size(state.latest_best_chain(), &state.db, hash_or_height),
1476            )),
1477
1478            // Used by the get_block (verbosity 3) RPC.
1479            ReadRequest::SpentOutputs(hash_or_height) => Ok(ReadResponse::SpentOutputs(
1480                read::spent_outputs_for_block(state.latest_best_chain(), &state.db, hash_or_height),
1481            )),
1482
1483            // Used by the get_block (verbose) RPC and the StateService.
1484            ReadRequest::BlockHeader(hash_or_height) => {
1485                let best_chain = state.latest_best_chain();
1486
1487                let height = hash_or_height
1488                    .height_or_else(|hash| {
1489                        read::find::height_by_hash(best_chain.clone(), &state.db, hash)
1490                    })
1491                    .ok_or_else(|| BoxError::from("block hash or height not found"))?;
1492
1493                let hash = hash_or_height
1494                    .hash_or_else(|height| {
1495                        read::find::hash_by_height(best_chain.clone(), &state.db, height)
1496                    })
1497                    .ok_or_else(|| BoxError::from("block hash or height not found"))?;
1498
1499                let next_height = height.next()?;
1500                let next_block_hash =
1501                    read::find::hash_by_height(best_chain.clone(), &state.db, next_height);
1502
1503                let header = read::block_header(best_chain, &state.db, height.into())
1504                    .ok_or_else(|| BoxError::from("block hash or height not found"))?;
1505
1506                Ok(ReadResponse::BlockHeader {
1507                    header,
1508                    hash,
1509                    height,
1510                    next_block_hash,
1511                })
1512            }
1513
1514            // For the get_raw_transaction RPC and the StateService.
1515            ReadRequest::Transaction(hash) => Ok(ReadResponse::Transaction(
1516                read::mined_transaction(state.latest_best_chain(), &state.db, hash),
1517            )),
1518
1519            ReadRequest::AnyChainTransaction(hash) => {
1520                Ok(ReadResponse::AnyChainTransaction(read::any_transaction(
1521                    state.latest_non_finalized_state().chain_iter(),
1522                    &state.db,
1523                    hash,
1524                )))
1525            }
1526
1527            // Used by the getblock (verbose) RPC.
1528            ReadRequest::TransactionIdsForBlock(hash_or_height) => Ok(
1529                ReadResponse::TransactionIdsForBlock(read::transaction_hashes_for_block(
1530                    state.latest_best_chain(),
1531                    &state.db,
1532                    hash_or_height,
1533                )),
1534            ),
1535
1536            ReadRequest::AnyChainTransactionIdsForBlock(hash_or_height) => {
1537                Ok(ReadResponse::AnyChainTransactionIdsForBlock(
1538                    read::transaction_hashes_for_any_block(
1539                        state.latest_non_finalized_state().chain_iter(),
1540                        &state.db,
1541                        hash_or_height,
1542                    ),
1543                ))
1544            }
1545
1546            #[cfg(feature = "indexer")]
1547            ReadRequest::SpendingTransactionId(spend) => Ok(ReadResponse::TransactionId(
1548                read::spending_transaction_hash(state.latest_best_chain(), &state.db, spend),
1549            )),
1550
1551            ReadRequest::UnspentBestChainUtxo(outpoint) => Ok(ReadResponse::UnspentBestChainUtxo(
1552                read::unspent_utxo(state.latest_best_chain(), &state.db, outpoint),
1553            )),
1554
1555            // Manually used by the StateService to implement part of AwaitUtxo.
1556            ReadRequest::AnyChainUtxo(outpoint) => Ok(ReadResponse::AnyChainUtxo(read::any_utxo(
1557                state.latest_non_finalized_state(),
1558                &state.db,
1559                outpoint,
1560            ))),
1561
1562            // Used by the StateService.
1563            ReadRequest::BlockLocator => Ok(ReadResponse::BlockLocator(
1564                read::block_locator(state.latest_best_chain(), &state.db).unwrap_or_default(),
1565            )),
1566
1567            // Used by the StateService.
1568            ReadRequest::FindBlockHashes { known_blocks, stop } => {
1569                Ok(ReadResponse::BlockHashes(read::find_chain_hashes(
1570                    state.latest_best_chain(),
1571                    &state.db,
1572                    known_blocks,
1573                    stop,
1574                    MAX_FIND_BLOCK_HASHES_RESULTS,
1575                )))
1576            }
1577
1578            // Used by the StateService.
1579            ReadRequest::FindBlockHeaders { known_blocks, stop } => Ok(ReadResponse::BlockHeaders(
1580                read::find_chain_headers(
1581                    state.latest_best_chain(),
1582                    &state.db,
1583                    known_blocks,
1584                    stop,
1585                    MAX_FIND_BLOCK_HEADERS_RESULTS,
1586                )
1587                .into_iter()
1588                .map(|header| CountedHeader { header })
1589                .collect(),
1590            )),
1591
1592            ReadRequest::FindForkPoint { known_blocks } => {
1593                // Reject over-long locators before doing any work, so an untrusted
1594                // caller can't force unbounded lookups.
1595                let locator_len: u64 = known_blocks
1596                    .len()
1597                    .try_into()
1598                    .expect("usize always fits in u64 on supported (<=64-bit) platforms");
1599                if locator_len > block::MAX_BLOCK_LOCATOR_LENGTH {
1600                    return Err(BoxError::from(format!(
1601                        "FindForkPoint locator length {locator_len} exceeds \
1602                         MAX_BLOCK_LOCATOR_LENGTH ({})",
1603                        block::MAX_BLOCK_LOCATOR_LENGTH,
1604                    )));
1605                }
1606
1607                Ok(ReadResponse::ForkPoint(read::find_fork_point(
1608                    state.latest_best_chain(),
1609                    &state.db,
1610                    known_blocks,
1611                )))
1612            }
1613
1614            ReadRequest::SaplingTree(hash_or_height) => Ok(ReadResponse::SaplingTree(
1615                read::sapling_tree(state.latest_best_chain(), &state.db, hash_or_height),
1616            )),
1617
1618            ReadRequest::OrchardTree(hash_or_height) => Ok(ReadResponse::OrchardTree(
1619                read::orchard_tree(state.latest_best_chain(), &state.db, hash_or_height),
1620            )),
1621
1622            ReadRequest::AnyChainSaplingTree(hash) => {
1623                Ok(ReadResponse::SaplingTree(read::any_sapling_tree(
1624                    state.latest_non_finalized_state().chain_iter(),
1625                    &state.db,
1626                    hash,
1627                )))
1628            }
1629
1630            ReadRequest::AnyChainOrchardTree(hash) => {
1631                Ok(ReadResponse::OrchardTree(read::any_orchard_tree(
1632                    state.latest_non_finalized_state().chain_iter(),
1633                    &state.db,
1634                    hash,
1635                )))
1636            }
1637
1638            ReadRequest::IronwoodTree(hash_or_height) => Ok(ReadResponse::IronwoodTree(
1639                read::ironwood_tree(state.latest_best_chain(), &state.db, hash_or_height),
1640            )),
1641
1642            ReadRequest::AnyChainIronwoodTree(hash) => {
1643                Ok(ReadResponse::IronwoodTree(read::any_ironwood_tree(
1644                    state.latest_non_finalized_state().chain_iter(),
1645                    &state.db,
1646                    hash,
1647                )))
1648            }
1649
1650            ReadRequest::SaplingSubtrees { start_index, limit } => {
1651                let end_index = limit
1652                    .and_then(|limit| start_index.0.checked_add(limit.0))
1653                    .map(NoteCommitmentSubtreeIndex);
1654
1655                let best_chain = state.latest_best_chain();
1656                let sapling_subtrees = if let Some(end_index) = end_index {
1657                    read::sapling_subtrees(best_chain, &state.db, start_index..end_index)
1658                } else {
1659                    // If there is no end bound, just return all the trees.
1660                    // If the end bound would overflow, just returns all the trees, because that's what
1661                    // `zcashd` does. (It never calculates an end bound, so it just keeps iterating until
1662                    // the trees run out.)
1663                    read::sapling_subtrees(best_chain, &state.db, start_index..)
1664                };
1665
1666                Ok(ReadResponse::SaplingSubtrees(sapling_subtrees))
1667            }
1668
1669            ReadRequest::OrchardSubtrees { start_index, limit } => {
1670                let end_index = limit
1671                    .and_then(|limit| start_index.0.checked_add(limit.0))
1672                    .map(NoteCommitmentSubtreeIndex);
1673
1674                let best_chain = state.latest_best_chain();
1675                let orchard_subtrees = if let Some(end_index) = end_index {
1676                    read::orchard_subtrees(best_chain, &state.db, start_index..end_index)
1677                } else {
1678                    // If there is no end bound, just return all the trees.
1679                    // If the end bound would overflow, just returns all the trees, because that's what
1680                    // `zcashd` does. (It never calculates an end bound, so it just keeps iterating until
1681                    // the trees run out.)
1682                    read::orchard_subtrees(best_chain, &state.db, start_index..)
1683                };
1684
1685                Ok(ReadResponse::OrchardSubtrees(orchard_subtrees))
1686            }
1687
1688            ReadRequest::IronwoodSubtrees { start_index, limit } => {
1689                let end_index = limit
1690                    .and_then(|limit| start_index.0.checked_add(limit.0))
1691                    .map(NoteCommitmentSubtreeIndex);
1692
1693                let best_chain = state.latest_best_chain();
1694                let ironwood_subtrees = if let Some(end_index) = end_index {
1695                    read::ironwood_subtrees(best_chain, &state.db, start_index..end_index)
1696                } else {
1697                    // If there is no end bound, just return all the trees.
1698                    // If the end bound would overflow, just returns all the trees, because that's what
1699                    // `zcashd` does. (It never calculates an end bound, so it just keeps iterating until
1700                    // the trees run out.)
1701                    read::ironwood_subtrees(best_chain, &state.db, start_index..)
1702                };
1703
1704                Ok(ReadResponse::IronwoodSubtrees(ironwood_subtrees))
1705            }
1706
1707            // For the get_address_balance RPC.
1708            ReadRequest::AddressBalance(addresses) => {
1709                let (balance, received) =
1710                    read::transparent_balance(state.latest_best_chain(), &state.db, addresses)?;
1711                Ok(ReadResponse::AddressBalance { balance, received })
1712            }
1713
1714            // For the get_address_tx_ids RPC.
1715            ReadRequest::TransactionIdsByAddresses {
1716                addresses,
1717                height_range,
1718                max_entries,
1719            } => read::transparent_tx_ids(
1720                state.latest_best_chain(),
1721                &state.db,
1722                addresses,
1723                height_range,
1724                max_entries,
1725            )
1726            .map(ReadResponse::AddressesTransactionIds),
1727
1728            // For the get_address_utxos RPC.
1729            ReadRequest::UtxosByAddresses {
1730                addresses,
1731                height_range,
1732                max_entries,
1733            } => read::address_utxos(
1734                &state.network,
1735                state.latest_best_chain(),
1736                &state.db,
1737                addresses,
1738                height_range,
1739                max_entries,
1740            )
1741            .map(ReadResponse::AddressUtxos),
1742
1743            ReadRequest::CheckBestChainTipNullifiersAndAnchors(unmined_tx) => {
1744                let latest_non_finalized_best_chain = state.latest_best_chain();
1745
1746                check::nullifier::tx_no_duplicates_in_chain(
1747                    &state.db,
1748                    latest_non_finalized_best_chain.as_ref(),
1749                    &unmined_tx.transaction,
1750                )?;
1751
1752                check::anchors::tx_anchors_refer_to_final_treestates(
1753                    &state.db,
1754                    latest_non_finalized_best_chain.as_ref(),
1755                    &unmined_tx,
1756                )?;
1757
1758                Ok(ReadResponse::ValidBestChainTipNullifiersAndAnchors)
1759            }
1760
1761            // Used by the get_block and get_block_hash RPCs.
1762            ReadRequest::BestChainBlockHash(height) => Ok(ReadResponse::BlockHash(
1763                read::hash_by_height(state.latest_best_chain(), &state.db, height),
1764            )),
1765
1766            // Used by get_block_template and getblockchaininfo RPCs.
1767            ReadRequest::ChainInfo => {
1768                // # Correctness
1769                //
1770                // It is ok to do these lookups using multiple database calls. Finalized state updates
1771                // can only add overlapping blocks, and block hashes are unique across all chain forks.
1772                //
1773                // If there is a large overlap between the non-finalized and finalized states,
1774                // where the finalized tip is above the non-finalized tip,
1775                // Zebra is receiving a lot of blocks, or this request has been delayed for a long time.
1776                //
1777                // In that case, the `getblocktemplate` RPC will return an error because Zebra
1778                // is not synced to the tip. That check happens before the RPC makes this request.
1779                read::difficulty::get_block_template_chain_info(
1780                    &state.latest_non_finalized_state(),
1781                    &state.db,
1782                    &state.network,
1783                )
1784                .map(ReadResponse::ChainInfo)
1785            }
1786
1787            // Used by getmininginfo, getnetworksolps, and getnetworkhashps RPCs.
1788            ReadRequest::SolutionRate { num_blocks, height } => {
1789                let latest_non_finalized_state = state.latest_non_finalized_state();
1790                // # Correctness
1791                //
1792                // It is ok to do these lookups using multiple database calls. Finalized state updates
1793                // can only add overlapping blocks, and block hashes are unique across all chain forks.
1794                //
1795                // The worst that can happen here is that the default `start_hash` will be below
1796                // the chain tip.
1797                let (tip_height, tip_hash) =
1798                    match read::tip(latest_non_finalized_state.best_chain(), &state.db) {
1799                        Some(tip_hash) => tip_hash,
1800                        None => return Ok(ReadResponse::SolutionRate(None)),
1801                    };
1802
1803                let start_height = height.map_or(tip_height, |height| height.min(tip_height));
1804                let start_hash = if start_height < tip_height {
1805                    read::hash_by_height(
1806                        latest_non_finalized_state.best_chain(),
1807                        &state.db,
1808                        start_height,
1809                    )
1810                } else {
1811                    Some(tip_hash)
1812                };
1813                let num_blocks = num_blocks.unwrap_or_else(|| {
1814                    NetworkUpgrade::averaging_window_for_height(&state.network, start_height)
1815                });
1816
1817                let solution_rate = start_hash.and_then(|start_hash| {
1818                    read::difficulty::solution_rate(
1819                        &latest_non_finalized_state,
1820                        &state.db,
1821                        num_blocks,
1822                        start_hash,
1823                    )
1824                });
1825
1826                Ok(ReadResponse::SolutionRate(solution_rate))
1827            }
1828
1829            ReadRequest::CheckBlockProposalValidity(semantically_verified) => {
1830                tracing::debug!(
1831                    "attempting to validate and commit block proposal \
1832                         onto a cloned non-finalized state"
1833                );
1834                let mut latest_non_finalized_state = state.latest_non_finalized_state();
1835
1836                // The previous block of a valid proposal must be on the best chain tip.
1837                let Some((_best_tip_height, best_tip_hash)) =
1838                    read::best_tip(&latest_non_finalized_state, &state.db)
1839                else {
1840                    return Err(
1841                        "state is empty: wait for Zebra to sync before submitting a proposal"
1842                            .into(),
1843                    );
1844                };
1845
1846                if semantically_verified.block.header.previous_block_hash != best_tip_hash {
1847                    return Err("proposal is not based on the current best chain tip: \
1848                                    previous block hash must be the best chain tip"
1849                        .into());
1850                }
1851
1852                // This clone of the non-finalized state is dropped when this closure returns.
1853                // The non-finalized state that's used in the rest of the state (including finalizing
1854                // blocks into the db) is not mutated here.
1855                //
1856                // TODO: Convert `CommitSemanticallyVerifiedError` to a new `ValidateProposalError`?
1857                latest_non_finalized_state.disable_metrics();
1858
1859                write::validate_and_commit_non_finalized(
1860                    &state.db,
1861                    &mut latest_non_finalized_state,
1862                    semantically_verified,
1863                )?;
1864
1865                Ok(ReadResponse::ValidBlockProposal)
1866            }
1867
1868            ReadRequest::TipBlockSize => {
1869                // Respond with the length of the obtained block if any.
1870                Ok(ReadResponse::TipBlockSize(
1871                    state
1872                        .best_tip()
1873                        .and_then(|(tip_height, _)| {
1874                            read::block_info(
1875                                state.latest_best_chain(),
1876                                &state.db,
1877                                tip_height.into(),
1878                            )
1879                        })
1880                        .map(|info| info.size().try_into().expect("u32 should fit in usize"))
1881                        .or_else(|| {
1882                            find::tip_block(state.latest_best_chain(), &state.db)
1883                                .map(|b| b.zcash_serialized_size())
1884                        }),
1885                ))
1886            }
1887
1888            ReadRequest::NonFinalizedBlocksListener { .. } => {
1889                unreachable!("should return early");
1890            }
1891
1892            // Used by `gettxout` RPC method.
1893            ReadRequest::IsTransparentOutputSpent(outpoint) => {
1894                let is_spent = read::unspent_utxo(state.latest_best_chain(), &state.db, outpoint);
1895                Ok(ReadResponse::IsTransparentOutputSpent(is_spent.is_none()))
1896            }
1897        };
1898
1899        timed_span.spawn_blocking(request_handler)
1900    }
1901}
1902
1903/// Initialize a state service from the provided [`Config`].
1904/// Returns a boxed state service, a read-only state service,
1905/// and receivers for state chain tip updates.
1906///
1907/// Each `network` has its own separate on-disk database.
1908///
1909/// The state uses the `max_checkpoint_height` and `checkpoint_verify_concurrency_limit`
1910/// to work out when it is near the final checkpoint.
1911///
1912/// To share access to the state, wrap the returned service in a `Buffer`,
1913/// or clone the returned [`ReadStateService`].
1914///
1915/// It's possible to construct multiple state services in the same application (as
1916/// long as they, e.g., use different storage locations), but doing so is
1917/// probably not what you want.
1918pub async fn init(
1919    config: Config,
1920    network: &Network,
1921    max_checkpoint_height: block::Height,
1922    checkpoint_verify_concurrency_limit: usize,
1923) -> (
1924    BoxService<Request, Response, BoxError>,
1925    ReadStateService,
1926    LatestChainTip,
1927    ChainTipChange,
1928) {
1929    let (state_service, read_only_state_service, latest_chain_tip, chain_tip_change) =
1930        StateService::new(
1931            config,
1932            network,
1933            max_checkpoint_height,
1934            checkpoint_verify_concurrency_limit,
1935        )
1936        .await;
1937
1938    (
1939        BoxService::new(state_service),
1940        read_only_state_service,
1941        latest_chain_tip,
1942        chain_tip_change,
1943    )
1944}
1945
1946/// Initialize a read state service from the provided [`Config`].
1947/// Returns a read-only state service,
1948///
1949/// Each `network` has its own separate on-disk database.
1950///
1951/// To share access to the state, clone the returned [`ReadStateService`].
1952pub fn init_read_only(
1953    config: Config,
1954    network: &Network,
1955) -> Result<
1956    (
1957        ReadStateService,
1958        ZebraDb,
1959        tokio::sync::watch::Sender<NonFinalizedState>,
1960    ),
1961    StateInitError,
1962> {
1963    let finalized_state = FinalizedState::new_with_debug(
1964        &config,
1965        network,
1966        true,
1967        #[cfg(feature = "elasticsearch")]
1968        false,
1969        true,
1970    )?;
1971    let (non_finalized_state_sender, non_finalized_state_receiver) =
1972        tokio::sync::watch::channel(NonFinalizedState::new(network));
1973
1974    Ok((
1975        ReadStateService::new(
1976            &finalized_state,
1977            None,
1978            WatchReceiver::new(non_finalized_state_receiver),
1979        ),
1980        finalized_state.db.clone(),
1981        non_finalized_state_sender,
1982    ))
1983}
1984
1985/// Calls [`init_read_only`] with the provided [`Config`] and [`Network`] from a blocking task.
1986///
1987/// Returns a [`tokio::task::JoinHandle`] whose output is a [`Result`]: awaiting it yields a
1988/// [`JoinError`](tokio::task::JoinError) if the blocking task panicked or was cancelled, and
1989/// otherwise an `Err(`[`StateInitError`]`)` if the read-only state could not be opened (for
1990/// example, a missing read-only database).
1991pub fn spawn_init_read_only(
1992    config: Config,
1993    network: &Network,
1994) -> tokio::task::JoinHandle<
1995    Result<
1996        (
1997            ReadStateService,
1998            ZebraDb,
1999            tokio::sync::watch::Sender<NonFinalizedState>,
2000        ),
2001        StateInitError,
2002    >,
2003> {
2004    let network = network.clone();
2005    tokio::task::spawn_blocking(move || init_read_only(config, &network))
2006}
2007
2008/// Returns a [`StateService`] with an ephemeral [`Config`] and a buffer with a single slot.
2009///
2010/// This can be used to create a state service for testing. See also [`init`].
2011#[cfg(any(test, feature = "proptest-impl"))]
2012pub async fn init_test(
2013    network: &Network,
2014) -> Buffer<BoxService<Request, Response, BoxError>, Request> {
2015    // TODO: pass max_checkpoint_height and checkpoint_verify_concurrency limit
2016    //       if we ever need to test final checkpoint sent UTXO queries
2017    let (state_service, _, _, _) =
2018        StateService::new(Config::ephemeral(), network, block::Height::MAX, 0).await;
2019
2020    Buffer::new(BoxService::new(state_service), 1)
2021}
2022
2023/// Initializes a state service with an ephemeral [`Config`] and a buffer with a single slot,
2024/// then returns the read-write service, read-only service, and tip watch channels.
2025///
2026/// This can be used to create a state service for testing. See also [`init`].
2027#[cfg(any(test, feature = "proptest-impl"))]
2028pub async fn init_test_services(
2029    network: &Network,
2030) -> (
2031    Buffer<BoxService<Request, Response, BoxError>, Request>,
2032    ReadStateService,
2033    LatestChainTip,
2034    ChainTipChange,
2035) {
2036    // TODO: pass max_checkpoint_height and checkpoint_verify_concurrency limit
2037    //       if we ever need to test final checkpoint sent UTXO queries
2038    let (state_service, read_state_service, latest_chain_tip, chain_tip_change) =
2039        StateService::new(Config::ephemeral(), network, block::Height::MAX, 0).await;
2040
2041    let state_service = Buffer::new(BoxService::new(state_service), 1);
2042
2043    (
2044        state_service,
2045        read_state_service,
2046        latest_chain_tip,
2047        chain_tip_change,
2048    )
2049}