Skip to main content

zebra_state/
request.rs

1//! State [`tower::Service`] request types.
2
3use std::{
4    collections::{HashMap, HashSet},
5    ops::{Add, Deref, DerefMut, RangeInclusive},
6    pin::Pin,
7    sync::Arc,
8    time::Instant,
9};
10
11use tower::{BoxError, Service, ServiceExt};
12use zebra_chain::{
13    amount::{Amount, DeferredPoolBalanceChange, NegativeAllowed, NonNegative},
14    block::{self, Block, HeightDiff},
15    diagnostic::{task::WaitForPanics, CodeTimer},
16    history_tree::HistoryTree,
17    parallel::tree::NoteCommitmentTrees,
18    parameters::Network,
19    serialization::SerializationError,
20    subtree::NoteCommitmentSubtreeIndex,
21    transaction::{self, UnminedTx},
22    transparent::{self, utxos_from_ordered_utxos},
23    value_balance::{ValueBalance, ValueBalanceError},
24};
25
26/// Allow *only* these unused imports, so that rustdoc link resolution
27/// will work with inline links.
28#[allow(unused_imports)]
29use crate::{
30    constants::{MAX_FIND_BLOCK_HASHES_RESULTS, MAX_FIND_BLOCK_HEADERS_RESULTS},
31    ReadResponse, Response,
32};
33use crate::{
34    error::{InvalidateError, LayeredStateError, ReconsiderError},
35    AwaitUtxoError, CommitCheckpointVerifiedError, CommitSemanticallyVerifiedError,
36};
37
38/// The per-pool nullifier types used by the indexer-only [`Spend`] enum, imported here rather than
39/// in the shared import block because they are only referenced under the `indexer` feature.
40#[cfg(feature = "indexer")]
41use zebra_chain::{ironwood, orchard, sapling, sprout};
42
43/// Identify a spend by a transparent outpoint or revealed nullifier.
44///
45/// This enum implements `From` for [`transparent::OutPoint`], [`sprout::Nullifier`],
46/// [`sapling::Nullifier`], [`orchard::Nullifier`], and [`ironwood::Nullifier`].
47#[derive(Copy, Clone, Debug, PartialEq, Eq)]
48#[cfg(feature = "indexer")]
49pub enum Spend {
50    /// A spend identified by a [`transparent::OutPoint`].
51    OutPoint(transparent::OutPoint),
52    /// A spend identified by a [`sprout::Nullifier`].
53    Sprout(sprout::Nullifier),
54    /// A spend identified by a [`sapling::Nullifier`].
55    Sapling(sapling::Nullifier),
56    /// A spend identified by a [`orchard::Nullifier`].
57    Orchard(orchard::Nullifier),
58    /// A spend identified by an [`ironwood::Nullifier`].
59    Ironwood(ironwood::Nullifier),
60}
61
62#[cfg(feature = "indexer")]
63impl From<transparent::OutPoint> for Spend {
64    fn from(outpoint: transparent::OutPoint) -> Self {
65        Self::OutPoint(outpoint)
66    }
67}
68
69#[cfg(feature = "indexer")]
70impl From<sprout::Nullifier> for Spend {
71    fn from(sprout_nullifier: sprout::Nullifier) -> Self {
72        Self::Sprout(sprout_nullifier)
73    }
74}
75
76#[cfg(feature = "indexer")]
77impl From<sapling::Nullifier> for Spend {
78    fn from(sapling_nullifier: sapling::Nullifier) -> Self {
79        Self::Sapling(sapling_nullifier)
80    }
81}
82
83#[cfg(feature = "indexer")]
84impl From<orchard::Nullifier> for Spend {
85    fn from(orchard_nullifier: orchard::Nullifier) -> Self {
86        Self::Orchard(orchard_nullifier)
87    }
88}
89
90#[cfg(feature = "indexer")]
91impl From<ironwood::Nullifier> for Spend {
92    fn from(ironwood_nullifier: ironwood::Nullifier) -> Self {
93        Self::Ironwood(ironwood_nullifier)
94    }
95}
96
97/// Identify a block by hash or height.
98///
99/// This enum implements `From` for [`block::Hash`] and [`block::Height`],
100/// so it can be created using `hash.into()` or `height.into()`.
101#[derive(Copy, Clone, Debug, PartialEq, Eq)]
102pub enum HashOrHeight {
103    /// A block identified by hash.
104    Hash(block::Hash),
105    /// A block identified by height.
106    Height(block::Height),
107}
108
109impl HashOrHeight {
110    /// Unwrap the inner height or attempt to retrieve the height for a given
111    /// hash if one exists.
112    pub fn height_or_else<F>(self, op: F) -> Option<block::Height>
113    where
114        F: FnOnce(block::Hash) -> Option<block::Height>,
115    {
116        match self {
117            HashOrHeight::Hash(hash) => op(hash),
118            HashOrHeight::Height(height) => Some(height),
119        }
120    }
121
122    /// Unwrap the inner hash or attempt to retrieve the hash for a given
123    /// height if one exists.
124    ///
125    /// # Consensus
126    ///
127    /// In the non-finalized state, a height can have multiple valid hashes.
128    /// We typically use the hash that is currently on the best chain.
129    pub fn hash_or_else<F>(self, op: F) -> Option<block::Hash>
130    where
131        F: FnOnce(block::Height) -> Option<block::Hash>,
132    {
133        match self {
134            HashOrHeight::Hash(hash) => Some(hash),
135            HashOrHeight::Height(height) => op(height),
136        }
137    }
138
139    /// Returns the hash if this is a [`HashOrHeight::Hash`].
140    pub fn hash(&self) -> Option<block::Hash> {
141        if let HashOrHeight::Hash(hash) = self {
142            Some(*hash)
143        } else {
144            None
145        }
146    }
147
148    /// Returns the height if this is a [`HashOrHeight::Height`].
149    pub fn height(&self) -> Option<block::Height> {
150        if let HashOrHeight::Height(height) = self {
151            Some(*height)
152        } else {
153            None
154        }
155    }
156
157    /// Constructs a new [`HashOrHeight`] from a string containing a hash or a positive or negative
158    /// height.
159    ///
160    /// When the provided `hash_or_height` contains a negative height, the `tip_height` parameter
161    /// needs to be `Some` since height `-1` points to the tip.
162    pub fn new(hash_or_height: &str, tip_height: Option<block::Height>) -> Result<Self, String> {
163        hash_or_height
164            .parse()
165            .map(Self::Hash)
166            .or_else(|_| hash_or_height.parse().map(Self::Height))
167            .or_else(|_| {
168                hash_or_height
169                    .parse()
170                    .map_err(|_| "could not parse negative height")
171                    .and_then(|d: HeightDiff| {
172                        if d.is_negative() {
173                            {
174                                Ok(HashOrHeight::Height(
175                                    tip_height
176                                        .ok_or("missing tip height")?
177                                        .add(d)
178                                        .ok_or("underflow when adding negative height to tip")?
179                                        .next()
180                                        .map_err(|_| "height -1 needs to point to tip")?,
181                                ))
182                            }
183                        } else {
184                            Err("height was not negative")
185                        }
186                    })
187            })
188            .map_err(|_| {
189                "parse error: could not convert the input string to a hash or height".to_string()
190            })
191    }
192}
193
194impl std::fmt::Display for HashOrHeight {
195    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196        match self {
197            HashOrHeight::Hash(hash) => write!(f, "{hash}"),
198            HashOrHeight::Height(height) => write!(f, "{}", height.0),
199        }
200    }
201}
202
203impl From<block::Hash> for HashOrHeight {
204    fn from(hash: block::Hash) -> Self {
205        Self::Hash(hash)
206    }
207}
208
209impl From<block::Height> for HashOrHeight {
210    fn from(height: block::Height) -> Self {
211        Self::Height(height)
212    }
213}
214
215impl From<(block::Height, block::Hash)> for HashOrHeight {
216    fn from((_height, hash): (block::Height, block::Hash)) -> Self {
217        // Hash is more specific than height for the non-finalized state
218        hash.into()
219    }
220}
221
222impl From<(block::Hash, block::Height)> for HashOrHeight {
223    fn from((hash, _height): (block::Hash, block::Height)) -> Self {
224        hash.into()
225    }
226}
227
228impl std::str::FromStr for HashOrHeight {
229    type Err = SerializationError;
230
231    fn from_str(s: &str) -> Result<Self, Self::Err> {
232        s.parse()
233            .map(Self::Hash)
234            .or_else(|_| s.parse().map(Self::Height))
235            .map_err(|_| {
236                SerializationError::Parse("could not convert the input string to a hash or height")
237            })
238    }
239}
240
241/// A block which has undergone semantic validation and has been prepared for
242/// contextual validation.
243///
244/// It is the constructor's responsibility to perform semantic validation and to
245/// ensure that all fields are consistent.
246///
247/// This structure contains data from contextual validation, which is computed in
248/// the *service caller*'s task, not inside the service call itself. This allows
249/// moving work out of the single-threaded state service.
250#[derive(Clone, Debug, PartialEq, Eq)]
251pub struct SemanticallyVerifiedBlock {
252    /// The block to commit to the state.
253    pub block: Arc<Block>,
254    /// The hash of the block.
255    pub hash: block::Hash,
256    /// The height of the block.
257    pub height: block::Height,
258    /// New transparent outputs created in this block, indexed by
259    /// [`OutPoint`](transparent::OutPoint).
260    ///
261    /// Each output is tagged with its transaction index in the block.
262    /// (The outputs of earlier transactions in a block can be spent by later
263    /// transactions.)
264    ///
265    /// Note: although these transparent outputs are newly created, they may not
266    /// be unspent, since a later transaction in a block can spend outputs of an
267    /// earlier transaction.
268    ///
269    /// This field can also contain unrelated outputs, which are ignored.
270    pub new_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
271    /// A precomputed list of the hashes of the transactions in this block,
272    /// in the same order as `block.transactions`.
273    pub transaction_hashes: Arc<[transaction::Hash]>,
274
275    /// The local time at which the block verifier received this block, if it passed
276    /// through the block verifier.
277    ///
278    /// Used by `Chain::cmp` to prefer the first-received chain when cumulative works
279    /// are equal, per the protocol specification:
280    ///
281    /// > To break ties between leaf blocks, a node will prefer the block that it received first.
282    ///
283    /// <https://zips.z.cash/protocol/protocol.pdf#blockchain>
284    ///
285    /// This is node-local, in-memory metadata, not consensus data. It is `None` for
286    /// blocks constructed outside the block verifier (checkpoint sync, backup restore,
287    /// tests); `Chain::cmp` treats unstamped blocks as received before any stamped
288    /// block, like `zcashd`'s disk-loaded blocks, which all share `nSequenceId` 0.
289    pub received_time: Option<Instant>,
290}
291
292/// A block ready to be committed directly to the finalized state with
293/// a small number of checks if compared with a `ContextuallyVerifiedBlock`.
294///
295/// This is exposed for use in checkpointing.
296///
297/// Note: The difference between a `CheckpointVerifiedBlock` and a `ContextuallyVerifiedBlock` is
298/// that the `CheckpointVerifier` doesn't bind the transaction authorizing data to the
299/// `ChainHistoryBlockTxAuthCommitmentHash`, but the `NonFinalizedState` and `FinalizedState` do.
300#[derive(Clone, Debug, PartialEq, Eq)]
301pub struct CheckpointVerifiedBlock(pub(crate) SemanticallyVerifiedBlock);
302
303// Some fields are pub(crate), so we can add whatever db-format-dependent
304// precomputation we want here without leaking internal details.
305
306/// A contextually verified block, ready to be committed directly to the finalized state with no
307/// checks, if it becomes the root of the best non-finalized chain.
308///
309/// Used by the state service and non-finalized `Chain`.
310///
311/// Note: The difference between a `CheckpointVerifiedBlock` and a `ContextuallyVerifiedBlock` is
312/// that the `CheckpointVerifier` doesn't bind the transaction authorizing data to the
313/// `ChainHistoryBlockTxAuthCommitmentHash`, but the `NonFinalizedState` and `FinalizedState` do.
314#[derive(Clone, Debug, PartialEq, Eq)]
315pub struct ContextuallyVerifiedBlock {
316    /// The block to commit to the state.
317    pub(crate) block: Arc<Block>,
318
319    /// The hash of the block.
320    pub(crate) hash: block::Hash,
321
322    /// The height of the block.
323    pub(crate) height: block::Height,
324
325    /// New transparent outputs created in this block, indexed by
326    /// [`OutPoint`](transparent::OutPoint).
327    ///
328    /// Note: although these transparent outputs are newly created, they may not
329    /// be unspent, since a later transaction in a block can spend outputs of an
330    /// earlier transaction.
331    ///
332    /// This field can also contain unrelated outputs, which are ignored.
333    pub(crate) new_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
334
335    /// The outputs spent by this block, indexed by the [`transparent::Input`]'s
336    /// [`OutPoint`](transparent::OutPoint).
337    ///
338    /// Note: these inputs can come from earlier transactions in this block,
339    /// or earlier blocks in the chain.
340    ///
341    /// This field can also contain unrelated outputs, which are ignored.
342    pub(crate) spent_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
343
344    /// A precomputed list of the hashes of the transactions in this block,
345    /// in the same order as `block.transactions`.
346    pub(crate) transaction_hashes: Arc<[transaction::Hash]>,
347
348    /// The sum of the chain value pool changes of all transactions in this block.
349    pub(crate) chain_value_pool_change: ValueBalance<NegativeAllowed>,
350
351    /// The local time at which the block verifier received this block, if it passed
352    /// through the block verifier, copied from the [`SemanticallyVerifiedBlock`] it was
353    /// built from. See that type's `received_time` field for details.
354    pub(crate) received_time: Option<Instant>,
355}
356
357/// Wraps note commitment trees and the history tree together.
358///
359/// The default instance represents the treestate that corresponds to the genesis block.
360#[derive(Clone, Debug, Default, Eq, PartialEq)]
361pub struct Treestate {
362    /// Note commitment trees.
363    pub note_commitment_trees: NoteCommitmentTrees,
364    /// History tree.
365    pub history_tree: Arc<HistoryTree>,
366}
367
368impl Treestate {
369    #[allow(missing_docs)]
370    pub(crate) fn new(
371        note_commitment_trees: NoteCommitmentTrees,
372        history_tree: Arc<HistoryTree>,
373    ) -> Self {
374        Self {
375            note_commitment_trees,
376            history_tree,
377        }
378    }
379}
380
381/// Contains a block ready to be committed.
382///
383/// Zebra's state service passes this `enum` over to the finalized state
384/// when committing a block.
385#[allow(missing_docs, clippy::large_enum_variant)]
386pub enum FinalizableBlock {
387    Checkpoint {
388        checkpoint_verified: CheckpointVerifiedBlock,
389    },
390    Contextual {
391        contextually_verified: ContextuallyVerifiedBlock,
392        treestate: Treestate,
393    },
394}
395
396/// Contains a block with all its associated data that the finalized state can commit to its
397/// database.
398///
399/// Note that it's the constructor's responsibility to ensure that all data is valid and verified.
400pub struct FinalizedBlock {
401    /// The block to commit to the state.
402    pub(super) block: Arc<Block>,
403    /// The hash of the block.
404    pub(super) hash: block::Hash,
405    /// The height of the block.
406    pub(super) height: block::Height,
407    /// New transparent outputs created in this block, indexed by
408    /// [`OutPoint`](transparent::OutPoint).
409    pub(super) new_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
410    /// A precomputed list of the hashes of the transactions in this block, in the same order as
411    /// `block.transactions`.
412    pub(super) transaction_hashes: Arc<[transaction::Hash]>,
413    /// The tresstate associated with the block.
414    pub(super) treestate: Treestate,
415    /// This block's deferred pool value balance change.
416    pub(super) deferred_pool_balance_change: DeferredPoolBalanceChange,
417}
418
419impl FinalizedBlock {
420    /// Constructs [`FinalizedBlock`] from [`CheckpointVerifiedBlock`] and its [`Treestate`].
421    pub fn from_checkpoint_verified(
422        block: CheckpointVerifiedBlock,
423        treestate: Treestate,
424        deferred_pool_balance_change: DeferredPoolBalanceChange,
425    ) -> Self {
426        Self::from_semantically_verified(
427            SemanticallyVerifiedBlock::from(block),
428            treestate,
429            deferred_pool_balance_change,
430        )
431    }
432
433    /// Constructs [`FinalizedBlock`] from [`ContextuallyVerifiedBlock`] and its [`Treestate`].
434    pub fn from_contextually_verified(
435        block: ContextuallyVerifiedBlock,
436        treestate: Treestate,
437        deferred_pool_balance_change: DeferredPoolBalanceChange,
438    ) -> Self {
439        Self::from_semantically_verified(
440            SemanticallyVerifiedBlock::from(block),
441            treestate,
442            deferred_pool_balance_change,
443        )
444    }
445
446    /// Constructs [`FinalizedBlock`] from [`SemanticallyVerifiedBlock`] and its [`Treestate`].
447    fn from_semantically_verified(
448        block: SemanticallyVerifiedBlock,
449        treestate: Treestate,
450        deferred_pool_balance_change: DeferredPoolBalanceChange,
451    ) -> Self {
452        Self {
453            block: block.block,
454            hash: block.hash,
455            height: block.height,
456            new_outputs: block.new_outputs,
457            transaction_hashes: block.transaction_hashes,
458            treestate,
459            deferred_pool_balance_change,
460        }
461    }
462}
463
464impl FinalizableBlock {
465    /// Create a new [`FinalizableBlock`] given a [`ContextuallyVerifiedBlock`].
466    pub fn new(contextually_verified: ContextuallyVerifiedBlock, treestate: Treestate) -> Self {
467        Self::Contextual {
468            contextually_verified,
469            treestate,
470        }
471    }
472
473    #[cfg(test)]
474    /// Extract a [`Block`] from a [`FinalizableBlock`] variant.
475    pub fn inner_block(&self) -> Arc<Block> {
476        match self {
477            FinalizableBlock::Checkpoint {
478                checkpoint_verified,
479            } => checkpoint_verified.block.clone(),
480            FinalizableBlock::Contextual {
481                contextually_verified,
482                ..
483            } => contextually_verified.block.clone(),
484        }
485    }
486}
487
488impl From<CheckpointVerifiedBlock> for FinalizableBlock {
489    fn from(checkpoint_verified: CheckpointVerifiedBlock) -> Self {
490        Self::Checkpoint {
491            checkpoint_verified,
492        }
493    }
494}
495
496impl From<Arc<Block>> for FinalizableBlock {
497    fn from(block: Arc<Block>) -> Self {
498        Self::from(CheckpointVerifiedBlock::from(block))
499    }
500}
501
502impl From<&SemanticallyVerifiedBlock> for SemanticallyVerifiedBlock {
503    fn from(semantically_verified: &SemanticallyVerifiedBlock) -> Self {
504        semantically_verified.clone()
505    }
506}
507
508// Doing precomputation in these impls means that it will be done in
509// the *service caller*'s task, not inside the service call itself.
510// This allows moving work out of the single-threaded state service.
511
512impl ContextuallyVerifiedBlock {
513    /// Create a block that's ready for non-finalized `Chain` contextual validation,
514    /// using a [`SemanticallyVerifiedBlock`] and the UTXOs it spends.
515    ///
516    /// When combined, `semantically_verified.new_outputs` and `spent_utxos` must contain
517    /// the [`Utxo`](transparent::Utxo)s spent by every transparent input in this block,
518    /// including UTXOs created by earlier transactions in this block.
519    ///
520    /// `previous_value_pools` and `deferred_pool_balance_change` must use the exact parent.
521    /// This constructor calculates ledger changes, but does not validate coinbase payouts or
522    /// funding outputs. The state service checks parent-dependent subsidy and miner fees before
523    /// pushing the block onto the non-finalized chain.
524    ///
525    /// A [`ContextuallyVerifiedBlock`] is only contextually valid after all state service
526    /// validation succeeds; construction or
527    /// [`Chain::push()`](crate::service::non_finalized_state::Chain::push) alone is not sufficient.
528    pub fn with_block_and_spent_utxos(
529        semantically_verified: SemanticallyVerifiedBlock,
530        spent_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
531        deferred_pool_balance_change: DeferredPoolBalanceChange,
532        network: &Network,
533        previous_value_pools: ValueBalance<NonNegative>,
534    ) -> Result<Self, ValueBalanceError> {
535        Self::with_block_spent_utxos_and_fees(
536            semantically_verified,
537            spent_outputs,
538            deferred_pool_balance_change,
539            network,
540            previous_value_pools,
541        )
542        .map(|(contextually_verified, _transaction_fees)| contextually_verified)
543    }
544
545    /// Like [`Self::with_block_and_spent_utxos`], and also returns the block's gross transaction
546    /// fees, as returned by [`Block::chain_value_pool_change_and_fees`].
547    pub(crate) fn with_block_spent_utxos_and_fees(
548        semantically_verified: SemanticallyVerifiedBlock,
549        mut spent_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
550        deferred_pool_balance_change: DeferredPoolBalanceChange,
551        network: &Network,
552        previous_value_pools: ValueBalance<NonNegative>,
553    ) -> Result<(Self, Option<Amount<NonNegative>>), ValueBalanceError> {
554        let SemanticallyVerifiedBlock {
555            block,
556            hash,
557            height,
558            new_outputs,
559            transaction_hashes,
560            received_time,
561        } = semantically_verified;
562
563        // This is redundant for the non-finalized state,
564        // but useful to make some tests pass more easily.
565        //
566        // TODO: fix the tests, and stop adding unrelated outputs.
567        spent_outputs.extend(new_outputs.clone());
568
569        let (chain_value_pool_change, transaction_fees) = block.chain_value_pool_change_and_fees(
570            &utxos_from_ordered_utxos(
571                spent_outputs
572                    .iter()
573                    .map(|(outpoint, utxo)| (*outpoint, utxo.clone())),
574            ),
575            deferred_pool_balance_change,
576            network,
577            previous_value_pools,
578        )?;
579
580        Ok((
581            Self {
582                block,
583                hash,
584                height,
585                new_outputs,
586                spent_outputs,
587                transaction_hashes,
588                chain_value_pool_change,
589                received_time,
590            },
591            transaction_fees,
592        ))
593    }
594}
595
596impl CheckpointVerifiedBlock {
597    /// Creates a [`CheckpointVerifiedBlock`] from [`Block`] with optional deferred balance and
598    /// optional pre-computed hash.
599    pub fn new(block: Arc<Block>, hash: Option<block::Hash>) -> Self {
600        Self::with_hash(block.clone(), hash.unwrap_or(block.hash()))
601    }
602    /// Creates a block that's ready to be committed to the finalized state,
603    /// using a precalculated [`block::Hash`].
604    ///
605    /// Note: a [`CheckpointVerifiedBlock`] isn't actually finalized
606    /// until [`Request::CommitCheckpointVerifiedBlock`] returns success.
607    pub fn with_hash(block: Arc<Block>, hash: block::Hash) -> Self {
608        Self(SemanticallyVerifiedBlock::with_hash(block, hash))
609    }
610}
611
612impl SemanticallyVerifiedBlock {
613    /// Creates [`SemanticallyVerifiedBlock`] from [`Block`] and [`block::Hash`].
614    pub fn with_hash(block: Arc<Block>, hash: block::Hash) -> Self {
615        let height = block
616            .coinbase_height()
617            .expect("semantically verified block should have a coinbase height");
618        let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
619        let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
620
621        Self {
622            block,
623            hash,
624            height,
625            new_outputs,
626            transaction_hashes,
627            received_time: None,
628        }
629    }
630}
631
632impl From<Arc<Block>> for CheckpointVerifiedBlock {
633    fn from(block: Arc<Block>) -> Self {
634        CheckpointVerifiedBlock(SemanticallyVerifiedBlock::from(block))
635    }
636}
637
638impl From<Arc<Block>> for SemanticallyVerifiedBlock {
639    fn from(block: Arc<Block>) -> Self {
640        let hash = block.hash();
641        let height = block
642            .coinbase_height()
643            .expect("semantically verified block should have a coinbase height");
644        let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
645        let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
646
647        Self {
648            block,
649            hash,
650            height,
651            new_outputs,
652            transaction_hashes,
653            received_time: None,
654        }
655    }
656}
657
658impl From<ContextuallyVerifiedBlock> for SemanticallyVerifiedBlock {
659    fn from(valid: ContextuallyVerifiedBlock) -> Self {
660        Self {
661            block: valid.block,
662            hash: valid.hash,
663            height: valid.height,
664            new_outputs: valid.new_outputs,
665            transaction_hashes: valid.transaction_hashes,
666            received_time: valid.received_time,
667        }
668    }
669}
670
671impl From<FinalizedBlock> for SemanticallyVerifiedBlock {
672    fn from(finalized: FinalizedBlock) -> Self {
673        Self {
674            block: finalized.block,
675            hash: finalized.hash,
676            height: finalized.height,
677            new_outputs: finalized.new_outputs,
678            transaction_hashes: finalized.transaction_hashes,
679            // The finalized state does not track receipt times, so there is no stamp.
680            received_time: None,
681        }
682    }
683}
684
685impl From<CheckpointVerifiedBlock> for SemanticallyVerifiedBlock {
686    fn from(checkpoint_verified: CheckpointVerifiedBlock) -> Self {
687        checkpoint_verified.0
688    }
689}
690
691impl Deref for CheckpointVerifiedBlock {
692    type Target = SemanticallyVerifiedBlock;
693
694    fn deref(&self) -> &Self::Target {
695        &self.0
696    }
697}
698impl DerefMut for CheckpointVerifiedBlock {
699    fn deref_mut(&mut self) -> &mut Self::Target {
700        &mut self.0
701    }
702}
703
704/// Helper trait for convenient access to expected response and error types.
705pub trait MappedRequest: Sized + Send + 'static {
706    /// Expected response type for this state request.
707    type MappedResponse;
708    /// Expected error type for this state request.
709    type Error: std::error::Error + std::fmt::Display + 'static;
710
711    /// Maps the request type to a [`Request`].
712    fn map_request(self) -> Request;
713
714    /// Maps the expected [`Response`] variant for this request to the mapped response type.
715    fn map_response(response: Response) -> Self::MappedResponse;
716
717    /// Accepts a state service to call, maps this request to a [`Request`], waits for the state to be ready,
718    /// calls the state with the mapped request, then maps the success or error response to the expected response
719    /// or error type for this request.
720    ///
721    /// Returns a [`Result<MappedResponse, LayeredServicesError<RequestError>>`].
722    #[allow(async_fn_in_trait)]
723    async fn mapped_oneshot<State>(
724        self,
725        state: &mut State,
726    ) -> Result<Self::MappedResponse, LayeredStateError<Self::Error>>
727    where
728        State: Service<Request, Response = Response, Error = BoxError>,
729        State::Future: Send,
730    {
731        let response = state.ready().await?.call(self.map_request()).await?;
732        Ok(Self::map_response(response))
733    }
734}
735
736/// Performs contextual validation of the given semantically verified block,
737/// committing it to the state if successful.
738///
739/// See the [`crate`] documentation and [`Request::CommitSemanticallyVerifiedBlock`] for details.
740pub struct CommitSemanticallyVerifiedBlockRequest(pub SemanticallyVerifiedBlock);
741
742impl MappedRequest for CommitSemanticallyVerifiedBlockRequest {
743    type MappedResponse = block::Hash;
744    type Error = CommitSemanticallyVerifiedError;
745
746    fn map_request(self) -> Request {
747        Request::CommitSemanticallyVerifiedBlock(self.0)
748    }
749
750    fn map_response(response: Response) -> Self::MappedResponse {
751        match response {
752            Response::Committed(hash) => hash,
753            _ => unreachable!("wrong response variant for request"),
754        }
755    }
756}
757
758/// Commit a checkpointed block to the state
759///
760/// See the [`crate`] documentation and [`Request::CommitCheckpointVerifiedBlock`] for details.
761#[allow(dead_code)]
762pub struct CommitCheckpointVerifiedBlockRequest(pub CheckpointVerifiedBlock);
763
764impl MappedRequest for CommitCheckpointVerifiedBlockRequest {
765    type MappedResponse = block::Hash;
766    type Error = CommitCheckpointVerifiedError;
767
768    fn map_request(self) -> Request {
769        Request::CommitCheckpointVerifiedBlock(self.0)
770    }
771
772    fn map_response(response: Response) -> Self::MappedResponse {
773        match response {
774            Response::Committed(hash) => hash,
775            _ => unreachable!("wrong response variant for request"),
776        }
777    }
778}
779
780/// Request to invalidate a block in the state.
781///
782/// See the [`crate`] documentation and [`Request::InvalidateBlock`] for details.
783#[allow(dead_code)]
784pub struct InvalidateBlockRequest(pub block::Hash);
785
786impl MappedRequest for InvalidateBlockRequest {
787    type MappedResponse = block::Hash;
788    type Error = InvalidateError;
789
790    fn map_request(self) -> Request {
791        Request::InvalidateBlock(self.0)
792    }
793
794    fn map_response(response: Response) -> Self::MappedResponse {
795        match response {
796            Response::Invalidated(hash) => hash,
797            _ => unreachable!("wrong response variant for request"),
798        }
799    }
800}
801
802/// Request to reconsider a previously invalidated block and re-commit it to the state.
803///
804/// See the [`crate`] documentation and [`Request::ReconsiderBlock`] for details.
805#[allow(dead_code)]
806pub struct ReconsiderBlockRequest(pub block::Hash);
807
808impl MappedRequest for ReconsiderBlockRequest {
809    type MappedResponse = Vec<block::Hash>;
810    type Error = ReconsiderError;
811
812    fn map_request(self) -> Request {
813        Request::ReconsiderBlock(self.0)
814    }
815
816    fn map_response(response: Response) -> Self::MappedResponse {
817        match response {
818            Response::Reconsidered(hashes) => hashes,
819            _ => unreachable!("wrong response variant for request"),
820        }
821    }
822}
823
824/// Request a UTXO, waiting for it to arrive if it is not yet available.
825///
826/// See the [`crate`] documentation and [`Request::AwaitUtxo`] for details.
827#[allow(dead_code)]
828pub struct AwaitUtxoRequest(pub transparent::OutPoint);
829
830impl MappedRequest for AwaitUtxoRequest {
831    type MappedResponse = transparent::Utxo;
832    type Error = AwaitUtxoError;
833
834    fn map_request(self) -> Request {
835        Request::AwaitUtxo(self.0)
836    }
837
838    fn map_response(response: Response) -> Self::MappedResponse {
839        match response {
840            Response::Utxo(utxo) => utxo,
841            _ => unreachable!("wrong response variant for request"),
842        }
843    }
844}
845
846#[derive(Clone, Debug, PartialEq, Eq)]
847/// A query about or modification to the chain state, via the
848/// [`StateService`](crate::service::StateService).
849pub enum Request {
850    /// Performs contextual validation of the given semantically verified block,
851    /// committing it to the state if successful.
852    ///
853    /// This request can be made out-of-order; the state service will queue it
854    /// until its parent is ready.
855    ///
856    /// Returns [`Response::Committed`] with the hash of the block when it is
857    /// committed to the state, or a [`CommitSemanticallyVerifiedError`][0] if
858    /// the block fails contextual validation or otherwise could not be committed.
859    ///
860    /// This request cannot be cancelled once submitted; dropping the response
861    /// future will have no effect on whether it is eventually processed. A
862    /// request to commit a block which has been queued internally but not yet
863    /// committed will fail the older request and replace it with the newer request.
864    ///
865    /// # Correctness
866    ///
867    /// Block commit requests should be wrapped in a timeout, so that
868    /// out-of-order and invalid requests do not hang indefinitely. See the [`crate`]
869    /// documentation for details.
870    ///
871    /// [0]: (crate::error::CommitSemanticallyVerifiedError)
872    CommitSemanticallyVerifiedBlock(SemanticallyVerifiedBlock),
873
874    /// Commit a checkpointed block to the state, skipping most but not all
875    /// contextual validation.
876    ///
877    /// This is exposed for use in checkpointing, which produces checkpoint vefified
878    /// blocks. This request can be made out-of-order; the state service will queue
879    /// it until its parent is ready.
880    ///
881    /// Returns [`Response::Committed`] with the hash of the newly committed
882    /// block, or a [`CommitCheckpointVerifiedError`][0] if the block could not be
883    /// committed to the state.
884    ///
885    /// This request cannot be cancelled once submitted; dropping the response
886    /// future will have no effect on whether it is eventually processed.
887    /// Duplicate requests will replace the older duplicate, and return an error
888    /// in its response future.
889    ///
890    /// # Note
891    ///
892    /// [`SemanticallyVerifiedBlock`], [`ContextuallyVerifiedBlock`] and
893    /// [`CheckpointVerifiedBlock`] are an internal Zebra implementation detail.
894    /// There is no difference between these blocks on the Zcash network, or in Zebra's
895    /// network or syncer implementations.
896    ///
897    /// # Consensus
898    ///
899    /// Checkpointing is allowed under the Zcash "social consensus" rules.
900    /// Zebra checkpoints both settled network upgrades, and blocks past the rollback limit.
901    /// (By the time Zebra release is tagged, its final checkpoint is typically hours or days old.)
902    ///
903    /// > A network upgrade is settled on a given network when there is a social consensus
904    /// > that it has activated with a given activation block hash. A full validator that
905    /// > potentially risks Mainnet funds or displays Mainnet transaction information to a user
906    /// > MUST do so only for a block chain that includes the activation block of the most
907    /// > recent settled network upgrade, with the corresponding activation block hash.
908    /// > ...
909    /// > A full validator MAY impose a limit on the number of blocks it will “roll back”
910    /// > when switching from one best valid block chain to another that is not a descendent.
911    /// > For `zcashd` and `zebra` this limit is 100 blocks.
912    ///
913    /// <https://zips.z.cash/protocol/protocol.pdf#blockchain>
914    ///
915    /// Note: Zebra's local rollback window
916    /// ([`MAX_BLOCK_REORG_HEIGHT`](crate::MAX_BLOCK_REORG_HEIGHT)) is now 1000 blocks, larger
917    /// than the 100 quoted from the protocol specification above.
918    ///
919    /// # Correctness
920    ///
921    /// Block commit requests should be wrapped in a timeout, so that
922    /// out-of-order and invalid requests do not hang indefinitely. See the [`crate`]
923    /// documentation for details.
924    ///
925    /// [0]: (crate::error::CommitCheckpointVerifiedError)
926    CommitCheckpointVerifiedBlock(CheckpointVerifiedBlock),
927
928    /// Computes the depth in the current best chain of the block identified by the given hash.
929    ///
930    /// Returns
931    ///
932    /// * [`Response::Depth(Some(depth))`](Response::Depth) if the block is in the best chain;
933    /// * [`Response::Depth(None)`](Response::Depth) otherwise.
934    Depth(block::Hash),
935
936    /// Returns [`Response::Tip(Option<(Height, block::Hash)>)`](Response::Tip)
937    /// with the current best chain tip.
938    Tip,
939
940    /// Computes a block locator object based on the current best chain.
941    ///
942    /// Returns [`Response::BlockLocator`] with hashes starting
943    /// from the best chain tip, and following the chain of previous
944    /// hashes. The first hash is the best chain tip. The last hash is
945    /// the tip of the finalized portion of the state. Block locators
946    /// are not continuous - some intermediate hashes might be skipped.
947    ///
948    /// If the state is empty, the block locator is also empty.
949    BlockLocator,
950
951    /// Looks up a transaction by hash in the current best chain.
952    ///
953    /// Returns
954    ///
955    /// * [`Response::Transaction(Some(Arc<Transaction>))`](Response::Transaction) if the transaction is in the best chain;
956    /// * [`Response::Transaction(None)`](Response::Transaction) otherwise.
957    Transaction(transaction::Hash),
958
959    /// Looks up a transaction by hash in any chain.
960    ///
961    /// Returns
962    ///
963    /// * [`Response::AnyChainTransaction(Some(AnyTx))`](Response::AnyChainTransaction)
964    ///   if the transaction is in any chain;
965    /// * [`Response::AnyChainTransaction(None)`](Response::AnyChainTransaction)
966    ///   otherwise.
967    AnyChainTransaction(transaction::Hash),
968
969    /// Looks up a UTXO identified by the given [`OutPoint`](transparent::OutPoint),
970    /// returning `None` immediately if it is unknown.
971    ///
972    /// Checks verified blocks in the finalized chain and the _best_ non-finalized chain.
973    UnspentBestChainUtxo(transparent::OutPoint),
974
975    /// Looks up a block by hash or height in the current best chain.
976    ///
977    /// Returns
978    ///
979    /// * [`Response::Block(Some(Arc<Block>))`](Response::Block) if the block is in the best chain;
980    /// * [`Response::Block(None)`](Response::Block) otherwise.
981    ///
982    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
983    /// [`block::Height`] using `.into()`.
984    Block(HashOrHeight),
985
986    /// Looks up a block by hash in any current chain or by height in the current best chain.
987    ///
988    /// Returns
989    ///
990    /// * [`Response::Block(Some(Arc<Block>))`](Response::Block) if the block hash is in any chain, or,
991    ///   if the block height is in the best chain;
992    /// * [`Response::Block(None)`](Response::Block) otherwise.
993    ///
994    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
995    /// [`block::Height`] using `.into()`.
996    AnyChainBlock(HashOrHeight),
997
998    //// Same as Block, but also returns serialized block size.
999    ////
1000    /// Returns
1001    ///
1002    /// * [`ReadResponse::BlockAndSize(Some((Arc<Block>, usize)))`](ReadResponse::BlockAndSize) if the block is in the best chain;
1003    /// * [`ReadResponse::BlockAndSize(None)`](ReadResponse::BlockAndSize) otherwise.
1004    BlockAndSize(HashOrHeight),
1005
1006    /// Looks up a block header by hash or height in the current best chain.
1007    ///
1008    /// Returns
1009    ///
1010    /// [`Response::BlockHeader(block::Header)`](Response::BlockHeader).
1011    ///
1012    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
1013    /// [`block::Height`] using `.into()`.
1014    BlockHeader(HashOrHeight),
1015
1016    /// Request a UTXO identified by the given [`OutPoint`](transparent::OutPoint),
1017    /// waiting until it becomes available if it is unknown.
1018    ///
1019    /// Checks the finalized chain, all non-finalized chains, queued unverified blocks,
1020    /// and any blocks that arrive at the state after the request future has been created.
1021    ///
1022    /// This request is purely informational, and there are no guarantees about
1023    /// whether the UTXO remains unspent or is on the best chain, or any chain.
1024    /// Its purpose is to allow asynchronous script verification or to wait until
1025    /// the UTXO arrives in the state before validating dependent transactions.
1026    ///
1027    /// Returns [`Response::Utxo`] with the UTXO, or an [`AwaitUtxoError`] if
1028    /// the state stopped responding before the UTXO arrived.
1029    ///
1030    /// # Correctness
1031    ///
1032    /// UTXO requests should be wrapped in a timeout, so that
1033    /// out-of-order and invalid requests do not hang indefinitely. See the [`crate`]
1034    /// documentation for details.
1035    ///
1036    /// Outdated requests are pruned on a regular basis.
1037    AwaitUtxo(transparent::OutPoint),
1038
1039    /// Finds the first hash that's in the peer's `known_blocks` and the local best chain.
1040    /// Returns a list of hashes that follow that intersection, from the best chain.
1041    ///
1042    /// If there is no matching hash in the best chain, starts from the genesis hash.
1043    ///
1044    /// Stops the list of hashes after:
1045    ///   * adding the best tip,
1046    ///   * adding the `stop` hash to the list, if it is in the best chain, or
1047    ///   * adding 500 hashes to the list.
1048    ///
1049    /// Returns an empty list if the state is empty.
1050    ///
1051    /// Returns
1052    ///
1053    /// [`Response::BlockHashes(Vec<block::Hash>)`](Response::BlockHashes).
1054    /// See <https://en.bitcoin.it/wiki/Protocol_documentation#getblocks>
1055    FindBlockHashes {
1056        /// Hashes of known blocks, ordered from highest height to lowest height.
1057        known_blocks: Vec<block::Hash>,
1058        /// Optionally, the last block hash to request.
1059        stop: Option<block::Hash>,
1060    },
1061
1062    /// Finds the first hash that's in the peer's `known_blocks` and the local best chain.
1063    /// Returns a list of headers that follow that intersection, from the best chain.
1064    ///
1065    /// If there is no matching hash in the best chain, starts from the genesis header.
1066    ///
1067    /// Stops the list of headers after:
1068    ///   * adding the best tip,
1069    ///   * adding the header matching the `stop` hash to the list, if it is in the best chain, or
1070    ///   * adding [`MAX_FIND_BLOCK_HEADERS_RESULTS`] headers to the list.
1071    ///
1072    /// Returns an empty list if the state is empty.
1073    ///
1074    /// Returns
1075    ///
1076    /// [`Response::BlockHeaders(Vec<block::Header>)`](Response::BlockHeaders).
1077    /// See <https://en.bitcoin.it/wiki/Protocol_documentation#getheaders>
1078    FindBlockHeaders {
1079        /// Hashes of known blocks, ordered from highest height to lowest height.
1080        known_blocks: Vec<block::Hash>,
1081        /// Optionally, the hash of the last header to request.
1082        stop: Option<block::Hash>,
1083    },
1084
1085    /// Contextually validates anchors and nullifiers of a transaction on the best chain
1086    ///
1087    /// Returns [`Response::ValidBestChainTipNullifiersAndAnchors`]
1088    CheckBestChainTipNullifiersAndAnchors(UnminedTx),
1089
1090    /// Calculates the median-time-past for the *next* block on the best chain.
1091    ///
1092    /// Returns [`Response::BestChainNextMedianTimePast`] when successful.
1093    BestChainNextMedianTimePast,
1094
1095    /// Looks up a block hash by height in the current best chain.
1096    ///
1097    /// Returns
1098    ///
1099    /// * [`Response::BlockHash(Some(hash))`](Response::BlockHash) if the block is in the best chain;
1100    /// * [`Response::BlockHash(None)`](Response::BlockHash) otherwise.
1101    BestChainBlockHash(block::Height),
1102
1103    /// Checks if a block is present anywhere in the state service.
1104    /// Looks up `hash` in block queues as well as the finalized chain and all non-finalized chains.
1105    ///
1106    /// Returns [`Response::KnownBlock(Some(Location))`](Response::KnownBlock) if the block is in the best state service.
1107    /// Returns [`Response::KnownBlock(None)`](Response::KnownBlock) otherwise.
1108    KnownBlock(block::Hash),
1109
1110    /// Invalidates a block in the non-finalized state with the provided hash if one is present, removing it and
1111    /// its child blocks, and rejecting it during contextual validation if it's resubmitted to the state.
1112    ///
1113    /// Returns [`Response::Invalidated`] with the hash of the invalidated block,
1114    /// or a [`InvalidateError`][0] if the block was not found, the state is still
1115    /// committing checkpointed blocks, or the request could not be processed.
1116    ///
1117    /// [0]: (crate::error::InvalidateError)
1118    InvalidateBlock(block::Hash),
1119
1120    /// Reconsiders a previously invalidated block in the non-finalized state with the provided hash if one is present.
1121    ///
1122    /// Returns [`Response::Reconsidered`] with the hash of the reconsidered block,
1123    /// or a [`ReconsiderError`][0] if the block was not previously invalidated,
1124    /// its parent chain is missing, or the state is not ready to process the request.
1125    ///
1126    /// [0]: (crate::error::ReconsiderError)
1127    ReconsiderBlock(block::Hash),
1128
1129    /// Performs contextual validation of the given block, but does not commit it to the state.
1130    ///
1131    /// Returns [`Response::ValidBlockProposal`] when successful.
1132    /// See `[ReadRequest::CheckBlockProposalValidity]` for details.
1133    CheckBlockProposalValidity(SemanticallyVerifiedBlock),
1134}
1135
1136impl Request {
1137    /// Returns a [`&'static str`](str) name of the variant representing this value.
1138    pub fn variant_name(&self) -> &'static str {
1139        match self {
1140            Request::CommitSemanticallyVerifiedBlock(_) => "commit_semantically_verified_block",
1141            Request::CommitCheckpointVerifiedBlock(_) => "commit_checkpoint_verified_block",
1142            Request::AwaitUtxo(_) => "await_utxo",
1143            Request::Depth(_) => "depth",
1144            Request::Tip => "tip",
1145            Request::BlockLocator => "block_locator",
1146            Request::Transaction(_) => "transaction",
1147            Request::AnyChainTransaction(_) => "any_chain_transaction",
1148            Request::UnspentBestChainUtxo { .. } => "unspent_best_chain_utxo",
1149            Request::Block(_) => "block",
1150            Request::AnyChainBlock(_) => "any_chain_block",
1151            Request::BlockAndSize(_) => "block_and_size",
1152            Request::BlockHeader(_) => "block_header",
1153            Request::FindBlockHashes { .. } => "find_block_hashes",
1154            Request::FindBlockHeaders { .. } => "find_block_headers",
1155            Request::CheckBestChainTipNullifiersAndAnchors(_) => {
1156                "best_chain_tip_nullifiers_anchors"
1157            }
1158            Request::BestChainNextMedianTimePast => "best_chain_next_median_time_past",
1159            Request::BestChainBlockHash(_) => "best_chain_block_hash",
1160            Request::KnownBlock(_) => "known_block",
1161            Request::InvalidateBlock(_) => "invalidate_block",
1162            Request::ReconsiderBlock(_) => "reconsider_block",
1163            Request::CheckBlockProposalValidity(_) => "check_block_proposal_validity",
1164        }
1165    }
1166
1167    /// Counts metric for StateService call
1168    pub fn count_metric(&self) {
1169        metrics::counter!(
1170            "state.requests",
1171            "service" => "state",
1172            "type" => self.variant_name()
1173        )
1174        .increment(1);
1175    }
1176}
1177
1178#[derive(Clone, Debug, PartialEq, Eq)]
1179/// A read-only query about the chain state, via the
1180/// [`ReadStateService`](crate::service::ReadStateService).
1181pub enum ReadRequest {
1182    /// Returns [`ReadResponse::UsageInfo(num_bytes: u64)`](ReadResponse::UsageInfo)
1183    /// with the current disk space usage in bytes.
1184    UsageInfo,
1185
1186    /// Returns [`ReadResponse::Tip(Option<(Height, block::Hash)>)`](ReadResponse::Tip)
1187    /// with the current best chain tip.
1188    Tip,
1189
1190    /// Returns [`ReadResponse::TipPoolValues(Option<(Height, block::Hash, ValueBalance)>)`](ReadResponse::TipPoolValues)
1191    /// with the pool values of the current best chain tip.
1192    TipPoolValues,
1193
1194    /// Looks up the block info after a block by hash or height in the current best chain.
1195    ///
1196    /// * [`ReadResponse::BlockInfo(Some(pool_values))`](ReadResponse::BlockInfo) if the block is in the best chain;
1197    /// * [`ReadResponse::BlockInfo(None)`](ReadResponse::BlockInfo) otherwise.
1198    BlockInfo(HashOrHeight),
1199
1200    /// Computes the depth in the current best chain of the block identified by the given hash.
1201    ///
1202    /// Returns
1203    ///
1204    /// * [`ReadResponse::Depth(Some(depth))`](ReadResponse::Depth) if the block is in the best chain;
1205    /// * [`ReadResponse::Depth(None)`](ReadResponse::Depth) otherwise.
1206    Depth(block::Hash),
1207
1208    /// Looks up a block by hash or height in the current best chain.
1209    ///
1210    /// Returns
1211    ///
1212    /// * [`ReadResponse::Block(Some(Arc<Block>))`](ReadResponse::Block) if the block is in the best chain;
1213    /// * [`ReadResponse::Block(None)`](ReadResponse::Block) otherwise.
1214    ///
1215    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
1216    /// [`block::Height`] using `.into()`.
1217    Block(HashOrHeight),
1218
1219    /// Looks up a block by hash in any current chain or by height in the current best chain.
1220    ///
1221    /// Returns
1222    ///
1223    /// * [`ReadResponse::Block(Some(Arc<Block>))`](ReadResponse::Block) if the block hash is in any chain, or
1224    ///   if the block height is in any chain, checking the best chain first
1225    ///   followed by side chains in order from most to least work.
1226    /// * [`ReadResponse::Block(None)`](ReadResponse::Block) otherwise.
1227    ///
1228    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
1229    /// [`block::Height`] using `.into()`.
1230    AnyChainBlock(HashOrHeight),
1231
1232    //// Same as Block, but also returns serialized block size.
1233    ////
1234    /// Returns
1235    ///
1236    /// * [`ReadResponse::BlockAndSize(Some((Arc<Block>, usize)))`](ReadResponse::BlockAndSize) if the block is in the best chain;
1237    /// * [`ReadResponse::BlockAndSize(None)`](ReadResponse::BlockAndSize) otherwise.
1238    BlockAndSize(HashOrHeight),
1239
1240    /// Looks up a block header by hash or height in the current best chain.
1241    ///
1242    /// Returns
1243    ///
1244    /// [`Response::BlockHeader(block::Header)`](Response::BlockHeader).
1245    ///
1246    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
1247    /// [`block::Height`] using `.into()`.
1248    BlockHeader(HashOrHeight),
1249
1250    /// Looks up the transparent outputs spent by the non-coinbase inputs of a
1251    /// block, using a block hash or height, in the current best chain.
1252    ///
1253    /// Returns
1254    ///
1255    /// * [`ReadResponse::SpentOutputs(Some(_))`](ReadResponse::SpentOutputs) with the spent
1256    ///   outputs keyed by the spending [`OutPoint`](transparent::OutPoint) if the block is in
1257    ///   the best chain;
1258    /// * [`ReadResponse::SpentOutputs(None)`](ReadResponse::SpentOutputs) otherwise.
1259    ///
1260    /// An outpoint whose spent output cannot be found in the best chain is omitted from the
1261    /// map, so callers must treat a missing outpoint as "prevout unknown", not an error.
1262    ///
1263    /// Used by the `getblock` RPC at verbosity 3.
1264    SpentOutputs(HashOrHeight),
1265
1266    /// Looks up a transaction by hash in the current best chain.
1267    ///
1268    /// Returns
1269    ///
1270    /// * [`ReadResponse::Transaction(Some(Arc<Transaction>))`](ReadResponse::Transaction) if the transaction is in the best chain;
1271    /// * [`ReadResponse::Transaction(None)`](ReadResponse::Transaction) otherwise.
1272    Transaction(transaction::Hash),
1273
1274    /// Looks up a transaction by hash in any chain.
1275    ///
1276    /// Returns
1277    ///
1278    /// * [`ReadResponse::AnyChainTransaction(Some(AnyTx))`](ReadResponse::AnyChainTransaction)
1279    ///   if the transaction is in any chain;
1280    /// * [`ReadResponse::AnyChainTransaction(None)`](ReadResponse::AnyChainTransaction)
1281    ///   otherwise.
1282    AnyChainTransaction(transaction::Hash),
1283
1284    /// Looks up the transaction IDs for a block, using a block hash or height.
1285    ///
1286    /// Returns
1287    ///
1288    /// * An ordered list of transaction hashes, or
1289    /// * `None` if the block was not found.
1290    ///
1291    /// Note: Each block has at least one transaction: the coinbase transaction.
1292    ///
1293    /// Returned txids are in the order they appear in the block.
1294    TransactionIdsForBlock(HashOrHeight),
1295
1296    /// Looks up the transaction IDs for a block, using a block hash or height,
1297    /// for any chain.
1298    ///
1299    /// Returns
1300    ///
1301    /// * An ordered list of transaction hashes and a flag indicating whether
1302    ///   the block is in the best chain, or
1303    /// * `None` if the block was not found.
1304    ///
1305    /// Note: Each block has at least one transaction: the coinbase transaction.
1306    ///
1307    /// Returned txids are in the order they appear in the block.
1308    AnyChainTransactionIdsForBlock(HashOrHeight),
1309
1310    /// Looks up a UTXO identified by the given [`OutPoint`](transparent::OutPoint),
1311    /// returning `None` immediately if it is unknown.
1312    ///
1313    /// Checks verified blocks in the finalized chain and the _best_ non-finalized chain.
1314    UnspentBestChainUtxo(transparent::OutPoint),
1315
1316    /// Looks up a UTXO identified by the given [`OutPoint`](transparent::OutPoint),
1317    /// returning `None` immediately if it is unknown.
1318    ///
1319    /// Checks verified blocks in the finalized chain and _all_ non-finalized chains.
1320    ///
1321    /// This request is purely informational, there is no guarantee that
1322    /// the UTXO remains unspent in the best chain.
1323    AnyChainUtxo(transparent::OutPoint),
1324
1325    /// Computes a block locator object based on the current best chain.
1326    ///
1327    /// Returns [`ReadResponse::BlockLocator`] with hashes starting
1328    /// from the best chain tip, and following the chain of previous
1329    /// hashes. The first hash is the best chain tip. The last hash is
1330    /// the tip of the finalized portion of the state. Block locators
1331    /// are not continuous - some intermediate hashes might be skipped.
1332    ///
1333    /// If the state is empty, the block locator is also empty.
1334    BlockLocator,
1335
1336    /// Finds the first hash that's in the peer's `known_blocks` and the local best chain.
1337    /// Returns a list of hashes that follow that intersection, from the best chain.
1338    ///
1339    /// If there is no matching hash in the best chain, starts from the genesis hash.
1340    ///
1341    /// Stops the list of hashes after:
1342    ///   * adding the best tip,
1343    ///   * adding the `stop` hash to the list, if it is in the best chain, or
1344    ///   * adding [`MAX_FIND_BLOCK_HASHES_RESULTS`] hashes to the list.
1345    ///
1346    /// Returns an empty list if the state is empty.
1347    ///
1348    /// Returns
1349    ///
1350    /// [`ReadResponse::BlockHashes(Vec<block::Hash>)`](ReadResponse::BlockHashes).
1351    /// See <https://en.bitcoin.it/wiki/Protocol_documentation#getblocks>
1352    FindBlockHashes {
1353        /// Hashes of known blocks, ordered from highest height to lowest height.
1354        known_blocks: Vec<block::Hash>,
1355        /// Optionally, the last block hash to request.
1356        stop: Option<block::Hash>,
1357    },
1358
1359    /// Finds the first hash that's in the peer's `known_blocks` and the local best chain.
1360    /// Returns a list of headers that follow that intersection, from the best chain.
1361    ///
1362    /// If there is no matching hash in the best chain, starts from the genesis header.
1363    ///
1364    /// Stops the list of headers after:
1365    ///   * adding the best tip,
1366    ///   * adding the header matching the `stop` hash to the list, if it is in the best chain, or
1367    ///   * adding [`MAX_FIND_BLOCK_HEADERS_RESULTS`] headers to the list.
1368    ///
1369    /// Returns an empty list if the state is empty.
1370    ///
1371    /// Returns
1372    ///
1373    /// [`ReadResponse::BlockHeaders(Vec<block::Header>)`](ReadResponse::BlockHeaders).
1374    /// See <https://en.bitcoin.it/wiki/Protocol_documentation#getheaders>
1375    FindBlockHeaders {
1376        /// Hashes of known blocks, ordered from highest height to lowest height.
1377        known_blocks: Vec<block::Hash>,
1378        /// Optionally, the hash of the last header to request.
1379        stop: Option<block::Hash>,
1380    },
1381
1382    /// Finds the fork point between the locator `known_blocks` and the best chain.
1383    ///
1384    /// `known_blocks` is a block locator. Returns the most recent locator entry that is
1385    /// on the best chain (the fork point), or `None` if no entry is on the best chain.
1386    /// Returns `None` if the state is empty.
1387    ///
1388    /// This is intentionally a narrow, best-chain-only query: it reports a single
1389    /// locator intersection against the best chain. It does not enumerate side-chain
1390    /// tips, branch lengths, or per-tip statuses, so it is not a general
1391    /// fork-monitoring API in the style of `getchaintips`. Callers that need to
1392    /// observe side chains should use a dedicated request rather than building on this
1393    /// one.
1394    ///
1395    /// The read state service rejects this request with an error if `known_blocks`
1396    /// is longer than [`MAX_BLOCK_LOCATOR_LENGTH`](zebra_chain::block::MAX_BLOCK_LOCATOR_LENGTH),
1397    /// so an untrusted caller cannot force an unbounded number of lookups. A locator
1398    /// built the usual way (one hash per standard block-locator height) is always
1399    /// within that bound.
1400    ///
1401    /// Returns
1402    ///
1403    /// [`ReadResponse::ForkPoint(Option<(block::Height, block::Hash)>)`](ReadResponse::ForkPoint).
1404    FindForkPoint {
1405        /// Hashes of known blocks, ordered from highest height to lowest height.
1406        known_blocks: Vec<block::Hash>,
1407    },
1408
1409    /// Looks up a Sapling note commitment tree either by a hash or height.
1410    ///
1411    /// Returns
1412    ///
1413    /// * [`ReadResponse::SaplingTree(Some(Arc<NoteCommitmentTree>))`](crate::ReadResponse::SaplingTree)
1414    ///   if the corresponding block contains a Sapling note commitment tree.
1415    /// * [`ReadResponse::SaplingTree(None)`](crate::ReadResponse::SaplingTree) otherwise.
1416    SaplingTree(HashOrHeight),
1417
1418    /// Looks up an Orchard note commitment tree either by a hash or height.
1419    ///
1420    /// Returns
1421    ///
1422    /// * [`ReadResponse::OrchardTree(Some(Arc<NoteCommitmentTree>))`](crate::ReadResponse::OrchardTree)
1423    ///   if the corresponding block contains a Sapling note commitment tree.
1424    /// * [`ReadResponse::OrchardTree(None)`](crate::ReadResponse::OrchardTree) otherwise.
1425    OrchardTree(HashOrHeight),
1426
1427    /// Looks up a Sapling note commitment tree by block hash in any current chain.
1428    ///
1429    /// Unlike [`SaplingTree`](Self::SaplingTree), this checks every non-finalized chain
1430    /// (and the finalized state), so it is immune to reorgs that move a block from the
1431    /// best chain onto a still-retained side chain.
1432    ///
1433    /// Only lookups by hash are supported, because the same height can have different
1434    /// treestates in different chain forks.
1435    ///
1436    /// Returns
1437    ///
1438    /// * [`ReadResponse::SaplingTree(Some(Arc<NoteCommitmentTree>))`](crate::ReadResponse::SaplingTree)
1439    ///   if the corresponding block contains a Sapling note commitment tree.
1440    /// * [`ReadResponse::SaplingTree(None)`](crate::ReadResponse::SaplingTree) otherwise.
1441    AnyChainSaplingTree(block::Hash),
1442
1443    /// Looks up an Orchard note commitment tree by block hash in any current chain.
1444    ///
1445    /// Unlike [`OrchardTree`](Self::OrchardTree), this checks every non-finalized chain
1446    /// (and the finalized state), so it is immune to reorgs that move a block from the
1447    /// best chain onto a still-retained side chain.
1448    ///
1449    /// Only lookups by hash are supported, because the same height can have different
1450    /// treestates in different chain forks.
1451    ///
1452    /// Returns
1453    ///
1454    /// * [`ReadResponse::OrchardTree(Some(Arc<NoteCommitmentTree>))`](crate::ReadResponse::OrchardTree)
1455    ///   if the corresponding block contains an Orchard note commitment tree.
1456    /// * [`ReadResponse::OrchardTree(None)`](crate::ReadResponse::OrchardTree) otherwise.
1457    AnyChainOrchardTree(block::Hash),
1458
1459    /// Looks up an Ironwood note commitment tree either by a hash or height.
1460    ///
1461    /// Returns
1462    ///
1463    /// * [`ReadResponse::IronwoodTree(Some(Arc<NoteCommitmentTree>))`](crate::ReadResponse::IronwoodTree)
1464    ///   if the corresponding block contains an Ironwood note commitment tree.
1465    /// * [`ReadResponse::IronwoodTree(None)`](crate::ReadResponse::IronwoodTree) otherwise.
1466    IronwoodTree(HashOrHeight),
1467
1468    /// Looks up an Ironwood note commitment tree by block hash in any current chain.
1469    ///
1470    /// Unlike [`IronwoodTree`](Self::IronwoodTree), this checks every non-finalized chain
1471    /// (and the finalized state), so it is immune to reorgs that move a block from the
1472    /// best chain onto a still-retained side chain.
1473    ///
1474    /// Only lookups by hash are supported, because the same height can have different
1475    /// treestates in different chain forks.
1476    ///
1477    /// Returns
1478    ///
1479    /// * [`ReadResponse::IronwoodTree(Some(Arc<NoteCommitmentTree>))`](crate::ReadResponse::IronwoodTree)
1480    ///   if the corresponding block contains an Ironwood note commitment tree.
1481    /// * [`ReadResponse::IronwoodTree(None)`](crate::ReadResponse::IronwoodTree) otherwise.
1482    AnyChainIronwoodTree(block::Hash),
1483
1484    /// Returns a list of Sapling note commitment subtrees by their indexes, starting at
1485    /// `start_index`, and returning up to `limit` subtrees.
1486    ///
1487    /// Returns
1488    ///
1489    /// * [`ReadResponse::SaplingSubtree(BTreeMap<_, NoteCommitmentSubtreeData<_>>))`](crate::ReadResponse::SaplingSubtrees)
1490    /// * An empty list if there is no subtree at `start_index`.
1491    SaplingSubtrees {
1492        /// The index of the first 2^16-leaf subtree to return.
1493        start_index: NoteCommitmentSubtreeIndex,
1494        /// The maximum number of subtree values to return.
1495        limit: Option<NoteCommitmentSubtreeIndex>,
1496    },
1497
1498    /// Returns a list of Orchard note commitment subtrees by their indexes, starting at
1499    /// `start_index`, and returning up to `limit` subtrees.
1500    ///
1501    /// Returns
1502    ///
1503    /// * [`ReadResponse::OrchardSubtree(BTreeMap<_, NoteCommitmentSubtreeData<_>>))`](crate::ReadResponse::OrchardSubtrees)
1504    /// * An empty list if there is no subtree at `start_index`.
1505    OrchardSubtrees {
1506        /// The index of the first 2^16-leaf subtree to return.
1507        start_index: NoteCommitmentSubtreeIndex,
1508        /// The maximum number of subtree values to return.
1509        limit: Option<NoteCommitmentSubtreeIndex>,
1510    },
1511
1512    /// Returns a list of Ironwood note commitment subtrees by their indexes, starting at
1513    /// `start_index`, and returning up to `limit` subtrees.
1514    ///
1515    /// Returns
1516    ///
1517    /// * [`ReadResponse::IronwoodSubtree(BTreeMap<_, NoteCommitmentSubtreeData<_>>))`](crate::ReadResponse::IronwoodSubtrees)
1518    /// * An empty list if there is no subtree at `start_index`.
1519    IronwoodSubtrees {
1520        /// The index of the first 2^16-leaf subtree to return.
1521        start_index: NoteCommitmentSubtreeIndex,
1522        /// The maximum number of subtree values to return.
1523        limit: Option<NoteCommitmentSubtreeIndex>,
1524    },
1525
1526    /// Looks up the balance of a set of transparent addresses.
1527    ///
1528    /// Returns an [`Amount`] with the total
1529    /// balance of the set of addresses.
1530    AddressBalance(HashSet<transparent::Address>),
1531
1532    /// Looks up transaction hashes that were sent or received from addresses,
1533    /// in an inclusive blockchain height range, returning at most `max_entries` of them.
1534    ///
1535    /// Returns
1536    ///
1537    /// * An ordered, unique map of transaction locations and hashes.
1538    /// * An empty map if no transactions were found for the given arguments.
1539    ///
1540    /// Returned txids are in the order they appear in blocks,
1541    /// which ensures that they are topologically sorted
1542    /// (i.e. parent txids will appear before child txids).
1543    TransactionIdsByAddresses {
1544        /// The requested addresses.
1545        addresses: HashSet<transparent::Address>,
1546
1547        /// The blocks to be queried for transactions.
1548        height_range: RangeInclusive<block::Height>,
1549
1550        /// The maximum number of transaction IDs to return, the first ones in chain order,
1551        /// or `None` for no limit. The limit bounds the finalized index scan, not just the response.
1552        max_entries: Option<usize>,
1553    },
1554
1555    /// Looks up a spending transaction id by its spent transparent input.
1556    ///
1557    /// Returns [`ReadResponse::TransactionId`] with the hash of the transaction
1558    /// that spent the output at the provided [`transparent::OutPoint`].
1559    #[cfg(feature = "indexer")]
1560    SpendingTransactionId(Spend),
1561
1562    /// Looks up utxos for the provided addresses, in the provided height range,
1563    /// returning at most `max_entries` of them.
1564    ///
1565    /// Returns a type with found utxos and transaction information.
1566    UtxosByAddresses {
1567        /// The addresses to look up utxos for.
1568        addresses: HashSet<transparent::Address>,
1569
1570        /// The blocks to be queried for utxos.
1571        height_range: RangeInclusive<block::Height>,
1572
1573        /// The maximum number of utxos to return, or `None` for no limit.
1574        ///
1575        /// The limit bounds the finalized index scan, not just the response.
1576        max_entries: Option<usize>,
1577    },
1578
1579    /// Contextually validates anchors and nullifiers of a transaction on the best chain
1580    ///
1581    /// Returns [`ReadResponse::ValidBestChainTipNullifiersAndAnchors`].
1582    CheckBestChainTipNullifiersAndAnchors(UnminedTx),
1583
1584    /// Calculates the median-time-past for the *next* block on the best chain.
1585    ///
1586    /// Returns [`ReadResponse::BestChainNextMedianTimePast`] when successful.
1587    BestChainNextMedianTimePast,
1588
1589    /// Looks up a block hash by height in the current best chain.
1590    ///
1591    /// Returns
1592    ///
1593    /// * [`ReadResponse::BlockHash(Some(hash))`](ReadResponse::BlockHash) if the block is in the best chain;
1594    /// * [`ReadResponse::BlockHash(None)`](ReadResponse::BlockHash) otherwise.
1595    BestChainBlockHash(block::Height),
1596
1597    /// Get state information from the best block chain.
1598    ///
1599    /// Returns [`ReadResponse::ChainInfo(info)`](ReadResponse::ChainInfo) where `info` is a
1600    /// [`zebra-state::GetBlockTemplateChainInfo`](zebra-state::GetBlockTemplateChainInfo)` structure containing
1601    /// best chain state information.
1602    ChainInfo,
1603
1604    /// Get the average solution rate in the best chain.
1605    ///
1606    /// Returns [`ReadResponse::SolutionRate`]
1607    SolutionRate {
1608        /// The number of blocks to calculate the average difficulty for, or `None`
1609        /// to use the averaging window at the effective (tip-clamped) height.
1610        num_blocks: Option<usize>,
1611        /// Optionally estimate the network solution rate at the time when this height was mined.
1612        /// Otherwise, estimate at the current tip height.
1613        height: Option<block::Height>,
1614    },
1615
1616    /// Performs contextual validation of the given block, but does not commit it to the state.
1617    ///
1618    /// It is the caller's responsibility to perform semantic validation.
1619    /// (The caller does not need to check proof of work for block proposals.)
1620    ///
1621    /// Returns [`ReadResponse::ValidBlockProposal`] when successful, or an error if
1622    /// the block fails contextual validation.
1623    CheckBlockProposalValidity(SemanticallyVerifiedBlock),
1624
1625    /// Returns [`ReadResponse::TipBlockSize(usize)`](ReadResponse::TipBlockSize)
1626    /// with the current best chain tip block size in bytes.
1627    TipBlockSize,
1628
1629    /// Returns [`ReadResponse::NonFinalizedBlocksListener`] with a channel receiver
1630    /// allowing the caller to listen for new blocks in the non-finalized state.
1631    NonFinalizedBlocksListener {
1632        /// The hashes of the chain tips the caller already has.
1633        ///
1634        /// Only blocks that come after these tips are streamed: walking each
1635        /// non-finalized chain from its tip downwards, blocks are sent until a
1636        /// hash in this set is reached. If empty, every block currently in the
1637        /// non-finalized state is sent.
1638        known_chain_tips: HashSet<block::Hash>,
1639    },
1640
1641    /// Returns `true` if the transparent output is spent in the best chain,
1642    /// or `false` if it is unspent.
1643    IsTransparentOutputSpent(transparent::OutPoint),
1644}
1645
1646impl ReadRequest {
1647    /// Returns a [`&'static str`](str) name of the variant representing this value.
1648    pub fn variant_name(&self) -> &'static str {
1649        match self {
1650            ReadRequest::UsageInfo => "usage_info",
1651            ReadRequest::Tip => "tip",
1652            ReadRequest::TipPoolValues => "tip_pool_values",
1653            ReadRequest::BlockInfo(_) => "block_info",
1654            ReadRequest::Depth(_) => "depth",
1655            ReadRequest::Block(_) => "block",
1656            ReadRequest::AnyChainBlock(_) => "any_chain_block",
1657            ReadRequest::BlockAndSize(_) => "block_and_size",
1658            ReadRequest::SpentOutputs(_) => "spent_outputs",
1659            ReadRequest::BlockHeader(_) => "block_header",
1660            ReadRequest::Transaction(_) => "transaction",
1661            ReadRequest::AnyChainTransaction(_) => "any_chain_transaction",
1662            ReadRequest::TransactionIdsForBlock(_) => "transaction_ids_for_block",
1663            ReadRequest::AnyChainTransactionIdsForBlock(_) => "any_chain_transaction_ids_for_block",
1664            ReadRequest::UnspentBestChainUtxo { .. } => "unspent_best_chain_utxo",
1665            ReadRequest::AnyChainUtxo { .. } => "any_chain_utxo",
1666            ReadRequest::BlockLocator => "block_locator",
1667            ReadRequest::FindBlockHashes { .. } => "find_block_hashes",
1668            ReadRequest::FindBlockHeaders { .. } => "find_block_headers",
1669            ReadRequest::FindForkPoint { .. } => "find_fork_point",
1670            ReadRequest::SaplingTree { .. } => "sapling_tree",
1671            ReadRequest::OrchardTree { .. } => "orchard_tree",
1672            ReadRequest::AnyChainSaplingTree { .. } => "any_chain_sapling_tree",
1673            ReadRequest::AnyChainOrchardTree { .. } => "any_chain_orchard_tree",
1674            ReadRequest::IronwoodTree { .. } => "ironwood_tree",
1675            ReadRequest::AnyChainIronwoodTree { .. } => "any_chain_ironwood_tree",
1676            ReadRequest::SaplingSubtrees { .. } => "sapling_subtrees",
1677            ReadRequest::OrchardSubtrees { .. } => "orchard_subtrees",
1678            ReadRequest::IronwoodSubtrees { .. } => "ironwood_subtrees",
1679            ReadRequest::AddressBalance { .. } => "address_balance",
1680            ReadRequest::TransactionIdsByAddresses { .. } => "transaction_ids_by_addresses",
1681            ReadRequest::UtxosByAddresses { .. } => "utxos_by_addresses",
1682            ReadRequest::CheckBestChainTipNullifiersAndAnchors(_) => {
1683                "best_chain_tip_nullifiers_anchors"
1684            }
1685            ReadRequest::BestChainNextMedianTimePast => "best_chain_next_median_time_past",
1686            ReadRequest::BestChainBlockHash(_) => "best_chain_block_hash",
1687            #[cfg(feature = "indexer")]
1688            ReadRequest::SpendingTransactionId(_) => "spending_transaction_id",
1689            ReadRequest::ChainInfo => "chain_info",
1690            ReadRequest::SolutionRate { .. } => "solution_rate",
1691            ReadRequest::CheckBlockProposalValidity(_) => "check_block_proposal_validity",
1692            ReadRequest::TipBlockSize => "tip_block_size",
1693            ReadRequest::NonFinalizedBlocksListener { .. } => "non_finalized_blocks_listener",
1694            ReadRequest::IsTransparentOutputSpent(_) => "is_transparent_output_spent",
1695        }
1696    }
1697
1698    /// Counts metric for ReadStateService call
1699    pub fn count_metric(&self) {
1700        metrics::counter!(
1701            "state.requests",
1702            "service" => "read_state",
1703            "type" => self.variant_name()
1704        )
1705        .increment(1);
1706    }
1707}
1708
1709/// Conversion from read-write [`Request`]s to read-only [`ReadRequest`]s.
1710///
1711/// Used to dispatch read requests concurrently from the [`StateService`](crate::service::StateService).
1712impl TryFrom<Request> for ReadRequest {
1713    type Error = &'static str;
1714
1715    fn try_from(request: Request) -> Result<ReadRequest, Self::Error> {
1716        match request {
1717            Request::Tip => Ok(ReadRequest::Tip),
1718            Request::Depth(hash) => Ok(ReadRequest::Depth(hash)),
1719            Request::BestChainNextMedianTimePast => Ok(ReadRequest::BestChainNextMedianTimePast),
1720            Request::BestChainBlockHash(hash) => Ok(ReadRequest::BestChainBlockHash(hash)),
1721
1722            Request::Block(hash_or_height) => Ok(ReadRequest::Block(hash_or_height)),
1723            Request::AnyChainBlock(hash_or_height) => {
1724                Ok(ReadRequest::AnyChainBlock(hash_or_height))
1725            }
1726            Request::BlockAndSize(hash_or_height) => Ok(ReadRequest::BlockAndSize(hash_or_height)),
1727            Request::BlockHeader(hash_or_height) => Ok(ReadRequest::BlockHeader(hash_or_height)),
1728            Request::Transaction(tx_hash) => Ok(ReadRequest::Transaction(tx_hash)),
1729            Request::AnyChainTransaction(tx_hash) => Ok(ReadRequest::AnyChainTransaction(tx_hash)),
1730            Request::UnspentBestChainUtxo(outpoint) => {
1731                Ok(ReadRequest::UnspentBestChainUtxo(outpoint))
1732            }
1733
1734            Request::BlockLocator => Ok(ReadRequest::BlockLocator),
1735            Request::FindBlockHashes { known_blocks, stop } => {
1736                Ok(ReadRequest::FindBlockHashes { known_blocks, stop })
1737            }
1738            Request::FindBlockHeaders { known_blocks, stop } => {
1739                Ok(ReadRequest::FindBlockHeaders { known_blocks, stop })
1740            }
1741
1742            Request::CheckBestChainTipNullifiersAndAnchors(tx) => {
1743                Ok(ReadRequest::CheckBestChainTipNullifiersAndAnchors(tx))
1744            }
1745
1746            Request::CommitSemanticallyVerifiedBlock(_)
1747            | Request::CommitCheckpointVerifiedBlock(_)
1748            | Request::InvalidateBlock(_)
1749            | Request::ReconsiderBlock(_) => Err("ReadService does not write blocks"),
1750
1751            Request::AwaitUtxo(_) => Err("ReadService does not track pending UTXOs. \
1752                     Manually convert the request to ReadRequest::AnyChainUtxo, \
1753                     and handle pending UTXOs"),
1754
1755            Request::KnownBlock(_) => Err("ReadService does not track queued blocks"),
1756
1757            Request::CheckBlockProposalValidity(semantically_verified) => Ok(
1758                ReadRequest::CheckBlockProposalValidity(semantically_verified),
1759            ),
1760        }
1761    }
1762}
1763
1764#[derive(Debug)]
1765/// A convenience type for spawning blocking tokio tasks with
1766/// a timer in the scope of a provided span.
1767pub struct TimedSpan {
1768    timer: CodeTimer,
1769    span: tracing::Span,
1770}
1771
1772impl TimedSpan {
1773    /// Creates a new [`TimedSpan`].
1774    pub fn new(timer: CodeTimer, span: tracing::Span) -> Self {
1775        Self { timer, span }
1776    }
1777
1778    /// Spawns a blocking tokio task in scope of the `span` field.
1779    #[track_caller]
1780    pub fn spawn_blocking<T: Send + 'static>(
1781        mut self,
1782        f: impl FnOnce() -> Result<T, BoxError> + Send + 'static,
1783    ) -> Pin<Box<dyn futures::Future<Output = Result<T, BoxError>> + Send>> {
1784        let location = std::panic::Location::caller();
1785        // # Performance
1786        //
1787        // Allow other async tasks to make progress while concurrently reading blocks from disk.
1788
1789        // The work is done in the future.
1790        tokio::task::spawn_blocking(move || {
1791            self.span.in_scope(move || {
1792                let result = f();
1793                self.timer
1794                    .finish_inner(Some(location.file()), Some(location.line()), "");
1795                result
1796            })
1797        })
1798        .wait_for_panics()
1799    }
1800}