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