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