zebra_state/
response.rs

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