Skip to main content

zebra_state/
response.rs

1//! State [`tower::Service`] response types.
2
3use std::{
4    collections::{BTreeMap, HashMap, HashSet},
5    sync::Arc,
6};
7
8use chrono::{DateTime, Utc};
9
10use zebra_chain::{
11    amount::{Amount, NonNegative},
12    block::{self, Block, ChainHistoryMmrRootHash},
13    block_info::BlockInfo,
14    orchard, sapling,
15    serialization::DateTime32,
16    subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
17    transaction::{self, Transaction},
18    transparent,
19    value_balance::ValueBalance,
20};
21
22use zebra_chain::work::difficulty::CompactDifficulty;
23
24// Allow *only* these unused imports, so that rustdoc link resolution
25// will work with inline links.
26#[allow(unused_imports)]
27use crate::{ReadRequest, Request};
28
29use crate::{
30    service::read::AddressUtxos, ContextuallyVerifiedBlock, NonFinalizedState, TransactionLocation,
31    WatchReceiver, MAX_BLOCK_REORG_HEIGHT,
32};
33
34#[cfg(test)]
35mod tests;
36
37#[derive(Clone, Debug, PartialEq, Eq)]
38/// A response to a [`StateService`](crate::service::StateService) [`Request`].
39pub enum Response {
40    /// Response to [`Request::CommitSemanticallyVerifiedBlock`] and [`Request::CommitCheckpointVerifiedBlock`]
41    /// indicating that a block was successfully committed to the state.
42    Committed(block::Hash),
43
44    /// Response to [`Request::InvalidateBlock`] indicating that a block was found and
45    /// invalidated in the state.
46    Invalidated(block::Hash),
47
48    /// Response to [`Request::ReconsiderBlock`] indicating that a previously invalidated
49    /// block was reconsidered and re-committed to the non-finalized state. Contains a list
50    /// of block hashes that were reconsidered in the state and successfully re-committed.
51    Reconsidered(Vec<block::Hash>),
52
53    /// Response to [`Request::Depth`] with the depth of the specified block.
54    Depth(Option<u32>),
55
56    /// Response to [`Request::Tip`] with the current best chain tip.
57    //
58    // TODO: remove this request, and replace it with a call to
59    //       `LatestChainTip::best_tip_height_and_hash()`
60    Tip(Option<(block::Height, block::Hash)>),
61
62    /// Response to [`Request::BlockLocator`] with a block locator object.
63    BlockLocator(Vec<block::Hash>),
64
65    /// Response to [`Request::Transaction`] with the specified transaction.
66    Transaction(Option<Arc<Transaction>>),
67
68    /// Response to [`Request::AnyChainTransaction`] with the specified transaction.
69    AnyChainTransaction(Option<AnyTx>),
70
71    /// Response to [`Request::UnspentBestChainUtxo`] with the UTXO
72    UnspentBestChainUtxo(Option<transparent::Utxo>),
73
74    /// Response to [`Request::Block`] with the specified block.
75    Block(Option<Arc<Block>>),
76
77    /// Response to [`Request::BlockAndSize`] with the specified block and size.
78    BlockAndSize(Option<(Arc<Block>, usize)>),
79
80    /// The response to a `BlockHeader` request.
81    BlockHeader {
82        /// The header of the requested block
83        header: Arc<block::Header>,
84        /// The hash of the requested block
85        hash: block::Hash,
86        /// The height of the requested block
87        height: block::Height,
88        /// The hash of the next block after the requested block
89        next_block_hash: Option<block::Hash>,
90    },
91
92    /// The response to a `AwaitUtxo` request, from any non-finalized chains, finalized chain,
93    /// pending unverified blocks, or blocks received after the request was sent.
94    Utxo(transparent::Utxo),
95
96    /// The response to a `FindBlockHashes` request.
97    BlockHashes(Vec<block::Hash>),
98
99    /// The response to a `FindBlockHeaders` request.
100    BlockHeaders(Vec<block::CountedHeader>),
101
102    /// Response to [`Request::CheckBestChainTipNullifiersAndAnchors`].
103    ///
104    /// Does not check transparent UTXO inputs
105    ValidBestChainTipNullifiersAndAnchors,
106
107    /// Response to [`Request::BestChainNextMedianTimePast`].
108    /// Contains the median-time-past for the *next* block on the best chain.
109    BestChainNextMedianTimePast(DateTime32),
110
111    /// Response to [`Request::BestChainBlockHash`] with the specified block hash.
112    BlockHash(Option<block::Hash>),
113
114    /// Response to [`Request::KnownBlock`].
115    KnownBlock(Option<KnownBlock>),
116
117    /// Response to [`Request::CheckBlockProposalValidity`]
118    ValidBlockProposal,
119}
120
121#[derive(Clone, Debug, PartialEq, Eq)]
122/// An enum of block stores in the state where a block hash could be found.
123pub enum KnownBlock {
124    /// Block is in the finalized portion of the best chain.
125    Finalized,
126
127    /// Block is in the best chain.
128    BestChain,
129
130    /// Block is in a side chain.
131    SideChain,
132
133    /// Block is in a block write channel
134    WriteChannel,
135
136    /// Block is queued to be validated and committed, or rejected and dropped.
137    Queue,
138}
139
140impl std::fmt::Display for KnownBlock {
141    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
142        match self {
143            KnownBlock::Finalized => write!(f, "finalized state"),
144            KnownBlock::BestChain => write!(f, "best chain"),
145            KnownBlock::SideChain => write!(f, "side chain"),
146            KnownBlock::WriteChannel => write!(f, "block write channel"),
147            KnownBlock::Queue => write!(f, "validation/commit queue"),
148        }
149    }
150}
151
152/// Information about a transaction in any chain.
153#[derive(Clone, Debug, PartialEq, Eq)]
154pub enum AnyTx {
155    /// A transaction in the best chain.
156    Mined(MinedTx),
157    /// A transaction in a side chain, and the hash of the block it is in.
158    Side((Arc<Transaction>, block::Hash)),
159}
160
161impl From<AnyTx> for Arc<Transaction> {
162    fn from(any_tx: AnyTx) -> Self {
163        match any_tx {
164            AnyTx::Mined(mined_tx) => mined_tx.tx,
165            AnyTx::Side((tx, _)) => tx,
166        }
167    }
168}
169
170/// Information about a transaction in the best chain
171#[derive(Clone, Debug, PartialEq, Eq)]
172pub struct MinedTx {
173    /// The transaction.
174    pub tx: Arc<Transaction>,
175
176    /// The transaction height.
177    pub height: block::Height,
178
179    /// The number of confirmations for this transaction
180    /// (1 + depth of block the transaction was found in)
181    pub confirmations: u32,
182
183    /// The time of the block where the transaction was mined.
184    pub block_time: DateTime<Utc>,
185
186    /// The best-chain tip hash captured in the same state snapshot used to
187    /// compute `confirmations`.
188    ///
189    /// Callers that combine this response with other state queries should
190    /// pin those follow-up queries to this hash (or to the resolved block
191    /// hash for the transaction) rather than issuing a separate `Tip` /
192    /// `BestChainBlockHash` request, which would re-sample the chain and
193    /// can race with reorgs or new blocks. See issue #10550.
194    pub best_chain_tip_hash: block::Hash,
195}
196
197impl MinedTx {
198    /// Creates a new [`MinedTx`]
199    pub fn new(
200        tx: Arc<Transaction>,
201        height: block::Height,
202        confirmations: u32,
203        block_time: DateTime<Utc>,
204        best_chain_tip_hash: block::Hash,
205    ) -> Self {
206        Self {
207            tx,
208            height,
209            confirmations,
210            block_time,
211            best_chain_tip_hash,
212        }
213    }
214}
215
216/// How many non-finalized block references to buffer in [`NonFinalizedBlocksListener`] before blocking sends.
217///
218/// # Correctness
219///
220/// This should be large enough to typically avoid blocking the sender when the non-finalized state is full so
221/// that the [`NonFinalizedBlocksListener`] reliably receives updates whenever the non-finalized state changes.
222///
223/// If the buffer does fill, sends apply backpressure (the sender awaits a free slot) rather than
224/// dropping blocks, so the listener still receives every block once the consumer catches up.
225// `MAX_BLOCK_REORG_HEIGHT` is a small `u32` constant (the reorg limit), so widening it to `usize`
226// and doubling it cannot overflow on any supported platform.
227const NON_FINALIZED_STATE_CHANGE_BUFFER_SIZE: usize = 2 * MAX_BLOCK_REORG_HEIGHT as usize;
228
229/// A listener for changes in the non-finalized state.
230#[derive(Clone, Debug)]
231pub struct NonFinalizedBlocksListener(
232    pub Arc<tokio::sync::mpsc::Receiver<(zebra_chain::block::Hash, Arc<zebra_chain::block::Block>)>>,
233);
234
235impl NonFinalizedBlocksListener {
236    /// Sends the blocks in `non_finalized_state` that satisfy `take_cond` to `sender`, in
237    /// ascending height order.
238    ///
239    /// Walks each chain from its tip downwards, taking blocks while `take_cond` holds and stopping
240    /// at the first block that fails it, so it sends the blocks a listener hasn't been sent yet by
241    /// stopping at the first block it already has.
242    ///
243    /// Blocks below a fork point belong to every chain that forked there, so each block is only
244    /// sent once, as part of the highest-work chain that contains it.
245    ///
246    /// Returns an error if the receiver has been dropped.
247    async fn take_and_send_blocks<'a>(
248        sender: &tokio::sync::mpsc::Sender<(block::Hash, Arc<Block>)>,
249        non_finalized_state: &'a NonFinalizedState,
250        take_cond: impl Fn(&&ContextuallyVerifiedBlock) -> bool + Copy + 'a,
251    ) -> Result<(), tokio::sync::mpsc::error::SendError<(block::Hash, Arc<Block>)>> {
252        let new_blocks = non_finalized_state.chain_iter().flat_map(move |chain| {
253            // Take blocks from the chain in reverse height order until we reach a block the
254            // listener already has, then restore ascending height order.
255            let mut blocks: Vec<_> = chain.blocks.values().rev().take_while(take_cond).collect();
256            blocks.reverse();
257            blocks
258        });
259
260        // Chains share every block below their fork point, so without this the shared blocks would
261        // be sent once per chain: up to `MAX_NON_FINALIZED_CHAIN_FORKS` copies of a chain that can
262        // be `MAX_BLOCK_REORG_HEIGHT` blocks long, which overflows the channel buffer and makes the
263        // listener wait on the consumer during an ordinary initial send. `chain_iter()` yields the
264        // highest-work chain first, so a fork's remaining blocks still follow the shared ancestors
265        // they build on.
266        let mut sent_hashes = HashSet::new();
267
268        for cv_block in new_blocks {
269            if !sent_hashes.insert(cv_block.hash) {
270                continue;
271            }
272
273            sender.send((cv_block.hash, cv_block.block.clone())).await?;
274        }
275
276        Ok(())
277    }
278
279    /// Spawns a task to listen for changes in the non-finalized state and sends any blocks in the non-finalized state
280    /// to the caller that have not already been sent.
281    ///
282    /// `known_chain_tips` holds the hashes of chain tips the caller already has. On the first send
283    /// only, each non-finalized chain is walked from its tip downwards and blocks are sent until a
284    /// hash in this set is reached, so any block at or below a known tip on the same chain is
285    /// skipped. If it is empty, every block currently in the non-finalized state is sent. After the
286    /// first send, those blocks are already tracked as sent, so later sends only forward blocks that
287    /// weren't in the previously seen non-finalized state.
288    ///
289    /// Returns a new instance of [`NonFinalizedBlocksListener`] for the caller to listen for new blocks in the non-finalized state.
290    pub fn spawn(
291        mut non_finalized_state_receiver: WatchReceiver<NonFinalizedState>,
292        known_chain_tips: HashSet<block::Hash>,
293    ) -> Self {
294        let (sender, receiver) = tokio::sync::mpsc::channel(NON_FINALIZED_STATE_CHANGE_BUFFER_SIZE);
295
296        tokio::spawn(async move {
297            // `prev_non_finalized_state` starts as the current non-finalized state. The first send
298            // below skips blocks at or below the caller's known chain tips; afterwards those blocks
299            // are already in `prev_non_finalized_state`, so later sends only need to check it.
300            let mut prev_non_finalized_state = non_finalized_state_receiver.cloned_watch_data();
301
302            // Send the blocks the caller is missing relative to its known chain tips. This checks
303            // `known_chain_tips` once; from here on those blocks are covered by the state check.
304            if Self::take_and_send_blocks(&sender, &prev_non_finalized_state, |b| {
305                !known_chain_tips.contains(&b.hash)
306            })
307            .await
308            .is_err()
309            {
310                tracing::debug!("non-finalized blocks receiver closed, ending task");
311                return;
312            }
313
314            // # Correctness
315            //
316            // This loop should check that the non-finalized state receiver has changed sooner
317            // than the non-finalized state could possibly have changed to avoid missing updates, so
318            // the logic here should be quicker than the contextual verification logic that precedes
319            // commits to the non-finalized state.
320            //
321            // See the `NON_FINALIZED_STATE_CHANGE_BUFFER_SIZE` documentation for more details.
322            loop {
323                let non_finalized_state = non_finalized_state_receiver.cloned_watch_data();
324
325                // Send blocks that weren't in the last seen copy of the non-finalized state. The
326                // caller's known tips are already covered by `prev_non_finalized_state`.
327                if Self::take_and_send_blocks(&sender, &non_finalized_state, |b| {
328                    !prev_non_finalized_state.any_chain_contains(&b.hash)
329                })
330                .await
331                .is_err()
332                {
333                    tracing::debug!("non-finalized blocks receiver closed, ending task");
334                    return;
335                }
336
337                prev_non_finalized_state = non_finalized_state;
338
339                // Wait for the next update to the non-finalized state.
340                if let Err(error) = non_finalized_state_receiver.changed().await {
341                    warn!(
342                        ?error,
343                        "non-finalized state receiver closed, is Zebra shutting down?"
344                    );
345                    break;
346                }
347            }
348        });
349
350        Self(Arc::new(receiver))
351    }
352
353    /// Consumes `self`, unwrapping the inner [`Arc`] and returning the non-finalized state change channel receiver.
354    ///
355    /// # Panics
356    ///
357    /// If the `Arc` has more than one strong reference, this will panic.
358    pub fn unwrap(
359        self,
360    ) -> tokio::sync::mpsc::Receiver<(zebra_chain::block::Hash, Arc<zebra_chain::block::Block>)>
361    {
362        Arc::try_unwrap(self.0).unwrap()
363    }
364}
365
366impl PartialEq for NonFinalizedBlocksListener {
367    fn eq(&self, other: &Self) -> bool {
368        Arc::ptr_eq(&self.0, &other.0)
369    }
370}
371
372impl Eq for NonFinalizedBlocksListener {}
373
374#[derive(Clone, Debug, PartialEq, Eq)]
375/// A response to a read-only
376/// [`ReadStateService`](crate::service::ReadStateService)'s [`ReadRequest`].
377pub enum ReadResponse {
378    /// Response to [`ReadRequest::UsageInfo`] with the current best chain tip.
379    UsageInfo(u64),
380
381    /// Response to [`ReadRequest::Tip`] with the current best chain tip.
382    Tip(Option<(block::Height, block::Hash)>),
383
384    /// Response to [`ReadRequest::TipPoolValues`] with
385    /// the current best chain tip and its [`ValueBalance`].
386    TipPoolValues {
387        /// The current best chain tip height.
388        tip_height: block::Height,
389        /// The current best chain tip hash.
390        tip_hash: block::Hash,
391        /// The value pool balance at the current best chain tip.
392        value_balance: ValueBalance<NonNegative>,
393    },
394
395    /// Response to [`ReadRequest::BlockInfo`] with
396    /// the block info after the specified block.
397    BlockInfo(Option<BlockInfo>),
398
399    /// Response to [`ReadRequest::Depth`] with the depth of the specified block.
400    Depth(Option<u32>),
401
402    /// Response to [`ReadRequest::Block`] with the specified block.
403    Block(Option<Arc<Block>>),
404
405    /// Response to [`ReadRequest::BlockAndSize`] with the specified block and
406    /// serialized size.
407    BlockAndSize(Option<(Arc<Block>, usize)>),
408
409    /// Response to [`ReadRequest::SpentOutputs`] with the outputs spent by the
410    /// non-coinbase inputs of the specified block, keyed by the spending
411    /// [`OutPoint`](transparent::OutPoint), or `None` if the block was not found.
412    ///
413    /// An outpoint whose spent output could not be found in the best chain is
414    /// omitted from the map.
415    SpentOutputs(Option<HashMap<transparent::OutPoint, transparent::Utxo>>),
416
417    /// The response to a `BlockHeader` request.
418    BlockHeader {
419        /// The header of the requested block
420        header: Arc<block::Header>,
421        /// The hash of the requested block
422        hash: block::Hash,
423        /// The height of the requested block
424        height: block::Height,
425        /// The hash of the next block after the requested block
426        next_block_hash: Option<block::Hash>,
427    },
428
429    /// Response to [`ReadRequest::Transaction`] with the specified transaction.
430    Transaction(Option<MinedTx>),
431
432    /// Response to [`Request::Transaction`] with the specified transaction.
433    AnyChainTransaction(Option<AnyTx>),
434
435    /// Response to [`ReadRequest::TransactionIdsForBlock`],
436    /// with an list of transaction hashes in block order,
437    /// or `None` if the block was not found.
438    TransactionIdsForBlock(Option<Arc<[transaction::Hash]>>),
439
440    /// Response to [`ReadRequest::AnyChainTransactionIdsForBlock`], with an list of
441    /// transaction hashes in block order and a flag indicating if the block is
442    /// in the best chain, or `None` if the block was not found.
443    AnyChainTransactionIdsForBlock(Option<(Arc<[transaction::Hash]>, bool)>),
444
445    /// Response to [`ReadRequest::SpendingTransactionId`],
446    /// with an list of transaction hashes in block order,
447    /// or `None` if the block was not found.
448    #[cfg(feature = "indexer")]
449    TransactionId(Option<transaction::Hash>),
450
451    /// Response to [`ReadRequest::BlockLocator`] with a block locator object.
452    BlockLocator(Vec<block::Hash>),
453
454    /// The response to a `FindBlockHashes` request.
455    BlockHashes(Vec<block::Hash>),
456
457    /// The response to a `FindBlockHeaders` request.
458    BlockHeaders(Vec<block::CountedHeader>),
459
460    /// The response to a `FindForkPoint` request.
461    /// Returns the height and hash of the fork point, or `None` if no locator entry is
462    /// on the best chain.
463    ForkPoint(Option<(block::Height, block::Hash)>),
464
465    /// The response to a `UnspentBestChainUtxo` request, from verified blocks in the
466    /// _best_ non-finalized chain, or the finalized chain.
467    UnspentBestChainUtxo(Option<transparent::Utxo>),
468
469    /// The response to an `AnyChainUtxo` request, from verified blocks in
470    /// _any_ non-finalized chain, or the finalized chain.
471    ///
472    /// This response is purely informational, there is no guarantee that
473    /// the UTXO remains unspent in the best chain.
474    AnyChainUtxo(Option<transparent::Utxo>),
475
476    /// Response to [`ReadRequest::SaplingTree`] with the specified Sapling note commitment tree.
477    SaplingTree(Option<Arc<sapling::tree::NoteCommitmentTree>>),
478
479    /// Response to [`ReadRequest::OrchardTree`] with the specified Orchard note commitment tree.
480    OrchardTree(Option<Arc<orchard::tree::NoteCommitmentTree>>),
481
482    /// Response to [`ReadRequest::IronwoodTree`] with the specified Ironwood note commitment tree.
483    IronwoodTree(Option<Arc<orchard::tree::NoteCommitmentTree>>),
484
485    /// Response to [`ReadRequest::SaplingSubtrees`] with the specified Sapling note commitment
486    /// subtrees.
487    SaplingSubtrees(
488        BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<sapling_crypto::Node>>,
489    ),
490
491    /// Response to [`ReadRequest::OrchardSubtrees`] with the specified Orchard note commitment
492    /// subtrees.
493    OrchardSubtrees(
494        BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>>,
495    ),
496
497    /// Response to [`ReadRequest::IronwoodSubtrees`] with the specified Ironwood note commitment
498    /// subtrees. Ironwood reuses the Orchard note type.
499    IronwoodSubtrees(
500        BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>>,
501    ),
502
503    /// Response to [`ReadRequest::AddressBalance`] with the total balance of the addresses,
504    /// and the total received funds, including change.
505    AddressBalance {
506        /// The total balance of the addresses.
507        balance: Amount<NonNegative>,
508        /// The total received funds in zatoshis, including change.
509        received: u64,
510    },
511
512    /// Response to [`ReadRequest::TransactionIdsByAddresses`]
513    /// with the obtained transaction ids, in the order they appear in blocks.
514    AddressesTransactionIds(BTreeMap<TransactionLocation, transaction::Hash>),
515
516    /// Response to [`ReadRequest::UtxosByAddresses`] with found utxos and transaction data.
517    AddressUtxos(AddressUtxos),
518
519    /// Response to [`ReadRequest::CheckBestChainTipNullifiersAndAnchors`].
520    ///
521    /// Does not check transparent UTXO inputs
522    ValidBestChainTipNullifiersAndAnchors,
523
524    /// Response to [`ReadRequest::BestChainNextMedianTimePast`].
525    /// Contains the median-time-past for the *next* block on the best chain.
526    BestChainNextMedianTimePast(DateTime32),
527
528    /// Response to [`ReadRequest::BestChainBlockHash`] with the specified block hash.
529    BlockHash(Option<block::Hash>),
530
531    /// Response to [`ReadRequest::ChainInfo`] with the state
532    /// information needed by the `getblocktemplate` RPC method.
533    ChainInfo(GetBlockTemplateChainInfo),
534
535    /// Response to [`ReadRequest::SolutionRate`]
536    SolutionRate(Option<u128>),
537
538    /// Response to [`ReadRequest::CheckBlockProposalValidity`]
539    ValidBlockProposal,
540
541    /// Response to [`ReadRequest::TipBlockSize`]
542    TipBlockSize(Option<usize>),
543
544    /// Response to [`ReadRequest::NonFinalizedBlocksListener`]
545    NonFinalizedBlocksListener(NonFinalizedBlocksListener),
546
547    /// Response to [`ReadRequest::IsTransparentOutputSpent`]
548    IsTransparentOutputSpent(bool),
549}
550
551/// A structure with the information needed from the state to build a `getblocktemplate` RPC response.
552#[derive(Clone, Debug, Eq, PartialEq)]
553pub struct GetBlockTemplateChainInfo {
554    // Data fetched directly from the state tip.
555    //
556    /// The current state tip height.
557    /// The block template for the candidate block has this hash as the previous block hash.
558    pub tip_hash: block::Hash,
559
560    /// The current state tip height.
561    /// The block template for the candidate block is the next block after this block.
562    /// Depends on the `tip_hash`.
563    pub tip_height: block::Height,
564
565    /// The FlyClient chain history root as of the end of the chain tip block.
566    /// Depends on the `tip_hash`.
567    pub chain_history_root: Option<ChainHistoryMmrRootHash>,
568
569    // Data derived from the state tip and recent blocks, and the current local clock.
570    //
571    /// The expected difficulty of the candidate block.
572    /// Depends on the `tip_hash`, and the local clock on testnet.
573    pub expected_difficulty: CompactDifficulty,
574
575    /// The current system time, adjusted to fit within `min_time` and `max_time`.
576    /// Always depends on the local clock and the `tip_hash`.
577    pub cur_time: DateTime32,
578
579    /// The mininimum time the miner can use in this block.
580    /// Depends on the `tip_hash`, and the local clock on testnet.
581    pub min_time: DateTime32,
582
583    /// The maximum time the miner can use in this block.
584    /// Depends on the `tip_hash`, and the local clock on testnet.
585    pub max_time: DateTime32,
586
587    /// The chain value pools after the tip block, which determine the candidate block's subsidy
588    /// once the [halving-preserving issuance ZIP][zip] is active.
589    /// Depends on the `tip_hash`.
590    ///
591    /// [zip]: https://zips.z.cash/zip-0237
592    pub chain_value_pools:
593        zebra_chain::value_balance::ValueBalance<zebra_chain::amount::NonNegative>,
594}
595
596/// Conversion from read-only [`ReadResponse`]s to read-write [`Response`]s.
597///
598/// Used to return read requests concurrently from the [`StateService`](crate::service::StateService).
599impl TryFrom<ReadResponse> for Response {
600    type Error = &'static str;
601
602    fn try_from(response: ReadResponse) -> Result<Response, Self::Error> {
603        match response {
604            ReadResponse::Tip(height_and_hash) => Ok(Response::Tip(height_and_hash)),
605            ReadResponse::Depth(depth) => Ok(Response::Depth(depth)),
606            ReadResponse::BestChainNextMedianTimePast(median_time_past) => Ok(Response::BestChainNextMedianTimePast(median_time_past)),
607            ReadResponse::BlockHash(hash) => Ok(Response::BlockHash(hash)),
608
609            ReadResponse::Block(block) => Ok(Response::Block(block)),
610            ReadResponse::BlockAndSize(block) => Ok(Response::BlockAndSize(block)),
611            ReadResponse::BlockHeader {
612                header,
613                hash,
614                height,
615                next_block_hash
616            } => Ok(Response::BlockHeader {
617                header,
618                hash,
619                height,
620                next_block_hash
621            }),
622            ReadResponse::Transaction(tx_info) => {
623                Ok(Response::Transaction(tx_info.map(|tx_info| tx_info.tx)))
624            }
625            ReadResponse::AnyChainTransaction(tx) => Ok(Response::AnyChainTransaction(tx)),
626            ReadResponse::UnspentBestChainUtxo(utxo) => Ok(Response::UnspentBestChainUtxo(utxo)),
627
628
629            ReadResponse::AnyChainUtxo(_) => Err("ReadService does not track pending UTXOs. \
630                                                  Manually unwrap the response, and handle pending UTXOs."),
631
632            ReadResponse::BlockLocator(hashes) => Ok(Response::BlockLocator(hashes)),
633            ReadResponse::BlockHashes(hashes) => Ok(Response::BlockHashes(hashes)),
634            ReadResponse::BlockHeaders(headers) => Ok(Response::BlockHeaders(headers)),
635
636            ReadResponse::ValidBestChainTipNullifiersAndAnchors => Ok(Response::ValidBestChainTipNullifiersAndAnchors),
637
638            ReadResponse::UsageInfo(_)
639            | ReadResponse::TipPoolValues { .. }
640            | ReadResponse::BlockInfo(_)
641            | ReadResponse::TransactionIdsForBlock(_)
642            | ReadResponse::SpentOutputs(_)
643            | ReadResponse::AnyChainTransactionIdsForBlock(_)
644            | ReadResponse::SaplingTree(_)
645            | ReadResponse::OrchardTree(_)
646            | ReadResponse::IronwoodTree(_)
647            | ReadResponse::SaplingSubtrees(_)
648            | ReadResponse::OrchardSubtrees(_)
649            | ReadResponse::IronwoodSubtrees(_)
650            | ReadResponse::AddressBalance { .. }
651            | ReadResponse::AddressesTransactionIds(_)
652            | ReadResponse::AddressUtxos(_)
653            | ReadResponse::ChainInfo(_)
654            | ReadResponse::NonFinalizedBlocksListener(_)
655            | ReadResponse::IsTransparentOutputSpent(_)
656            | ReadResponse::ForkPoint(_) => {
657                Err("there is no corresponding Response for this ReadResponse")
658            }
659
660            #[cfg(feature = "indexer")]
661            ReadResponse::TransactionId(_) => Err("there is no corresponding Response for this ReadResponse"),
662
663            ReadResponse::ValidBlockProposal => Ok(Response::ValidBlockProposal),
664
665            ReadResponse::SolutionRate(_) | ReadResponse::TipBlockSize(_) => {
666                Err("there is no corresponding Response for this ReadResponse")
667            }
668        }
669    }
670}