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}