zebra_state/
response.rs

1//! State [`tower::Service`] response types.
2
3use std::{collections::BTreeMap, sync::Arc};
4
5use zebra_chain::{
6    amount::{Amount, NonNegative},
7    block::{self, Block},
8    orchard, sapling,
9    serialization::DateTime32,
10    subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
11    transaction::{self, Transaction},
12    transparent,
13    value_balance::ValueBalance,
14};
15
16use zebra_chain::work::difficulty::CompactDifficulty;
17
18// Allow *only* these unused imports, so that rustdoc link resolution
19// will work with inline links.
20#[allow(unused_imports)]
21use crate::{ReadRequest, Request};
22
23use crate::{service::read::AddressUtxos, TransactionLocation};
24
25#[derive(Clone, Debug, PartialEq, Eq)]
26/// A response to a [`StateService`](crate::service::StateService) [`Request`].
27pub enum Response {
28    /// Response to [`Request::CommitSemanticallyVerifiedBlock`] indicating that a block was
29    /// successfully committed to the state.
30    Committed(block::Hash),
31
32    /// Response to [`Request::Depth`] with the depth of the specified block.
33    Depth(Option<u32>),
34
35    /// Response to [`Request::Tip`] with the current best chain tip.
36    //
37    // TODO: remove this request, and replace it with a call to
38    //       `LatestChainTip::best_tip_height_and_hash()`
39    Tip(Option<(block::Height, block::Hash)>),
40
41    /// Response to [`Request::BlockLocator`] with a block locator object.
42    BlockLocator(Vec<block::Hash>),
43
44    /// Response to [`Request::Transaction`] with the specified transaction.
45    Transaction(Option<Arc<Transaction>>),
46
47    /// Response to [`Request::UnspentBestChainUtxo`] with the UTXO
48    UnspentBestChainUtxo(Option<transparent::Utxo>),
49
50    /// Response to [`Request::Block`] with the specified block.
51    Block(Option<Arc<Block>>),
52
53    /// Response to [`Request::BlockAndSize`] with the specified block and size.
54    BlockAndSize(Option<(Arc<Block>, usize)>),
55
56    /// The response to a `BlockHeader` request.
57    BlockHeader {
58        /// The header of the requested block
59        header: Arc<block::Header>,
60        /// The hash of the requested block
61        hash: block::Hash,
62        /// The height of the requested block
63        height: block::Height,
64        /// The hash of the next block after the requested block
65        next_block_hash: Option<block::Hash>,
66    },
67
68    /// The response to a `AwaitUtxo` request, from any non-finalized chains, finalized chain,
69    /// pending unverified blocks, or blocks received after the request was sent.
70    Utxo(transparent::Utxo),
71
72    /// The response to a `FindBlockHashes` request.
73    BlockHashes(Vec<block::Hash>),
74
75    /// The response to a `FindBlockHeaders` request.
76    BlockHeaders(Vec<block::CountedHeader>),
77
78    /// Response to [`Request::CheckBestChainTipNullifiersAndAnchors`].
79    ///
80    /// Does not check transparent UTXO inputs
81    ValidBestChainTipNullifiersAndAnchors,
82
83    /// Response to [`Request::BestChainNextMedianTimePast`].
84    /// Contains the median-time-past for the *next* block on the best chain.
85    BestChainNextMedianTimePast(DateTime32),
86
87    /// Response to [`Request::BestChainBlockHash`] with the specified block hash.
88    BlockHash(Option<block::Hash>),
89
90    /// Response to [`Request::KnownBlock`].
91    KnownBlock(Option<KnownBlock>),
92
93    /// Response to [`Request::CheckBlockProposalValidity`]
94    ValidBlockProposal,
95}
96
97#[derive(Clone, Debug, PartialEq, Eq)]
98/// An enum of block stores in the state where a block hash could be found.
99pub enum KnownBlock {
100    /// Block is in the best chain.
101    BestChain,
102
103    /// Block is in a side chain.
104    SideChain,
105
106    /// Block is queued to be validated and committed, or rejected and dropped.
107    Queue,
108}
109
110/// Information about a transaction in the best chain
111#[derive(Clone, Debug, PartialEq, Eq)]
112pub struct MinedTx {
113    /// The transaction.
114    pub tx: Arc<Transaction>,
115
116    /// The transaction height.
117    pub height: block::Height,
118
119    /// The number of confirmations for this transaction
120    /// (1 + depth of block the transaction was found in)
121    pub confirmations: u32,
122}
123
124impl MinedTx {
125    /// Creates a new [`MinedTx`]
126    pub fn new(tx: Arc<Transaction>, height: block::Height, confirmations: u32) -> Self {
127        Self {
128            tx,
129            height,
130            confirmations,
131        }
132    }
133}
134
135#[derive(Clone, Debug, PartialEq, Eq)]
136/// A response to a read-only
137/// [`ReadStateService`](crate::service::ReadStateService)'s [`ReadRequest`].
138pub enum ReadResponse {
139    /// Response to [`ReadRequest::UsageInfo`] with the current best chain tip.
140    UsageInfo(u64),
141
142    /// Response to [`ReadRequest::Tip`] with the current best chain tip.
143    Tip(Option<(block::Height, block::Hash)>),
144
145    /// Response to [`ReadRequest::TipPoolValues`] with
146    /// the current best chain tip and its [`ValueBalance`].
147    TipPoolValues {
148        /// The current best chain tip height.
149        tip_height: block::Height,
150        /// The current best chain tip hash.
151        tip_hash: block::Hash,
152        /// The value pool balance at the current best chain tip.
153        value_balance: ValueBalance<NonNegative>,
154    },
155
156    /// Response to [`ReadRequest::Depth`] with the depth of the specified block.
157    Depth(Option<u32>),
158
159    /// Response to [`ReadRequest::Block`] with the specified block.
160    Block(Option<Arc<Block>>),
161
162    /// Response to [`ReadRequest::BlockAndSize`] with the specified block and
163    /// serialized size.
164    BlockAndSize(Option<(Arc<Block>, usize)>),
165
166    /// The response to a `BlockHeader` request.
167    BlockHeader {
168        /// The header of the requested block
169        header: Arc<block::Header>,
170        /// The hash of the requested block
171        hash: block::Hash,
172        /// The height of the requested block
173        height: block::Height,
174        /// The hash of the next block after the requested block
175        next_block_hash: Option<block::Hash>,
176    },
177
178    /// Response to [`ReadRequest::Transaction`] with the specified transaction.
179    Transaction(Option<MinedTx>),
180
181    /// Response to [`ReadRequest::TransactionIdsForBlock`],
182    /// with an list of transaction hashes in block order,
183    /// or `None` if the block was not found.
184    TransactionIdsForBlock(Option<Arc<[transaction::Hash]>>),
185
186    /// Response to [`ReadRequest::SpendingTransactionId`],
187    /// with an list of transaction hashes in block order,
188    /// or `None` if the block was not found.
189    #[cfg(feature = "indexer")]
190    TransactionId(Option<transaction::Hash>),
191
192    /// Response to [`ReadRequest::BlockLocator`] with a block locator object.
193    BlockLocator(Vec<block::Hash>),
194
195    /// The response to a `FindBlockHashes` request.
196    BlockHashes(Vec<block::Hash>),
197
198    /// The response to a `FindBlockHeaders` request.
199    BlockHeaders(Vec<block::CountedHeader>),
200
201    /// The response to a `UnspentBestChainUtxo` request, from verified blocks in the
202    /// _best_ non-finalized chain, or the finalized chain.
203    UnspentBestChainUtxo(Option<transparent::Utxo>),
204
205    /// The response to an `AnyChainUtxo` request, from verified blocks in
206    /// _any_ non-finalized chain, or the finalized chain.
207    ///
208    /// This response is purely informational, there is no guarantee that
209    /// the UTXO remains unspent in the best chain.
210    AnyChainUtxo(Option<transparent::Utxo>),
211
212    /// Response to [`ReadRequest::SaplingTree`] with the specified Sapling note commitment tree.
213    SaplingTree(Option<Arc<sapling::tree::NoteCommitmentTree>>),
214
215    /// Response to [`ReadRequest::OrchardTree`] with the specified Orchard note commitment tree.
216    OrchardTree(Option<Arc<orchard::tree::NoteCommitmentTree>>),
217
218    /// Response to [`ReadRequest::SaplingSubtrees`] with the specified Sapling note commitment
219    /// subtrees.
220    SaplingSubtrees(
221        BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<sapling::tree::Node>>,
222    ),
223
224    /// Response to [`ReadRequest::OrchardSubtrees`] with the specified Orchard note commitment
225    /// subtrees.
226    OrchardSubtrees(
227        BTreeMap<NoteCommitmentSubtreeIndex, NoteCommitmentSubtreeData<orchard::tree::Node>>,
228    ),
229
230    /// Response to [`ReadRequest::AddressBalance`] with the total balance of the addresses.
231    AddressBalance(Amount<NonNegative>),
232
233    /// Response to [`ReadRequest::TransactionIdsByAddresses`]
234    /// with the obtained transaction ids, in the order they appear in blocks.
235    AddressesTransactionIds(BTreeMap<TransactionLocation, transaction::Hash>),
236
237    /// Response to [`ReadRequest::UtxosByAddresses`] with found utxos and transaction data.
238    AddressUtxos(AddressUtxos),
239
240    /// Response to [`ReadRequest::CheckBestChainTipNullifiersAndAnchors`].
241    ///
242    /// Does not check transparent UTXO inputs
243    ValidBestChainTipNullifiersAndAnchors,
244
245    /// Response to [`ReadRequest::BestChainNextMedianTimePast`].
246    /// Contains the median-time-past for the *next* block on the best chain.
247    BestChainNextMedianTimePast(DateTime32),
248
249    /// Response to [`ReadRequest::BestChainBlockHash`] with the specified block hash.
250    BlockHash(Option<block::Hash>),
251
252    /// Response to [`ReadRequest::ChainInfo`] with the state
253    /// information needed by the `getblocktemplate` RPC method.
254    ChainInfo(GetBlockTemplateChainInfo),
255
256    /// Response to [`ReadRequest::SolutionRate`]
257    SolutionRate(Option<u128>),
258
259    /// Response to [`ReadRequest::CheckBlockProposalValidity`]
260    ValidBlockProposal,
261
262    /// Response to [`ReadRequest::TipBlockSize`]
263    TipBlockSize(Option<usize>),
264}
265
266/// A structure with the information needed from the state to build a `getblocktemplate` RPC response.
267#[derive(Clone, Debug, Eq, PartialEq)]
268pub struct GetBlockTemplateChainInfo {
269    // Data fetched directly from the state tip.
270    //
271    /// The current state tip height.
272    /// The block template for the candidate block has this hash as the previous block hash.
273    pub tip_hash: block::Hash,
274
275    /// The current state tip height.
276    /// The block template for the candidate block is the next block after this block.
277    /// Depends on the `tip_hash`.
278    pub tip_height: block::Height,
279
280    /// The history tree of the current best chain.
281    /// Depends on the `tip_hash`.
282    pub history_tree: Arc<zebra_chain::history_tree::HistoryTree>,
283
284    // Data derived from the state tip and recent blocks, and the current local clock.
285    //
286    /// The expected difficulty of the candidate block.
287    /// Depends on the `tip_hash`, and the local clock on testnet.
288    pub expected_difficulty: CompactDifficulty,
289
290    /// The current system time, adjusted to fit within `min_time` and `max_time`.
291    /// Always depends on the local clock and the `tip_hash`.
292    pub cur_time: DateTime32,
293
294    /// The mininimum time the miner can use in this block.
295    /// Depends on the `tip_hash`, and the local clock on testnet.
296    pub min_time: DateTime32,
297
298    /// The maximum time the miner can use in this block.
299    /// Depends on the `tip_hash`, and the local clock on testnet.
300    pub max_time: DateTime32,
301}
302
303/// Conversion from read-only [`ReadResponse`]s to read-write [`Response`]s.
304///
305/// Used to return read requests concurrently from the [`StateService`](crate::service::StateService).
306impl TryFrom<ReadResponse> for Response {
307    type Error = &'static str;
308
309    fn try_from(response: ReadResponse) -> Result<Response, Self::Error> {
310        match response {
311            ReadResponse::Tip(height_and_hash) => Ok(Response::Tip(height_and_hash)),
312            ReadResponse::Depth(depth) => Ok(Response::Depth(depth)),
313            ReadResponse::BestChainNextMedianTimePast(median_time_past) => Ok(Response::BestChainNextMedianTimePast(median_time_past)),
314            ReadResponse::BlockHash(hash) => Ok(Response::BlockHash(hash)),
315
316            ReadResponse::Block(block) => Ok(Response::Block(block)),
317            ReadResponse::BlockAndSize(block) => Ok(Response::BlockAndSize(block)),
318            ReadResponse::BlockHeader {
319                header,
320                hash,
321                height,
322                next_block_hash
323            } => Ok(Response::BlockHeader {
324                header,
325                hash,
326                height,
327                next_block_hash
328            }),
329            ReadResponse::Transaction(tx_info) => {
330                Ok(Response::Transaction(tx_info.map(|tx_info| tx_info.tx)))
331            }
332            ReadResponse::UnspentBestChainUtxo(utxo) => Ok(Response::UnspentBestChainUtxo(utxo)),
333
334
335            ReadResponse::AnyChainUtxo(_) => Err("ReadService does not track pending UTXOs. \
336                                                  Manually unwrap the response, and handle pending UTXOs."),
337
338            ReadResponse::BlockLocator(hashes) => Ok(Response::BlockLocator(hashes)),
339            ReadResponse::BlockHashes(hashes) => Ok(Response::BlockHashes(hashes)),
340            ReadResponse::BlockHeaders(headers) => Ok(Response::BlockHeaders(headers)),
341
342            ReadResponse::ValidBestChainTipNullifiersAndAnchors => Ok(Response::ValidBestChainTipNullifiersAndAnchors),
343
344            ReadResponse::UsageInfo(_)
345            | ReadResponse::TipPoolValues { .. }
346            | ReadResponse::TransactionIdsForBlock(_)
347            | ReadResponse::SaplingTree(_)
348            | ReadResponse::OrchardTree(_)
349            | ReadResponse::SaplingSubtrees(_)
350            | ReadResponse::OrchardSubtrees(_)
351            | ReadResponse::AddressBalance(_)
352            | ReadResponse::AddressesTransactionIds(_)
353            | ReadResponse::AddressUtxos(_)
354            | ReadResponse::ChainInfo(_) => {
355                Err("there is no corresponding Response for this ReadResponse")
356            }
357
358            #[cfg(feature = "indexer")]
359            ReadResponse::TransactionId(_) => Err("there is no corresponding Response for this ReadResponse"),
360
361            ReadResponse::ValidBlockProposal => Ok(Response::ValidBlockProposal),
362
363            ReadResponse::SolutionRate(_) | ReadResponse::TipBlockSize(_) => {
364                Err("there is no corresponding Response for this ReadResponse")
365            }
366        }
367    }
368}