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}