zebra_state/
request.rs

1//! State [`tower::Service`] request types.
2
3use std::{
4    collections::{HashMap, HashSet},
5    ops::{Add, Deref, DerefMut, RangeInclusive},
6    sync::Arc,
7};
8
9use zebra_chain::{
10    amount::{DeferredPoolBalanceChange, NegativeAllowed},
11    block::{self, Block, HeightDiff},
12    history_tree::HistoryTree,
13    orchard,
14    parallel::tree::NoteCommitmentTrees,
15    sapling,
16    serialization::SerializationError,
17    sprout,
18    subtree::{NoteCommitmentSubtree, NoteCommitmentSubtreeIndex},
19    transaction::{self, UnminedTx},
20    transparent::{self, utxos_from_ordered_utxos},
21    value_balance::{ValueBalance, ValueBalanceError},
22};
23
24/// Allow *only* these unused imports, so that rustdoc link resolution
25/// will work with inline links.
26#[allow(unused_imports)]
27use crate::{
28    constants::{MAX_FIND_BLOCK_HASHES_RESULTS, MAX_FIND_BLOCK_HEADERS_RESULTS},
29    ReadResponse, Response,
30};
31
32/// Identify a spend by a transparent outpoint or revealed nullifier.
33///
34/// This enum implements `From` for [`transparent::OutPoint`], [`sprout::Nullifier`],
35/// [`sapling::Nullifier`], and [`orchard::Nullifier`].
36#[derive(Copy, Clone, Debug, PartialEq, Eq)]
37#[cfg(feature = "indexer")]
38pub enum Spend {
39    /// A spend identified by a [`transparent::OutPoint`].
40    OutPoint(transparent::OutPoint),
41    /// A spend identified by a [`sprout::Nullifier`].
42    Sprout(sprout::Nullifier),
43    /// A spend identified by a [`sapling::Nullifier`].
44    Sapling(sapling::Nullifier),
45    /// A spend identified by a [`orchard::Nullifier`].
46    Orchard(orchard::Nullifier),
47}
48
49#[cfg(feature = "indexer")]
50impl From<transparent::OutPoint> for Spend {
51    fn from(outpoint: transparent::OutPoint) -> Self {
52        Self::OutPoint(outpoint)
53    }
54}
55
56#[cfg(feature = "indexer")]
57impl From<sprout::Nullifier> for Spend {
58    fn from(sprout_nullifier: sprout::Nullifier) -> Self {
59        Self::Sprout(sprout_nullifier)
60    }
61}
62
63#[cfg(feature = "indexer")]
64impl From<sapling::Nullifier> for Spend {
65    fn from(sapling_nullifier: sapling::Nullifier) -> Self {
66        Self::Sapling(sapling_nullifier)
67    }
68}
69
70#[cfg(feature = "indexer")]
71impl From<orchard::Nullifier> for Spend {
72    fn from(orchard_nullifier: orchard::Nullifier) -> Self {
73        Self::Orchard(orchard_nullifier)
74    }
75}
76
77/// Identify a block by hash or height.
78///
79/// This enum implements `From` for [`block::Hash`] and [`block::Height`],
80/// so it can be created using `hash.into()` or `height.into()`.
81#[derive(Copy, Clone, Debug, PartialEq, Eq)]
82pub enum HashOrHeight {
83    /// A block identified by hash.
84    Hash(block::Hash),
85    /// A block identified by height.
86    Height(block::Height),
87}
88
89impl HashOrHeight {
90    /// Unwrap the inner height or attempt to retrieve the height for a given
91    /// hash if one exists.
92    pub fn height_or_else<F>(self, op: F) -> Option<block::Height>
93    where
94        F: FnOnce(block::Hash) -> Option<block::Height>,
95    {
96        match self {
97            HashOrHeight::Hash(hash) => op(hash),
98            HashOrHeight::Height(height) => Some(height),
99        }
100    }
101
102    /// Unwrap the inner hash or attempt to retrieve the hash for a given
103    /// height if one exists.
104    ///
105    /// # Consensus
106    ///
107    /// In the non-finalized state, a height can have multiple valid hashes.
108    /// We typically use the hash that is currently on the best chain.
109    pub fn hash_or_else<F>(self, op: F) -> Option<block::Hash>
110    where
111        F: FnOnce(block::Height) -> Option<block::Hash>,
112    {
113        match self {
114            HashOrHeight::Hash(hash) => Some(hash),
115            HashOrHeight::Height(height) => op(height),
116        }
117    }
118
119    /// Returns the hash if this is a [`HashOrHeight::Hash`].
120    pub fn hash(&self) -> Option<block::Hash> {
121        if let HashOrHeight::Hash(hash) = self {
122            Some(*hash)
123        } else {
124            None
125        }
126    }
127
128    /// Returns the height if this is a [`HashOrHeight::Height`].
129    pub fn height(&self) -> Option<block::Height> {
130        if let HashOrHeight::Height(height) = self {
131            Some(*height)
132        } else {
133            None
134        }
135    }
136
137    /// Constructs a new [`HashOrHeight`] from a string containing a hash or a positive or negative
138    /// height.
139    ///
140    /// When the provided `hash_or_height` contains a negative height, the `tip_height` parameter
141    /// needs to be `Some` since height `-1` points to the tip.
142    pub fn new(hash_or_height: &str, tip_height: Option<block::Height>) -> Result<Self, String> {
143        hash_or_height
144            .parse()
145            .map(Self::Hash)
146            .or_else(|_| hash_or_height.parse().map(Self::Height))
147            .or_else(|_| {
148                hash_or_height
149                    .parse()
150                    .map_err(|_| "could not parse negative height")
151                    .and_then(|d: HeightDiff| {
152                        if d.is_negative() {
153                            {
154                                Ok(HashOrHeight::Height(
155                                    tip_height
156                                        .ok_or("missing tip height")?
157                                        .add(d)
158                                        .ok_or("underflow when adding negative height to tip")?
159                                        .next()
160                                        .map_err(|_| "height -1 needs to point to tip")?,
161                                ))
162                            }
163                        } else {
164                            Err("height was not negative")
165                        }
166                    })
167            })
168            .map_err(|_| {
169                "parse error: could not convert the input string to a hash or height".to_string()
170            })
171    }
172}
173
174impl std::fmt::Display for HashOrHeight {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        match self {
177            HashOrHeight::Hash(hash) => write!(f, "{hash}"),
178            HashOrHeight::Height(height) => write!(f, "{}", height.0),
179        }
180    }
181}
182
183impl From<block::Hash> for HashOrHeight {
184    fn from(hash: block::Hash) -> Self {
185        Self::Hash(hash)
186    }
187}
188
189impl From<block::Height> for HashOrHeight {
190    fn from(height: block::Height) -> Self {
191        Self::Height(height)
192    }
193}
194
195impl From<(block::Height, block::Hash)> for HashOrHeight {
196    fn from((_height, hash): (block::Height, block::Hash)) -> Self {
197        // Hash is more specific than height for the non-finalized state
198        hash.into()
199    }
200}
201
202impl From<(block::Hash, block::Height)> for HashOrHeight {
203    fn from((hash, _height): (block::Hash, block::Height)) -> Self {
204        hash.into()
205    }
206}
207
208impl std::str::FromStr for HashOrHeight {
209    type Err = SerializationError;
210
211    fn from_str(s: &str) -> Result<Self, Self::Err> {
212        s.parse()
213            .map(Self::Hash)
214            .or_else(|_| s.parse().map(Self::Height))
215            .map_err(|_| {
216                SerializationError::Parse("could not convert the input string to a hash or height")
217            })
218    }
219}
220
221/// A block which has undergone semantic validation and has been prepared for
222/// contextual validation.
223///
224/// It is the constructor's responsibility to perform semantic validation and to
225/// ensure that all fields are consistent.
226///
227/// This structure contains data from contextual validation, which is computed in
228/// the *service caller*'s task, not inside the service call itself. This allows
229/// moving work out of the single-threaded state service.
230#[derive(Clone, Debug, PartialEq, Eq)]
231pub struct SemanticallyVerifiedBlock {
232    /// The block to commit to the state.
233    pub block: Arc<Block>,
234    /// The hash of the block.
235    pub hash: block::Hash,
236    /// The height of the block.
237    pub height: block::Height,
238    /// New transparent outputs created in this block, indexed by
239    /// [`OutPoint`](transparent::OutPoint).
240    ///
241    /// Each output is tagged with its transaction index in the block.
242    /// (The outputs of earlier transactions in a block can be spent by later
243    /// transactions.)
244    ///
245    /// Note: although these transparent outputs are newly created, they may not
246    /// be unspent, since a later transaction in a block can spend outputs of an
247    /// earlier transaction.
248    ///
249    /// This field can also contain unrelated outputs, which are ignored.
250    pub new_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
251    /// A precomputed list of the hashes of the transactions in this block,
252    /// in the same order as `block.transactions`.
253    pub transaction_hashes: Arc<[transaction::Hash]>,
254    /// This block's deferred pool value balance change.
255    pub deferred_pool_balance_change: Option<DeferredPoolBalanceChange>,
256}
257
258/// A block ready to be committed directly to the finalized state with
259/// a small number of checks if compared with a `ContextuallyVerifiedBlock`.
260///
261/// This is exposed for use in checkpointing.
262///
263/// Note: The difference between a `CheckpointVerifiedBlock` and a `ContextuallyVerifiedBlock` is
264/// that the `CheckpointVerifier` doesn't bind the transaction authorizing data to the
265/// `ChainHistoryBlockTxAuthCommitmentHash`, but the `NonFinalizedState` and `FinalizedState` do.
266#[derive(Clone, Debug, PartialEq, Eq)]
267pub struct CheckpointVerifiedBlock(pub(crate) SemanticallyVerifiedBlock);
268
269// Some fields are pub(crate), so we can add whatever db-format-dependent
270// precomputation we want here without leaking internal details.
271
272/// A contextually verified block, ready to be committed directly to the finalized state with no
273/// checks, if it becomes the root of the best non-finalized chain.
274///
275/// Used by the state service and non-finalized `Chain`.
276///
277/// Note: The difference between a `CheckpointVerifiedBlock` and a `ContextuallyVerifiedBlock` is
278/// that the `CheckpointVerifier` doesn't bind the transaction authorizing data to the
279/// `ChainHistoryBlockTxAuthCommitmentHash`, but the `NonFinalizedState` and `FinalizedState` do.
280#[derive(Clone, Debug, PartialEq, Eq)]
281pub struct ContextuallyVerifiedBlock {
282    /// The block to commit to the state.
283    pub(crate) block: Arc<Block>,
284
285    /// The hash of the block.
286    pub(crate) hash: block::Hash,
287
288    /// The height of the block.
289    pub(crate) height: block::Height,
290
291    /// New transparent outputs created in this block, indexed by
292    /// [`OutPoint`](transparent::OutPoint).
293    ///
294    /// Note: although these transparent outputs are newly created, they may not
295    /// be unspent, since a later transaction in a block can spend outputs of an
296    /// earlier transaction.
297    ///
298    /// This field can also contain unrelated outputs, which are ignored.
299    pub(crate) new_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
300
301    /// The outputs spent by this block, indexed by the [`transparent::Input`]'s
302    /// [`OutPoint`](transparent::OutPoint).
303    ///
304    /// Note: these inputs can come from earlier transactions in this block,
305    /// or earlier blocks in the chain.
306    ///
307    /// This field can also contain unrelated outputs, which are ignored.
308    pub(crate) spent_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
309
310    /// A precomputed list of the hashes of the transactions in this block,
311    /// in the same order as `block.transactions`.
312    pub(crate) transaction_hashes: Arc<[transaction::Hash]>,
313
314    /// The sum of the chain value pool changes of all transactions in this block.
315    pub(crate) chain_value_pool_change: ValueBalance<NegativeAllowed>,
316}
317
318/// Wraps note commitment trees and the history tree together.
319///
320/// The default instance represents the treestate that corresponds to the genesis block.
321#[derive(Clone, Debug, Default, Eq, PartialEq)]
322pub struct Treestate {
323    /// Note commitment trees.
324    pub note_commitment_trees: NoteCommitmentTrees,
325    /// History tree.
326    pub history_tree: Arc<HistoryTree>,
327}
328
329impl Treestate {
330    pub fn new(
331        sprout: Arc<sprout::tree::NoteCommitmentTree>,
332        sapling: Arc<sapling::tree::NoteCommitmentTree>,
333        orchard: Arc<orchard::tree::NoteCommitmentTree>,
334        sapling_subtree: Option<NoteCommitmentSubtree<sapling::tree::Node>>,
335        orchard_subtree: Option<NoteCommitmentSubtree<orchard::tree::Node>>,
336        history_tree: Arc<HistoryTree>,
337    ) -> Self {
338        Self {
339            note_commitment_trees: NoteCommitmentTrees {
340                sprout,
341                sapling,
342                sapling_subtree,
343                orchard,
344                orchard_subtree,
345            },
346            history_tree,
347        }
348    }
349}
350
351/// Contains a block ready to be committed.
352///
353/// Zebra's state service passes this `enum` over to the finalized state
354/// when committing a block.
355pub enum FinalizableBlock {
356    Checkpoint {
357        checkpoint_verified: CheckpointVerifiedBlock,
358    },
359    Contextual {
360        contextually_verified: ContextuallyVerifiedBlock,
361        treestate: Treestate,
362    },
363}
364
365/// Contains a block with all its associated data that the finalized state can commit to its
366/// database.
367///
368/// Note that it's the constructor's responsibility to ensure that all data is valid and verified.
369pub struct FinalizedBlock {
370    /// The block to commit to the state.
371    pub(super) block: Arc<Block>,
372    /// The hash of the block.
373    pub(super) hash: block::Hash,
374    /// The height of the block.
375    pub(super) height: block::Height,
376    /// New transparent outputs created in this block, indexed by
377    /// [`OutPoint`](transparent::OutPoint).
378    pub(super) new_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
379    /// A precomputed list of the hashes of the transactions in this block, in the same order as
380    /// `block.transactions`.
381    pub(super) transaction_hashes: Arc<[transaction::Hash]>,
382    /// The tresstate associated with the block.
383    pub(super) treestate: Treestate,
384    /// This block's deferred pool value balance change.
385    pub(super) deferred_pool_balance_change: Option<DeferredPoolBalanceChange>,
386}
387
388impl FinalizedBlock {
389    /// Constructs [`FinalizedBlock`] from [`CheckpointVerifiedBlock`] and its [`Treestate`].
390    pub fn from_checkpoint_verified(block: CheckpointVerifiedBlock, treestate: Treestate) -> Self {
391        Self::from_semantically_verified(SemanticallyVerifiedBlock::from(block), treestate)
392    }
393
394    /// Constructs [`FinalizedBlock`] from [`ContextuallyVerifiedBlock`] and its [`Treestate`].
395    pub fn from_contextually_verified(
396        block: ContextuallyVerifiedBlock,
397        treestate: Treestate,
398    ) -> Self {
399        Self::from_semantically_verified(SemanticallyVerifiedBlock::from(block), treestate)
400    }
401
402    /// Constructs [`FinalizedBlock`] from [`SemanticallyVerifiedBlock`] and its [`Treestate`].
403    fn from_semantically_verified(block: SemanticallyVerifiedBlock, treestate: Treestate) -> Self {
404        Self {
405            block: block.block,
406            hash: block.hash,
407            height: block.height,
408            new_outputs: block.new_outputs,
409            transaction_hashes: block.transaction_hashes,
410            treestate,
411            deferred_pool_balance_change: block.deferred_pool_balance_change,
412        }
413    }
414}
415
416impl FinalizableBlock {
417    /// Create a new [`FinalizableBlock`] given a [`ContextuallyVerifiedBlock`].
418    pub fn new(contextually_verified: ContextuallyVerifiedBlock, treestate: Treestate) -> Self {
419        Self::Contextual {
420            contextually_verified,
421            treestate,
422        }
423    }
424
425    #[cfg(test)]
426    /// Extract a [`Block`] from a [`FinalizableBlock`] variant.
427    pub fn inner_block(&self) -> Arc<Block> {
428        match self {
429            FinalizableBlock::Checkpoint {
430                checkpoint_verified,
431            } => checkpoint_verified.block.clone(),
432            FinalizableBlock::Contextual {
433                contextually_verified,
434                ..
435            } => contextually_verified.block.clone(),
436        }
437    }
438}
439
440impl From<CheckpointVerifiedBlock> for FinalizableBlock {
441    fn from(checkpoint_verified: CheckpointVerifiedBlock) -> Self {
442        Self::Checkpoint {
443            checkpoint_verified,
444        }
445    }
446}
447
448impl From<Arc<Block>> for FinalizableBlock {
449    fn from(block: Arc<Block>) -> Self {
450        Self::from(CheckpointVerifiedBlock::from(block))
451    }
452}
453
454impl From<&SemanticallyVerifiedBlock> for SemanticallyVerifiedBlock {
455    fn from(semantically_verified: &SemanticallyVerifiedBlock) -> Self {
456        semantically_verified.clone()
457    }
458}
459
460// Doing precomputation in these impls means that it will be done in
461// the *service caller*'s task, not inside the service call itself.
462// This allows moving work out of the single-threaded state service.
463
464impl ContextuallyVerifiedBlock {
465    /// Create a block that's ready for non-finalized `Chain` contextual validation,
466    /// using a [`SemanticallyVerifiedBlock`] and the UTXOs it spends.
467    ///
468    /// When combined, `semantically_verified.new_outputs` and `spent_utxos` must contain
469    /// the [`Utxo`](transparent::Utxo)s spent by every transparent input in this block,
470    /// including UTXOs created by earlier transactions in this block.
471    ///
472    /// Note: a [`ContextuallyVerifiedBlock`] isn't actually contextually valid until
473    /// [`Chain::push()`](crate::service::non_finalized_state::Chain::push) returns success.
474    pub fn with_block_and_spent_utxos(
475        semantically_verified: SemanticallyVerifiedBlock,
476        mut spent_outputs: HashMap<transparent::OutPoint, transparent::OrderedUtxo>,
477    ) -> Result<Self, ValueBalanceError> {
478        let SemanticallyVerifiedBlock {
479            block,
480            hash,
481            height,
482            new_outputs,
483            transaction_hashes,
484            deferred_pool_balance_change,
485        } = semantically_verified;
486
487        // This is redundant for the non-finalized state,
488        // but useful to make some tests pass more easily.
489        //
490        // TODO: fix the tests, and stop adding unrelated outputs.
491        spent_outputs.extend(new_outputs.clone());
492
493        Ok(Self {
494            block: block.clone(),
495            hash,
496            height,
497            new_outputs,
498            spent_outputs: spent_outputs.clone(),
499            transaction_hashes,
500            chain_value_pool_change: block.chain_value_pool_change(
501                &utxos_from_ordered_utxos(spent_outputs),
502                deferred_pool_balance_change,
503            )?,
504        })
505    }
506}
507
508impl CheckpointVerifiedBlock {
509    /// Creates a [`CheckpointVerifiedBlock`] from [`Block`] with optional deferred balance and
510    /// optional pre-computed hash.
511    pub fn new(
512        block: Arc<Block>,
513        hash: Option<block::Hash>,
514        deferred_pool_balance_change: Option<DeferredPoolBalanceChange>,
515    ) -> Self {
516        let mut block = Self::with_hash(block.clone(), hash.unwrap_or(block.hash()));
517        block.deferred_pool_balance_change = deferred_pool_balance_change;
518        block
519    }
520    /// Creates a block that's ready to be committed to the finalized state,
521    /// using a precalculated [`block::Hash`].
522    ///
523    /// Note: a [`CheckpointVerifiedBlock`] isn't actually finalized
524    /// until [`Request::CommitCheckpointVerifiedBlock`] returns success.
525    pub fn with_hash(block: Arc<Block>, hash: block::Hash) -> Self {
526        Self(SemanticallyVerifiedBlock::with_hash(block, hash))
527    }
528}
529
530impl SemanticallyVerifiedBlock {
531    /// Creates [`SemanticallyVerifiedBlock`] from [`Block`] and [`block::Hash`].
532    pub fn with_hash(block: Arc<Block>, hash: block::Hash) -> Self {
533        let height = block
534            .coinbase_height()
535            .expect("semantically verified block should have a coinbase height");
536        let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
537        let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
538
539        Self {
540            block,
541            hash,
542            height,
543            new_outputs,
544            transaction_hashes,
545            deferred_pool_balance_change: None,
546        }
547    }
548
549    /// Sets the deferred balance in the block.
550    pub fn with_deferred_pool_balance_change(
551        mut self,
552        deferred_pool_balance_change: Option<DeferredPoolBalanceChange>,
553    ) -> Self {
554        self.deferred_pool_balance_change = deferred_pool_balance_change;
555        self
556    }
557}
558
559impl From<Arc<Block>> for CheckpointVerifiedBlock {
560    fn from(block: Arc<Block>) -> Self {
561        CheckpointVerifiedBlock(SemanticallyVerifiedBlock::from(block))
562    }
563}
564
565impl From<Arc<Block>> for SemanticallyVerifiedBlock {
566    fn from(block: Arc<Block>) -> Self {
567        let hash = block.hash();
568        let height = block
569            .coinbase_height()
570            .expect("semantically verified block should have a coinbase height");
571        let transaction_hashes: Arc<[_]> = block.transactions.iter().map(|tx| tx.hash()).collect();
572        let new_outputs = transparent::new_ordered_outputs(&block, &transaction_hashes);
573
574        Self {
575            block,
576            hash,
577            height,
578            new_outputs,
579            transaction_hashes,
580            deferred_pool_balance_change: None,
581        }
582    }
583}
584
585impl From<ContextuallyVerifiedBlock> for SemanticallyVerifiedBlock {
586    fn from(valid: ContextuallyVerifiedBlock) -> Self {
587        Self {
588            block: valid.block,
589            hash: valid.hash,
590            height: valid.height,
591            new_outputs: valid.new_outputs,
592            transaction_hashes: valid.transaction_hashes,
593            deferred_pool_balance_change: Some(DeferredPoolBalanceChange::new(
594                valid.chain_value_pool_change.deferred_amount(),
595            )),
596        }
597    }
598}
599
600impl From<FinalizedBlock> for SemanticallyVerifiedBlock {
601    fn from(finalized: FinalizedBlock) -> Self {
602        Self {
603            block: finalized.block,
604            hash: finalized.hash,
605            height: finalized.height,
606            new_outputs: finalized.new_outputs,
607            transaction_hashes: finalized.transaction_hashes,
608            deferred_pool_balance_change: finalized.deferred_pool_balance_change,
609        }
610    }
611}
612
613impl From<CheckpointVerifiedBlock> for SemanticallyVerifiedBlock {
614    fn from(checkpoint_verified: CheckpointVerifiedBlock) -> Self {
615        checkpoint_verified.0
616    }
617}
618
619impl Deref for CheckpointVerifiedBlock {
620    type Target = SemanticallyVerifiedBlock;
621
622    fn deref(&self) -> &Self::Target {
623        &self.0
624    }
625}
626impl DerefMut for CheckpointVerifiedBlock {
627    fn deref_mut(&mut self) -> &mut Self::Target {
628        &mut self.0
629    }
630}
631
632#[derive(Clone, Debug, PartialEq, Eq)]
633/// A query about or modification to the chain state, via the
634/// [`StateService`](crate::service::StateService).
635pub enum Request {
636    /// Performs contextual validation of the given semantically verified block,
637    /// committing it to the state if successful.
638    ///
639    /// This request can be made out-of-order; the state service will queue it
640    /// until its parent is ready.
641    ///
642    /// Returns [`Response::Committed`] with the hash of the block when it is
643    /// committed to the state, or an error if the block fails contextual
644    /// validation or has already been committed to the state.
645    ///
646    /// This request cannot be cancelled once submitted; dropping the response
647    /// future will have no effect on whether it is eventually processed. A
648    /// request to commit a block which has been queued internally but not yet
649    /// committed will fail the older request and replace it with the newer request.
650    ///
651    /// # Correctness
652    ///
653    /// Block commit requests should be wrapped in a timeout, so that
654    /// out-of-order and invalid requests do not hang indefinitely. See the [`crate`]
655    /// documentation for details.
656    CommitSemanticallyVerifiedBlock(SemanticallyVerifiedBlock),
657
658    /// Commit a checkpointed block to the state, skipping most but not all
659    /// contextual validation.
660    ///
661    /// This is exposed for use in checkpointing, which produces checkpoint vefified
662    /// blocks. This request can be made out-of-order; the state service will queue
663    /// it until its parent is ready.
664    ///
665    /// Returns [`Response::Committed`] with the hash of the newly committed
666    /// block, or an error.
667    ///
668    /// This request cannot be cancelled once submitted; dropping the response
669    /// future will have no effect on whether it is eventually processed.
670    /// Duplicate requests will replace the older duplicate, and return an error
671    /// in its response future.
672    ///
673    /// # Note
674    ///
675    /// [`SemanticallyVerifiedBlock`], [`ContextuallyVerifiedBlock`] and
676    /// [`CheckpointVerifiedBlock`] are an internal Zebra implementation detail.
677    /// There is no difference between these blocks on the Zcash network, or in Zebra's
678    /// network or syncer implementations.
679    ///
680    /// # Consensus
681    ///
682    /// Checkpointing is allowed under the Zcash "social consensus" rules.
683    /// Zebra checkpoints both settled network upgrades, and blocks past the rollback limit.
684    /// (By the time Zebra release is tagged, its final checkpoint is typically hours or days old.)
685    ///
686    /// > A network upgrade is settled on a given network when there is a social consensus
687    /// > that it has activated with a given activation block hash. A full validator that
688    /// > potentially risks Mainnet funds or displays Mainnet transaction information to a user
689    /// > MUST do so only for a block chain that includes the activation block of the most
690    /// > recent settled network upgrade, with the corresponding activation block hash.
691    /// > ...
692    /// > A full validator MAY impose a limit on the number of blocks it will “roll back”
693    /// > when switching from one best valid block chain to another that is not a descendent.
694    /// > For `zcashd` and `zebra` this limit is 100 blocks.
695    ///
696    /// <https://zips.z.cash/protocol/protocol.pdf#blockchain>
697    ///
698    /// # Correctness
699    ///
700    /// Block commit requests should be wrapped in a timeout, so that
701    /// out-of-order and invalid requests do not hang indefinitely. See the [`crate`]
702    /// documentation for details.
703    CommitCheckpointVerifiedBlock(CheckpointVerifiedBlock),
704
705    /// Computes the depth in the current best chain of the block identified by the given hash.
706    ///
707    /// Returns
708    ///
709    /// * [`Response::Depth(Some(depth))`](Response::Depth) if the block is in the best chain;
710    /// * [`Response::Depth(None)`](Response::Depth) otherwise.
711    Depth(block::Hash),
712
713    /// Returns [`Response::Tip(Option<(Height, block::Hash)>)`](Response::Tip)
714    /// with the current best chain tip.
715    Tip,
716
717    /// Computes a block locator object based on the current best chain.
718    ///
719    /// Returns [`Response::BlockLocator`] with hashes starting
720    /// from the best chain tip, and following the chain of previous
721    /// hashes. The first hash is the best chain tip. The last hash is
722    /// the tip of the finalized portion of the state. Block locators
723    /// are not continuous - some intermediate hashes might be skipped.
724    ///
725    /// If the state is empty, the block locator is also empty.
726    BlockLocator,
727
728    /// Looks up a transaction by hash in the current best chain.
729    ///
730    /// Returns
731    ///
732    /// * [`Response::Transaction(Some(Arc<Transaction>))`](Response::Transaction) if the transaction is in the best chain;
733    /// * [`Response::Transaction(None)`](Response::Transaction) otherwise.
734    Transaction(transaction::Hash),
735
736    /// Looks up a UTXO identified by the given [`OutPoint`](transparent::OutPoint),
737    /// returning `None` immediately if it is unknown.
738    ///
739    /// Checks verified blocks in the finalized chain and the _best_ non-finalized chain.
740    UnspentBestChainUtxo(transparent::OutPoint),
741
742    /// Looks up a block by hash or height in the current best chain.
743    ///
744    /// Returns
745    ///
746    /// * [`Response::Block(Some(Arc<Block>))`](Response::Block) if the block is in the best chain;
747    /// * [`Response::Block(None)`](Response::Block) otherwise.
748    ///
749    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
750    /// [`block::Height`] using `.into()`.
751    Block(HashOrHeight),
752
753    //// Same as Block, but also returns serialized block size.
754    ////
755    /// Returns
756    ///
757    /// * [`ReadResponse::BlockAndSize(Some((Arc<Block>, usize)))`](ReadResponse::BlockAndSize) if the block is in the best chain;
758    /// * [`ReadResponse::BlockAndSize(None)`](ReadResponse::BlockAndSize) otherwise.
759    BlockAndSize(HashOrHeight),
760
761    /// Looks up a block header by hash or height in the current best chain.
762    ///
763    /// Returns
764    ///
765    /// [`Response::BlockHeader(block::Header)`](Response::BlockHeader).
766    ///
767    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
768    /// [`block::Height`] using `.into()`.
769    BlockHeader(HashOrHeight),
770
771    /// Request a UTXO identified by the given [`OutPoint`](transparent::OutPoint),
772    /// waiting until it becomes available if it is unknown.
773    ///
774    /// Checks the finalized chain, all non-finalized chains, queued unverified blocks,
775    /// and any blocks that arrive at the state after the request future has been created.
776    ///
777    /// This request is purely informational, and there are no guarantees about
778    /// whether the UTXO remains unspent or is on the best chain, or any chain.
779    /// Its purpose is to allow asynchronous script verification or to wait until
780    /// the UTXO arrives in the state before validating dependent transactions.
781    ///
782    /// # Correctness
783    ///
784    /// UTXO requests should be wrapped in a timeout, so that
785    /// out-of-order and invalid requests do not hang indefinitely. See the [`crate`]
786    /// documentation for details.
787    ///
788    /// Outdated requests are pruned on a regular basis.
789    AwaitUtxo(transparent::OutPoint),
790
791    /// Finds the first hash that's in the peer's `known_blocks` and the local best chain.
792    /// Returns a list of hashes that follow that intersection, from the best chain.
793    ///
794    /// If there is no matching hash in the best chain, starts from the genesis hash.
795    ///
796    /// Stops the list of hashes after:
797    ///   * adding the best tip,
798    ///   * adding the `stop` hash to the list, if it is in the best chain, or
799    ///   * adding 500 hashes to the list.
800    ///
801    /// Returns an empty list if the state is empty.
802    ///
803    /// Returns
804    ///
805    /// [`Response::BlockHashes(Vec<block::Hash>)`](Response::BlockHashes).
806    /// See <https://en.bitcoin.it/wiki/Protocol_documentation#getblocks>
807    FindBlockHashes {
808        /// Hashes of known blocks, ordered from highest height to lowest height.
809        known_blocks: Vec<block::Hash>,
810        /// Optionally, the last block hash to request.
811        stop: Option<block::Hash>,
812    },
813
814    /// Finds the first hash that's in the peer's `known_blocks` and the local best chain.
815    /// Returns a list of headers that follow that intersection, from the best chain.
816    ///
817    /// If there is no matching hash in the best chain, starts from the genesis header.
818    ///
819    /// Stops the list of headers after:
820    ///   * adding the best tip,
821    ///   * adding the header matching the `stop` hash to the list, if it is in the best chain, or
822    ///   * adding [`MAX_FIND_BLOCK_HEADERS_RESULTS`] headers to the list.
823    ///
824    /// Returns an empty list if the state is empty.
825    ///
826    /// Returns
827    ///
828    /// [`Response::BlockHeaders(Vec<block::Header>)`](Response::BlockHeaders).
829    /// See <https://en.bitcoin.it/wiki/Protocol_documentation#getheaders>
830    FindBlockHeaders {
831        /// Hashes of known blocks, ordered from highest height to lowest height.
832        known_blocks: Vec<block::Hash>,
833        /// Optionally, the hash of the last header to request.
834        stop: Option<block::Hash>,
835    },
836
837    /// Contextually validates anchors and nullifiers of a transaction on the best chain
838    ///
839    /// Returns [`Response::ValidBestChainTipNullifiersAndAnchors`]
840    CheckBestChainTipNullifiersAndAnchors(UnminedTx),
841
842    /// Calculates the median-time-past for the *next* block on the best chain.
843    ///
844    /// Returns [`Response::BestChainNextMedianTimePast`] when successful.
845    BestChainNextMedianTimePast,
846
847    /// Looks up a block hash by height in the current best chain.
848    ///
849    /// Returns
850    ///
851    /// * [`Response::BlockHash(Some(hash))`](Response::BlockHash) if the block is in the best chain;
852    /// * [`Response::BlockHash(None)`](Response::BlockHash) otherwise.
853    BestChainBlockHash(block::Height),
854
855    /// Checks if a block is present anywhere in the state service.
856    /// Looks up `hash` in block queues as well as the finalized chain and all non-finalized chains.
857    ///
858    /// Returns [`Response::KnownBlock(Some(Location))`](Response::KnownBlock) if the block is in the best state service.
859    /// Returns [`Response::KnownBlock(None)`](Response::KnownBlock) otherwise.
860    KnownBlock(block::Hash),
861
862    /// Invalidates a block in the non-finalized state with the provided hash if one is present, removing it and
863    /// its child blocks, and rejecting it during contextual validation if it's resubmitted to the state.
864    InvalidateBlock(block::Hash),
865
866    /// Reconsiders a previously invalidated block in the non-finalized state with the provided hash if one is present.
867    ReconsiderBlock(block::Hash),
868
869    /// Performs contextual validation of the given block, but does not commit it to the state.
870    ///
871    /// Returns [`Response::ValidBlockProposal`] when successful.
872    /// See `[ReadRequest::CheckBlockProposalValidity]` for details.
873    CheckBlockProposalValidity(SemanticallyVerifiedBlock),
874}
875
876impl Request {
877    fn variant_name(&self) -> &'static str {
878        match self {
879            Request::CommitSemanticallyVerifiedBlock(_) => "commit_semantically_verified_block",
880            Request::CommitCheckpointVerifiedBlock(_) => "commit_checkpoint_verified_block",
881
882            Request::AwaitUtxo(_) => "await_utxo",
883            Request::Depth(_) => "depth",
884            Request::Tip => "tip",
885            Request::BlockLocator => "block_locator",
886            Request::Transaction(_) => "transaction",
887            Request::UnspentBestChainUtxo { .. } => "unspent_best_chain_utxo",
888            Request::Block(_) => "block",
889            Request::BlockAndSize(_) => "block_and_size",
890            Request::BlockHeader(_) => "block_header",
891            Request::FindBlockHashes { .. } => "find_block_hashes",
892            Request::FindBlockHeaders { .. } => "find_block_headers",
893            Request::CheckBestChainTipNullifiersAndAnchors(_) => {
894                "best_chain_tip_nullifiers_anchors"
895            }
896            Request::BestChainNextMedianTimePast => "best_chain_next_median_time_past",
897            Request::BestChainBlockHash(_) => "best_chain_block_hash",
898            Request::KnownBlock(_) => "known_block",
899            Request::InvalidateBlock(_) => "invalidate_block",
900            Request::ReconsiderBlock(_) => "reconsider_block",
901            Request::CheckBlockProposalValidity(_) => "check_block_proposal_validity",
902        }
903    }
904
905    /// Counts metric for StateService call
906    pub fn count_metric(&self) {
907        metrics::counter!(
908            "state.requests",
909            "service" => "state",
910            "type" => self.variant_name()
911        )
912        .increment(1);
913    }
914}
915
916#[derive(Clone, Debug, PartialEq, Eq)]
917/// A read-only query about the chain state, via the
918/// [`ReadStateService`](crate::service::ReadStateService).
919pub enum ReadRequest {
920    /// Returns [`ReadResponse::UsageInfo(num_bytes: u64)`](ReadResponse::UsageInfo)
921    /// with the current disk space usage in bytes.
922    UsageInfo,
923
924    /// Returns [`ReadResponse::Tip(Option<(Height, block::Hash)>)`](ReadResponse::Tip)
925    /// with the current best chain tip.
926    Tip,
927
928    /// Returns [`ReadResponse::TipPoolValues(Option<(Height, block::Hash, ValueBalance)>)`](ReadResponse::TipPoolValues)
929    /// with the pool values of the current best chain tip.
930    TipPoolValues,
931
932    /// Looks up the block info after a block by hash or height in the current best chain.
933    ///
934    /// * [`ReadResponse::BlockInfo(Some(pool_values))`](ReadResponse::BlockInfo) if the block is in the best chain;
935    /// * [`ReadResponse::BlockInfo(None)`](ReadResponse::BlockInfo) otherwise.
936    BlockInfo(HashOrHeight),
937
938    /// Computes the depth in the current best chain of the block identified by the given hash.
939    ///
940    /// Returns
941    ///
942    /// * [`ReadResponse::Depth(Some(depth))`](ReadResponse::Depth) if the block is in the best chain;
943    /// * [`ReadResponse::Depth(None)`](ReadResponse::Depth) otherwise.
944    Depth(block::Hash),
945
946    /// Looks up a block by hash or height in the current best chain.
947    ///
948    /// Returns
949    ///
950    /// * [`ReadResponse::Block(Some(Arc<Block>))`](ReadResponse::Block) if the block is in the best chain;
951    /// * [`ReadResponse::Block(None)`](ReadResponse::Block) otherwise.
952    ///
953    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
954    /// [`block::Height`] using `.into()`.
955    Block(HashOrHeight),
956
957    //// Same as Block, but also returns serialized block size.
958    ////
959    /// Returns
960    ///
961    /// * [`ReadResponse::BlockAndSize(Some((Arc<Block>, usize)))`](ReadResponse::BlockAndSize) if the block is in the best chain;
962    /// * [`ReadResponse::BlockAndSize(None)`](ReadResponse::BlockAndSize) otherwise.
963    BlockAndSize(HashOrHeight),
964
965    /// Looks up a block header by hash or height in the current best chain.
966    ///
967    /// Returns
968    ///
969    /// [`Response::BlockHeader(block::Header)`](Response::BlockHeader).
970    ///
971    /// Note: the [`HashOrHeight`] can be constructed from a [`block::Hash`] or
972    /// [`block::Height`] using `.into()`.
973    BlockHeader(HashOrHeight),
974
975    /// Looks up a transaction by hash in the current best chain.
976    ///
977    /// Returns
978    ///
979    /// * [`ReadResponse::Transaction(Some(Arc<Transaction>))`](ReadResponse::Transaction) if the transaction is in the best chain;
980    /// * [`ReadResponse::Transaction(None)`](ReadResponse::Transaction) otherwise.
981    Transaction(transaction::Hash),
982
983    /// Looks up the transaction IDs for a block, using a block hash or height.
984    ///
985    /// Returns
986    ///
987    /// * An ordered list of transaction hashes, or
988    /// * `None` if the block was not found.
989    ///
990    /// Note: Each block has at least one transaction: the coinbase transaction.
991    ///
992    /// Returned txids are in the order they appear in the block.
993    TransactionIdsForBlock(HashOrHeight),
994
995    /// Looks up a UTXO identified by the given [`OutPoint`](transparent::OutPoint),
996    /// returning `None` immediately if it is unknown.
997    ///
998    /// Checks verified blocks in the finalized chain and the _best_ non-finalized chain.
999    UnspentBestChainUtxo(transparent::OutPoint),
1000
1001    /// Looks up a UTXO identified by the given [`OutPoint`](transparent::OutPoint),
1002    /// returning `None` immediately if it is unknown.
1003    ///
1004    /// Checks verified blocks in the finalized chain and _all_ non-finalized chains.
1005    ///
1006    /// This request is purely informational, there is no guarantee that
1007    /// the UTXO remains unspent in the best chain.
1008    AnyChainUtxo(transparent::OutPoint),
1009
1010    /// Computes a block locator object based on the current best chain.
1011    ///
1012    /// Returns [`ReadResponse::BlockLocator`] with hashes starting
1013    /// from the best chain tip, and following the chain of previous
1014    /// hashes. The first hash is the best chain tip. The last hash is
1015    /// the tip of the finalized portion of the state. Block locators
1016    /// are not continuous - some intermediate hashes might be skipped.
1017    ///
1018    /// If the state is empty, the block locator is also empty.
1019    BlockLocator,
1020
1021    /// Finds the first hash that's in the peer's `known_blocks` and the local best chain.
1022    /// Returns a list of hashes that follow that intersection, from the best chain.
1023    ///
1024    /// If there is no matching hash in the best chain, starts from the genesis hash.
1025    ///
1026    /// Stops the list of hashes after:
1027    ///   * adding the best tip,
1028    ///   * adding the `stop` hash to the list, if it is in the best chain, or
1029    ///   * adding [`MAX_FIND_BLOCK_HASHES_RESULTS`] hashes to the list.
1030    ///
1031    /// Returns an empty list if the state is empty.
1032    ///
1033    /// Returns
1034    ///
1035    /// [`ReadResponse::BlockHashes(Vec<block::Hash>)`](ReadResponse::BlockHashes).
1036    /// See <https://en.bitcoin.it/wiki/Protocol_documentation#getblocks>
1037    FindBlockHashes {
1038        /// Hashes of known blocks, ordered from highest height to lowest height.
1039        known_blocks: Vec<block::Hash>,
1040        /// Optionally, the last block hash to request.
1041        stop: Option<block::Hash>,
1042    },
1043
1044    /// Finds the first hash that's in the peer's `known_blocks` and the local best chain.
1045    /// Returns a list of headers that follow that intersection, from the best chain.
1046    ///
1047    /// If there is no matching hash in the best chain, starts from the genesis header.
1048    ///
1049    /// Stops the list of headers after:
1050    ///   * adding the best tip,
1051    ///   * adding the header matching the `stop` hash to the list, if it is in the best chain, or
1052    ///   * adding [`MAX_FIND_BLOCK_HEADERS_RESULTS`] headers to the list.
1053    ///
1054    /// Returns an empty list if the state is empty.
1055    ///
1056    /// Returns
1057    ///
1058    /// [`ReadResponse::BlockHeaders(Vec<block::Header>)`](ReadResponse::BlockHeaders).
1059    /// See <https://en.bitcoin.it/wiki/Protocol_documentation#getheaders>
1060    FindBlockHeaders {
1061        /// Hashes of known blocks, ordered from highest height to lowest height.
1062        known_blocks: Vec<block::Hash>,
1063        /// Optionally, the hash of the last header to request.
1064        stop: Option<block::Hash>,
1065    },
1066
1067    /// Looks up a Sapling note commitment tree either by a hash or height.
1068    ///
1069    /// Returns
1070    ///
1071    /// * [`ReadResponse::SaplingTree(Some(Arc<NoteCommitmentTree>))`](crate::ReadResponse::SaplingTree)
1072    ///   if the corresponding block contains a Sapling note commitment tree.
1073    /// * [`ReadResponse::SaplingTree(None)`](crate::ReadResponse::SaplingTree) otherwise.
1074    SaplingTree(HashOrHeight),
1075
1076    /// Looks up an Orchard note commitment tree either by a hash or height.
1077    ///
1078    /// Returns
1079    ///
1080    /// * [`ReadResponse::OrchardTree(Some(Arc<NoteCommitmentTree>))`](crate::ReadResponse::OrchardTree)
1081    ///   if the corresponding block contains a Sapling note commitment tree.
1082    /// * [`ReadResponse::OrchardTree(None)`](crate::ReadResponse::OrchardTree) otherwise.
1083    OrchardTree(HashOrHeight),
1084
1085    /// Returns a list of Sapling note commitment subtrees by their indexes, starting at
1086    /// `start_index`, and returning up to `limit` subtrees.
1087    ///
1088    /// Returns
1089    ///
1090    /// * [`ReadResponse::SaplingSubtree(BTreeMap<_, NoteCommitmentSubtreeData<_>>))`](crate::ReadResponse::SaplingSubtrees)
1091    /// * An empty list if there is no subtree at `start_index`.
1092    SaplingSubtrees {
1093        /// The index of the first 2^16-leaf subtree to return.
1094        start_index: NoteCommitmentSubtreeIndex,
1095        /// The maximum number of subtree values to return.
1096        limit: Option<NoteCommitmentSubtreeIndex>,
1097    },
1098
1099    /// Returns a list of Orchard note commitment subtrees by their indexes, starting at
1100    /// `start_index`, and returning up to `limit` subtrees.
1101    ///
1102    /// Returns
1103    ///
1104    /// * [`ReadResponse::OrchardSubtree(BTreeMap<_, NoteCommitmentSubtreeData<_>>))`](crate::ReadResponse::OrchardSubtrees)
1105    /// * An empty list if there is no subtree at `start_index`.
1106    OrchardSubtrees {
1107        /// The index of the first 2^16-leaf subtree to return.
1108        start_index: NoteCommitmentSubtreeIndex,
1109        /// The maximum number of subtree values to return.
1110        limit: Option<NoteCommitmentSubtreeIndex>,
1111    },
1112
1113    /// Looks up the balance of a set of transparent addresses.
1114    ///
1115    /// Returns an [`Amount`](zebra_chain::amount::Amount) with the total
1116    /// balance of the set of addresses.
1117    AddressBalance(HashSet<transparent::Address>),
1118
1119    /// Looks up transaction hashes that were sent or received from addresses,
1120    /// in an inclusive blockchain height range.
1121    ///
1122    /// Returns
1123    ///
1124    /// * An ordered, unique map of transaction locations and hashes.
1125    /// * An empty map if no transactions were found for the given arguments.
1126    ///
1127    /// Returned txids are in the order they appear in blocks,
1128    /// which ensures that they are topologically sorted
1129    /// (i.e. parent txids will appear before child txids).
1130    TransactionIdsByAddresses {
1131        /// The requested addresses.
1132        addresses: HashSet<transparent::Address>,
1133
1134        /// The blocks to be queried for transactions.
1135        height_range: RangeInclusive<block::Height>,
1136    },
1137
1138    /// Looks up a spending transaction id by its spent transparent input.
1139    ///
1140    /// Returns [`ReadResponse::TransactionId`] with the hash of the transaction
1141    /// that spent the output at the provided [`transparent::OutPoint`].
1142    #[cfg(feature = "indexer")]
1143    SpendingTransactionId(Spend),
1144
1145    /// Looks up utxos for the provided addresses.
1146    ///
1147    /// Returns a type with found utxos and transaction information.
1148    UtxosByAddresses(HashSet<transparent::Address>),
1149
1150    /// Contextually validates anchors and nullifiers of a transaction on the best chain
1151    ///
1152    /// Returns [`ReadResponse::ValidBestChainTipNullifiersAndAnchors`].
1153    CheckBestChainTipNullifiersAndAnchors(UnminedTx),
1154
1155    /// Calculates the median-time-past for the *next* block on the best chain.
1156    ///
1157    /// Returns [`ReadResponse::BestChainNextMedianTimePast`] when successful.
1158    BestChainNextMedianTimePast,
1159
1160    /// Looks up a block hash by height in the current best chain.
1161    ///
1162    /// Returns
1163    ///
1164    /// * [`ReadResponse::BlockHash(Some(hash))`](ReadResponse::BlockHash) if the block is in the best chain;
1165    /// * [`ReadResponse::BlockHash(None)`](ReadResponse::BlockHash) otherwise.
1166    BestChainBlockHash(block::Height),
1167
1168    /// Get state information from the best block chain.
1169    ///
1170    /// Returns [`ReadResponse::ChainInfo(info)`](ReadResponse::ChainInfo) where `info` is a
1171    /// [`zebra-state::GetBlockTemplateChainInfo`](zebra-state::GetBlockTemplateChainInfo)` structure containing
1172    /// best chain state information.
1173    ChainInfo,
1174
1175    /// Get the average solution rate in the best chain.
1176    ///
1177    /// Returns [`ReadResponse::SolutionRate`]
1178    SolutionRate {
1179        /// The number of blocks to calculate the average difficulty for.
1180        num_blocks: usize,
1181        /// Optionally estimate the network solution rate at the time when this height was mined.
1182        /// Otherwise, estimate at the current tip height.
1183        height: Option<block::Height>,
1184    },
1185
1186    /// Performs contextual validation of the given block, but does not commit it to the state.
1187    ///
1188    /// It is the caller's responsibility to perform semantic validation.
1189    /// (The caller does not need to check proof of work for block proposals.)
1190    ///
1191    /// Returns [`ReadResponse::ValidBlockProposal`] when successful, or an error if
1192    /// the block fails contextual validation.
1193    CheckBlockProposalValidity(SemanticallyVerifiedBlock),
1194
1195    /// Returns [`ReadResponse::TipBlockSize(usize)`](ReadResponse::TipBlockSize)
1196    /// with the current best chain tip block size in bytes.
1197    TipBlockSize,
1198
1199    /// Returns [`ReadResponse::NonFinalizedBlocksListener`] with a channel receiver
1200    /// allowing the caller to listen for new blocks in the non-finalized state.
1201    NonFinalizedBlocksListener,
1202}
1203
1204impl ReadRequest {
1205    fn variant_name(&self) -> &'static str {
1206        match self {
1207            ReadRequest::UsageInfo => "usage_info",
1208            ReadRequest::Tip => "tip",
1209            ReadRequest::TipPoolValues => "tip_pool_values",
1210            ReadRequest::BlockInfo(_) => "block_info",
1211            ReadRequest::Depth(_) => "depth",
1212            ReadRequest::Block(_) => "block",
1213            ReadRequest::BlockAndSize(_) => "block_and_size",
1214            ReadRequest::BlockHeader(_) => "block_header",
1215            ReadRequest::Transaction(_) => "transaction",
1216            ReadRequest::TransactionIdsForBlock(_) => "transaction_ids_for_block",
1217            ReadRequest::UnspentBestChainUtxo { .. } => "unspent_best_chain_utxo",
1218            ReadRequest::AnyChainUtxo { .. } => "any_chain_utxo",
1219            ReadRequest::BlockLocator => "block_locator",
1220            ReadRequest::FindBlockHashes { .. } => "find_block_hashes",
1221            ReadRequest::FindBlockHeaders { .. } => "find_block_headers",
1222            ReadRequest::SaplingTree { .. } => "sapling_tree",
1223            ReadRequest::OrchardTree { .. } => "orchard_tree",
1224            ReadRequest::SaplingSubtrees { .. } => "sapling_subtrees",
1225            ReadRequest::OrchardSubtrees { .. } => "orchard_subtrees",
1226            ReadRequest::AddressBalance { .. } => "address_balance",
1227            ReadRequest::TransactionIdsByAddresses { .. } => "transaction_ids_by_addresses",
1228            ReadRequest::UtxosByAddresses(_) => "utxos_by_addresses",
1229            ReadRequest::CheckBestChainTipNullifiersAndAnchors(_) => {
1230                "best_chain_tip_nullifiers_anchors"
1231            }
1232            ReadRequest::BestChainNextMedianTimePast => "best_chain_next_median_time_past",
1233            ReadRequest::BestChainBlockHash(_) => "best_chain_block_hash",
1234            #[cfg(feature = "indexer")]
1235            ReadRequest::SpendingTransactionId(_) => "spending_transaction_id",
1236            ReadRequest::ChainInfo => "chain_info",
1237            ReadRequest::SolutionRate { .. } => "solution_rate",
1238            ReadRequest::CheckBlockProposalValidity(_) => "check_block_proposal_validity",
1239            ReadRequest::TipBlockSize => "tip_block_size",
1240            ReadRequest::NonFinalizedBlocksListener => "non_finalized_blocks_listener",
1241        }
1242    }
1243
1244    /// Counts metric for ReadStateService call
1245    pub fn count_metric(&self) {
1246        metrics::counter!(
1247            "state.requests",
1248            "service" => "read_state",
1249            "type" => self.variant_name()
1250        )
1251        .increment(1);
1252    }
1253}
1254
1255/// Conversion from read-write [`Request`]s to read-only [`ReadRequest`]s.
1256///
1257/// Used to dispatch read requests concurrently from the [`StateService`](crate::service::StateService).
1258impl TryFrom<Request> for ReadRequest {
1259    type Error = &'static str;
1260
1261    fn try_from(request: Request) -> Result<ReadRequest, Self::Error> {
1262        match request {
1263            Request::Tip => Ok(ReadRequest::Tip),
1264            Request::Depth(hash) => Ok(ReadRequest::Depth(hash)),
1265            Request::BestChainNextMedianTimePast => Ok(ReadRequest::BestChainNextMedianTimePast),
1266            Request::BestChainBlockHash(hash) => Ok(ReadRequest::BestChainBlockHash(hash)),
1267
1268            Request::Block(hash_or_height) => Ok(ReadRequest::Block(hash_or_height)),
1269            Request::BlockAndSize(hash_or_height) => Ok(ReadRequest::BlockAndSize(hash_or_height)),
1270            Request::BlockHeader(hash_or_height) => Ok(ReadRequest::BlockHeader(hash_or_height)),
1271            Request::Transaction(tx_hash) => Ok(ReadRequest::Transaction(tx_hash)),
1272            Request::UnspentBestChainUtxo(outpoint) => {
1273                Ok(ReadRequest::UnspentBestChainUtxo(outpoint))
1274            }
1275
1276            Request::BlockLocator => Ok(ReadRequest::BlockLocator),
1277            Request::FindBlockHashes { known_blocks, stop } => {
1278                Ok(ReadRequest::FindBlockHashes { known_blocks, stop })
1279            }
1280            Request::FindBlockHeaders { known_blocks, stop } => {
1281                Ok(ReadRequest::FindBlockHeaders { known_blocks, stop })
1282            }
1283
1284            Request::CheckBestChainTipNullifiersAndAnchors(tx) => {
1285                Ok(ReadRequest::CheckBestChainTipNullifiersAndAnchors(tx))
1286            }
1287
1288            Request::CommitSemanticallyVerifiedBlock(_)
1289            | Request::CommitCheckpointVerifiedBlock(_)
1290            | Request::InvalidateBlock(_)
1291            | Request::ReconsiderBlock(_) => Err("ReadService does not write blocks"),
1292
1293            Request::AwaitUtxo(_) => Err("ReadService does not track pending UTXOs. \
1294                     Manually convert the request to ReadRequest::AnyChainUtxo, \
1295                     and handle pending UTXOs"),
1296
1297            Request::KnownBlock(_) => Err("ReadService does not track queued blocks"),
1298
1299            Request::CheckBlockProposalValidity(semantically_verified) => Ok(
1300                ReadRequest::CheckBlockProposalValidity(semantically_verified),
1301            ),
1302        }
1303    }
1304}