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