zebra_state/response.rs
1//! State [`tower::Service`] response types.
2
3use std::{
4 collections::{BTreeMap, 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
187impl MinedTx {
188 /// Creates a new [`MinedTx`]
189 pub fn new(
190 tx: Arc<Transaction>,
191 height: block::Height,
192 confirmations: u32,
193 block_time: DateTime<Utc>,
194 ) -> Self {
195 Self {
196 tx,
197 height,
198 confirmations,
199 block_time,
200 }
201 }
202}
203
204/// How many non-finalized block references to buffer in [`NonFinalizedBlocksListener`] before blocking sends.
205///
206/// # Correctness
207///
208/// This should be large enough to typically avoid blocking the sender when the non-finalized state is full so
209/// that the [`NonFinalizedBlocksListener`] reliably receives updates whenever the non-finalized state changes.
210///
211/// If the buffer does fill, sends apply backpressure (the sender awaits a free slot) rather than
212/// dropping blocks, so the listener still receives every block once the consumer catches up.
213// `MAX_BLOCK_REORG_HEIGHT` is a small `u32` constant (the reorg limit), so widening it to `usize`
214// and doubling it cannot overflow on any supported platform.
215const NON_FINALIZED_STATE_CHANGE_BUFFER_SIZE: usize = 2 * MAX_BLOCK_REORG_HEIGHT as usize;
216
217/// A listener for changes in the non-finalized state.
218#[derive(Clone, Debug)]
219pub struct NonFinalizedBlocksListener(
220 pub Arc<tokio::sync::mpsc::Receiver<(zebra_chain::block::Hash, Arc<zebra_chain::block::Block>)>>,
221);
222
223impl NonFinalizedBlocksListener {
224 /// Sends the blocks in `non_finalized_state` that satisfy `take_cond` to `sender`, in
225 /// ascending height order.
226 ///
227 /// Walks each chain from its tip downwards, taking blocks while `take_cond` holds and stopping
228 /// at the first block that fails it, so it sends the blocks a listener hasn't been sent yet by
229 /// stopping at the first block it already has.
230 ///
231 /// Returns an error if the receiver has been dropped.
232 async fn take_and_send_blocks<'a>(
233 sender: &tokio::sync::mpsc::Sender<(block::Hash, Arc<Block>)>,
234 non_finalized_state: &'a NonFinalizedState,
235 take_cond: impl Fn(&&ContextuallyVerifiedBlock) -> bool + Copy + 'a,
236 ) -> Result<(), tokio::sync::mpsc::error::SendError<(block::Hash, Arc<Block>)>> {
237 let new_blocks = non_finalized_state
238 .chain_iter()
239 .flat_map(move |chain| {
240 // Take blocks from the chain in reverse height order until we reach a block the
241 // listener already has, then restore ascending height order.
242 let mut blocks: Vec<_> =
243 chain.blocks.values().rev().take_while(take_cond).collect();
244 blocks.reverse();
245 blocks
246 })
247 .map(|cv_block| (cv_block.hash, cv_block.block.clone()));
248
249 for new_block_with_hash in new_blocks {
250 sender.send(new_block_with_hash).await?;
251 }
252
253 Ok(())
254 }
255
256 /// Spawns a task to listen for changes in the non-finalized state and sends any blocks in the non-finalized state
257 /// to the caller that have not already been sent.
258 ///
259 /// `known_chain_tips` holds the hashes of chain tips the caller already has. On the first send
260 /// only, each non-finalized chain is walked from its tip downwards and blocks are sent until a
261 /// hash in this set is reached, so any block at or below a known tip on the same chain is
262 /// skipped. If it is empty, every block currently in the non-finalized state is sent. After the
263 /// first send, those blocks are already tracked as sent, so later sends only forward blocks that
264 /// weren't in the previously seen non-finalized state.
265 ///
266 /// Returns a new instance of [`NonFinalizedBlocksListener`] for the caller to listen for new blocks in the non-finalized state.
267 pub fn spawn(
268 mut non_finalized_state_receiver: WatchReceiver<NonFinalizedState>,
269 known_chain_tips: HashSet<block::Hash>,
270 ) -> Self {
271 let (sender, receiver) = tokio::sync::mpsc::channel(NON_FINALIZED_STATE_CHANGE_BUFFER_SIZE);
272
273 tokio::spawn(async move {
274 // `prev_non_finalized_state` starts as the current non-finalized state. The first send
275 // below skips blocks at or below the caller's known chain tips; afterwards those blocks
276 // are already in `prev_non_finalized_state`, so later sends only need to check it.
277 let mut prev_non_finalized_state = non_finalized_state_receiver.cloned_watch_data();
278
279 // Send the blocks the caller is missing relative to its known chain tips. This checks
280 // `known_chain_tips` once; from here on those blocks are covered by the state check.
281 if Self::take_and_send_blocks(&sender, &prev_non_finalized_state, |b| {
282 !known_chain_tips.contains(&b.hash)
283 })
284 .await
285 .is_err()
286 {
287 tracing::debug!("non-finalized blocks receiver closed, ending task");
288 return;
289 }
290
291 // # Correctness
292 //
293 // This loop should check that the non-finalized state receiver has changed sooner
294 // than the non-finalized state could possibly have changed to avoid missing updates, so
295 // the logic here should be quicker than the contextual verification logic that precedes
296 // commits to the non-finalized state.
297 //
298 // See the `NON_FINALIZED_STATE_CHANGE_BUFFER_SIZE` documentation for more details.
299 loop {
300 let non_finalized_state = non_finalized_state_receiver.cloned_watch_data();
301
302 // Send blocks that weren't in the last seen copy of the non-finalized state. The
303 // caller's known tips are already covered by `prev_non_finalized_state`.
304 if Self::take_and_send_blocks(&sender, &non_finalized_state, |b| {
305 !prev_non_finalized_state.any_chain_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 prev_non_finalized_state = non_finalized_state;
315
316 // Wait for the next update to the non-finalized state.
317 if let Err(error) = non_finalized_state_receiver.changed().await {
318 warn!(
319 ?error,
320 "non-finalized state receiver closed, is Zebra shutting down?"
321 );
322 break;
323 }
324 }
325 });
326
327 Self(Arc::new(receiver))
328 }
329
330 /// Consumes `self`, unwrapping the inner [`Arc`] and returning the non-finalized state change channel receiver.
331 ///
332 /// # Panics
333 ///
334 /// If the `Arc` has more than one strong reference, this will panic.
335 pub fn unwrap(
336 self,
337 ) -> tokio::sync::mpsc::Receiver<(zebra_chain::block::Hash, Arc<zebra_chain::block::Block>)>
338 {
339 Arc::try_unwrap(self.0).unwrap()
340 }
341}
342
343impl PartialEq for NonFinalizedBlocksListener {
344 fn eq(&self, other: &Self) -> bool {
345 Arc::ptr_eq(&self.0, &other.0)
346 }
347}
348
349impl Eq for NonFinalizedBlocksListener {}
350
351#[derive(Clone, Debug, PartialEq, Eq)]
352/// A response to a read-only
353/// [`ReadStateService`](crate::service::ReadStateService)'s [`ReadRequest`].
354pub enum ReadResponse {
355 /// Response to [`ReadRequest::UsageInfo`] with the current best chain tip.
356 UsageInfo(u64),
357
358 /// Response to [`ReadRequest::Tip`] with the current best chain tip.
359 Tip(Option<(block::Height, block::Hash)>),
360
361 /// Response to [`ReadRequest::TipPoolValues`] with
362 /// the current best chain tip and its [`ValueBalance`].
363 TipPoolValues {
364 /// The current best chain tip height.
365 tip_height: block::Height,
366 /// The current best chain tip hash.
367 tip_hash: block::Hash,
368 /// The value pool balance at the current best chain tip.
369 value_balance: ValueBalance<NonNegative>,
370 },
371
372 /// Response to [`ReadRequest::BlockInfo`] with
373 /// the block info after the specified block.
374 BlockInfo(Option<BlockInfo>),
375
376 /// Response to [`ReadRequest::Depth`] with the depth of the specified block.
377 Depth(Option<u32>),
378
379 /// Response to [`ReadRequest::Block`] with the specified block.
380 Block(Option<Arc<Block>>),
381
382 /// Response to [`ReadRequest::BlockAndSize`] with the specified block and
383 /// serialized size.
384 BlockAndSize(Option<(Arc<Block>, usize)>),
385
386 /// The response to a `BlockHeader` request.
387 BlockHeader {
388 /// The header of the requested block
389 header: Arc<block::Header>,
390 /// The hash of the requested block
391 hash: block::Hash,
392 /// The height of the requested block
393 height: block::Height,
394 /// The hash of the next block after the requested block
395 next_block_hash: Option<block::Hash>,
396 },
397
398 /// Response to [`ReadRequest::Transaction`] with the specified transaction.
399 Transaction(Option<MinedTx>),
400
401 /// Response to [`Request::Transaction`] with the specified transaction.
402 AnyChainTransaction(Option<AnyTx>),
403
404 /// Response to [`ReadRequest::TransactionIdsForBlock`],
405 /// with an list of transaction hashes in block order,
406 /// or `None` if the block was not found.
407 TransactionIdsForBlock(Option<Arc<[transaction::Hash]>>),
408
409 /// Response to [`ReadRequest::AnyChainTransactionIdsForBlock`], with an list of
410 /// transaction hashes in block order and a flag indicating if the block is
411 /// in the best chain, or `None` if the block was not found.
412 AnyChainTransactionIdsForBlock(Option<(Arc<[transaction::Hash]>, bool)>),
413
414 /// Response to [`ReadRequest::SpendingTransactionId`],
415 /// with an list of transaction hashes in block order,
416 /// or `None` if the block was not found.
417 #[cfg(feature = "indexer")]
418 TransactionId(Option<transaction::Hash>),
419
420 /// Response to [`ReadRequest::BlockLocator`] with a block locator object.
421 BlockLocator(Vec<block::Hash>),
422
423 /// The response to a `FindBlockHashes` request.
424 BlockHashes(Vec<block::Hash>),
425
426 /// The response to a `FindBlockHeaders` request.
427 BlockHeaders(Vec<block::CountedHeader>),
428
429 /// The response to a `FindForkPoint` request.
430 /// Returns the height and hash of the fork point, or `None` if no locator entry is
431 /// on the best chain.
432 ForkPoint(Option<(block::Height, block::Hash)>),
433
434 /// The response to a `UnspentBestChainUtxo` request, from verified blocks in the
435 /// _best_ non-finalized chain, or the finalized chain.
436 UnspentBestChainUtxo(Option<transparent::Utxo>),
437
438 /// The response to an `AnyChainUtxo` request, from verified blocks in
439 /// _any_ non-finalized chain, or the finalized chain.
440 ///
441 /// This response is purely informational, there is no guarantee that
442 /// the UTXO remains unspent in the best chain.
443 AnyChainUtxo(Option<transparent::Utxo>),
444
445 /// Response to [`ReadRequest::SaplingTree`] with the specified Sapling note commitment tree.
446 SaplingTree(Option<Arc<sapling::tree::NoteCommitmentTree>>),
447
448 /// Response to [`ReadRequest::OrchardTree`] with the specified Orchard note commitment tree.
449 OrchardTree(Option<Arc<orchard::tree::NoteCommitmentTree>>),
450
451 /// Response to [`ReadRequest::IronwoodTree`] with the specified Ironwood note commitment tree.
452 IronwoodTree(Option<Arc<orchard::tree::NoteCommitmentTree>>),
453
454 /// Response to [`ReadRequest::SaplingSubtrees`] with the specified Sapling note commitment
455 /// subtrees.
456 SaplingSubtrees(
457 BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<sapling_crypto::Node>>,
458 ),
459
460 /// Response to [`ReadRequest::OrchardSubtrees`] with the specified Orchard note commitment
461 /// subtrees.
462 OrchardSubtrees(
463 BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>>,
464 ),
465
466 /// Response to [`ReadRequest::IronwoodSubtrees`] with the specified Ironwood note commitment
467 /// subtrees. Ironwood reuses the Orchard note type.
468 IronwoodSubtrees(
469 BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>>,
470 ),
471
472 /// Response to [`ReadRequest::AddressBalance`] with the total balance of the addresses,
473 /// and the total received funds, including change.
474 AddressBalance {
475 /// The total balance of the addresses.
476 balance: Amount<NonNegative>,
477 /// The total received funds in zatoshis, including change.
478 received: u64,
479 },
480
481 /// Response to [`ReadRequest::TransactionIdsByAddresses`]
482 /// with the obtained transaction ids, in the order they appear in blocks.
483 AddressesTransactionIds(BTreeMap<TransactionLocation, transaction::Hash>),
484
485 /// Response to [`ReadRequest::UtxosByAddresses`] with found utxos and transaction data.
486 AddressUtxos(AddressUtxos),
487
488 /// Response to [`ReadRequest::CheckBestChainTipNullifiersAndAnchors`].
489 ///
490 /// Does not check transparent UTXO inputs
491 ValidBestChainTipNullifiersAndAnchors,
492
493 /// Response to [`ReadRequest::BestChainNextMedianTimePast`].
494 /// Contains the median-time-past for the *next* block on the best chain.
495 BestChainNextMedianTimePast(DateTime32),
496
497 /// Response to [`ReadRequest::BestChainBlockHash`] with the specified block hash.
498 BlockHash(Option<block::Hash>),
499
500 /// Response to [`ReadRequest::ChainInfo`] with the state
501 /// information needed by the `getblocktemplate` RPC method.
502 ChainInfo(GetBlockTemplateChainInfo),
503
504 /// Response to [`ReadRequest::SolutionRate`]
505 SolutionRate(Option<u128>),
506
507 /// Response to [`ReadRequest::CheckBlockProposalValidity`]
508 ValidBlockProposal,
509
510 /// Response to [`ReadRequest::TipBlockSize`]
511 TipBlockSize(Option<usize>),
512
513 /// Response to [`ReadRequest::NonFinalizedBlocksListener`]
514 NonFinalizedBlocksListener(NonFinalizedBlocksListener),
515
516 /// Response to [`ReadRequest::IsTransparentOutputSpent`]
517 IsTransparentOutputSpent(bool),
518}
519
520/// A structure with the information needed from the state to build a `getblocktemplate` RPC response.
521#[derive(Clone, Debug, Eq, PartialEq)]
522pub struct GetBlockTemplateChainInfo {
523 // Data fetched directly from the state tip.
524 //
525 /// The current state tip height.
526 /// The block template for the candidate block has this hash as the previous block hash.
527 pub tip_hash: block::Hash,
528
529 /// The current state tip height.
530 /// The block template for the candidate block is the next block after this block.
531 /// Depends on the `tip_hash`.
532 pub tip_height: block::Height,
533
534 /// The FlyClient chain history root as of the end of the chain tip block.
535 /// Depends on the `tip_hash`.
536 pub chain_history_root: Option<ChainHistoryMmrRootHash>,
537
538 // Data derived from the state tip and recent blocks, and the current local clock.
539 //
540 /// The expected difficulty of the candidate block.
541 /// Depends on the `tip_hash`, and the local clock on testnet.
542 pub expected_difficulty: CompactDifficulty,
543
544 /// The current system time, adjusted to fit within `min_time` and `max_time`.
545 /// Always depends on the local clock and the `tip_hash`.
546 pub cur_time: DateTime32,
547
548 /// The mininimum time the miner can use in this block.
549 /// Depends on the `tip_hash`, and the local clock on testnet.
550 pub min_time: DateTime32,
551
552 /// The maximum time the miner can use in this block.
553 /// Depends on the `tip_hash`, and the local clock on testnet.
554 pub max_time: DateTime32,
555}
556
557/// Conversion from read-only [`ReadResponse`]s to read-write [`Response`]s.
558///
559/// Used to return read requests concurrently from the [`StateService`](crate::service::StateService).
560impl TryFrom<ReadResponse> for Response {
561 type Error = &'static str;
562
563 fn try_from(response: ReadResponse) -> Result<Response, Self::Error> {
564 match response {
565 ReadResponse::Tip(height_and_hash) => Ok(Response::Tip(height_and_hash)),
566 ReadResponse::Depth(depth) => Ok(Response::Depth(depth)),
567 ReadResponse::BestChainNextMedianTimePast(median_time_past) => Ok(Response::BestChainNextMedianTimePast(median_time_past)),
568 ReadResponse::BlockHash(hash) => Ok(Response::BlockHash(hash)),
569
570 ReadResponse::Block(block) => Ok(Response::Block(block)),
571 ReadResponse::BlockAndSize(block) => Ok(Response::BlockAndSize(block)),
572 ReadResponse::BlockHeader {
573 header,
574 hash,
575 height,
576 next_block_hash
577 } => Ok(Response::BlockHeader {
578 header,
579 hash,
580 height,
581 next_block_hash
582 }),
583 ReadResponse::Transaction(tx_info) => {
584 Ok(Response::Transaction(tx_info.map(|tx_info| tx_info.tx)))
585 }
586 ReadResponse::AnyChainTransaction(tx) => Ok(Response::AnyChainTransaction(tx)),
587 ReadResponse::UnspentBestChainUtxo(utxo) => Ok(Response::UnspentBestChainUtxo(utxo)),
588
589
590 ReadResponse::AnyChainUtxo(_) => Err("ReadService does not track pending UTXOs. \
591 Manually unwrap the response, and handle pending UTXOs."),
592
593 ReadResponse::BlockLocator(hashes) => Ok(Response::BlockLocator(hashes)),
594 ReadResponse::BlockHashes(hashes) => Ok(Response::BlockHashes(hashes)),
595 ReadResponse::BlockHeaders(headers) => Ok(Response::BlockHeaders(headers)),
596
597 ReadResponse::ValidBestChainTipNullifiersAndAnchors => Ok(Response::ValidBestChainTipNullifiersAndAnchors),
598
599 ReadResponse::UsageInfo(_)
600 | ReadResponse::TipPoolValues { .. }
601 | ReadResponse::BlockInfo(_)
602 | ReadResponse::TransactionIdsForBlock(_)
603 | ReadResponse::AnyChainTransactionIdsForBlock(_)
604 | ReadResponse::SaplingTree(_)
605 | ReadResponse::OrchardTree(_)
606 | ReadResponse::IronwoodTree(_)
607 | ReadResponse::SaplingSubtrees(_)
608 | ReadResponse::OrchardSubtrees(_)
609 | ReadResponse::IronwoodSubtrees(_)
610 | ReadResponse::AddressBalance { .. }
611 | ReadResponse::AddressesTransactionIds(_)
612 | ReadResponse::AddressUtxos(_)
613 | ReadResponse::ChainInfo(_)
614 | ReadResponse::NonFinalizedBlocksListener(_)
615 | ReadResponse::IsTransparentOutputSpent(_)
616 | ReadResponse::ForkPoint(_) => {
617 Err("there is no corresponding Response for this ReadResponse")
618 }
619
620 #[cfg(feature = "indexer")]
621 ReadResponse::TransactionId(_) => Err("there is no corresponding Response for this ReadResponse"),
622
623 ReadResponse::ValidBlockProposal => Ok(Response::ValidBlockProposal),
624
625 ReadResponse::SolutionRate(_) | ReadResponse::TipBlockSize(_) => {
626 Err("there is no corresponding Response for this ReadResponse")
627 }
628 }
629 }
630}