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