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