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