Skip to main content

zebra_state/
request.rs

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