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