hotshot_query_service/explorer/
query_data.rs

1// Copyright (c) 2022 Espresso Systems (espressosys.com)
2// This file is part of the HotShot Query Service library.
3//
4// This program is free software: you can redistribute it and/or modify it under the terms of the GNU
5// General Public License as published by the Free Software Foundation, either version 3 of the
6// License, or (at your option) any later version.
7// This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
8// even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
9// General Public License for more details.
10// You should have received a copy of the GNU General Public License along with this program. If not,
11// see <https://www.gnu.org/licenses/>.
12
13use std::{
14    collections::VecDeque,
15    fmt::{Debug, Display},
16    num::{NonZeroUsize, TryFromIntError},
17};
18
19use hotshot_types::traits::{block_contents::BlockHeader, node_implementation::NodeType};
20use serde::{Deserialize, Serialize};
21use tide_disco::StatusCode;
22use time::format_description::well_known::Rfc3339;
23
24use super::{
25    errors::{BadQuery, ExplorerAPIError, InvalidLimit, NotFound, QueryError, Unimplemented},
26    monetary_value::MonetaryValue,
27    traits::{ExplorerHeader, ExplorerTransaction},
28};
29use crate::{
30    Header, Payload, Resolvable, Transaction,
31    availability::{
32        BlockQueryData, NamespaceId, QueryableHeader, QueryablePayload, TransactionHash,
33    },
34    node::BlockHash,
35    types::HeightIndexed,
36};
37
38/// BlockIdentifier is an enum that represents multiple ways of referring to
39/// a specific Block.  These use cases are specific to a Block Explorer and
40/// can be used to reference a specific individual block.
41///
42/// Any identifier specified here is not guaranteed to be valid, and may not
43/// guarantee that a Block can actually be identified with the information
44/// provided.
45#[derive(Debug, Clone, PartialEq, Eq)]
46pub enum BlockIdentifier<Types: NodeType> {
47    Latest,
48    Height(usize),
49    Hash(BlockHash<Types>),
50}
51
52impl<Types: NodeType> Display for BlockIdentifier<Types> {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        match self {
55            BlockIdentifier::Latest => write!(f, "latest"),
56            BlockIdentifier::Height(height) => write!(f, "{height}"),
57            BlockIdentifier::Hash(hash) => write!(f, "{hash}"),
58        }
59    }
60}
61
62/// TransactionIdentifier is an enum that represents multiple ways of of
63/// identifying a specific Transaction.  These use cases are specific to a
64/// Block Explorer and can be used to **ideally** uniquely identify a
65/// `Transaction` within the Block Chain.
66///
67/// Any identified specified here is not guaranteed to actually point to a
68/// transaction, and does not guarantee that a transaction with the specified
69/// identification actually exists.
70///
71/// A TransactionHash is not actually guaranteed to point to a unique
72/// transaction at the moment, however we will assume that it does for the
73/// purposes of this API.
74#[derive(Debug, Clone, PartialEq, Eq)]
75pub enum TransactionIdentifier<Types: NodeType> {
76    Latest,
77    HeightAndOffset(usize, usize),
78    Hash(TransactionHash<Types>),
79}
80
81impl<Types: NodeType> Display for TransactionIdentifier<Types> {
82    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
83        match self {
84            TransactionIdentifier::Latest => write!(f, "latest"),
85            TransactionIdentifier::HeightAndOffset(height, offset) => {
86                write!(f, "{height} {offset}")
87            },
88            TransactionIdentifier::Hash(hash) => write!(f, "{hash}"),
89        }
90    }
91}
92
93/// BlockRange is a struct that represents a range for a specific set of
94/// blocks, starting at the given `BlockIdentifier`.
95///
96/// This range is expected to be descending starting and the `target` and
97/// descending toward `0`.
98///
99/// Given a stable and resolved Block Chain this should always refer to the
100/// same set of blocks when the parameters themselves are the same.
101///
102/// If the `num_blocks` is not possible, then this should be considered as
103/// referring to as many `Block`s as are possible.
104#[derive(Debug, Clone, PartialEq, Eq)]
105pub struct BlockRange<Types: NodeType> {
106    pub target: BlockIdentifier<Types>,
107    pub num_blocks: NonZeroUsize,
108}
109
110/// TransactionRange is a struct that represents a range for a specific set of
111/// transactions, starting at the given `TransactionIdentifier`.
112///
113/// This range is expected to be descending starting at the `target` and
114/// descending toward the first transaction in the `Block Chain`.
115///
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub struct TransactionRange<Types: NodeType> {
118    pub target: TransactionIdentifier<Types>,
119    pub num_transactions: NonZeroUsize,
120}
121
122/// [Timestamp] represents a specific point in time that has a possible
123/// offset.
124///
125/// This specific type is utilized in order to ensure that the timestamp is
126/// always serialized in a specific format, specifically as string following
127/// the RFC3339 standard.
128#[derive(Debug, Clone, Copy, PartialEq, Eq)]
129pub struct Timestamp(pub time::OffsetDateTime);
130
131impl Serialize for Timestamp {
132    /// serialize converts the timestamp into a string representation of a
133    /// RFC3339 formatted date.
134    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
135        let formatted = self.0.format(&Rfc3339).map_err(serde::ser::Error::custom)?;
136        formatted.serialize(serializer)
137    }
138}
139
140impl<'de> Deserialize<'de> for Timestamp {
141    /// deserialize converts a string representation of a RFC3339 formatted
142    /// date.
143    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
144        let s = String::deserialize(deserializer)?;
145        let dt = time::OffsetDateTime::parse(&s, &Rfc3339).map_err(serde::de::Error::custom)?;
146        Ok(Timestamp(dt))
147    }
148}
149
150pub type WalletAddress<Types> = <Header<Types> as ExplorerHeader<Types>>::WalletAddress;
151pub type ProposerId<Types> = <Header<Types> as ExplorerHeader<Types>>::ProposerId;
152pub type BalanceAmount<Types> = <Header<Types> as ExplorerHeader<Types>>::BalanceAmount;
153
154/// [BlockDetail] is a struct that represents the details of a specific block
155/// for use in a Block Explorer.
156#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
157#[serde(bound = "")]
158pub struct BlockDetail<Types: NodeType>
159where
160    Header<Types>: ExplorerHeader<Types>,
161{
162    pub hash: BlockHash<Types>,
163    pub height: u64,
164    pub time: Timestamp,
165    pub num_transactions: u64,
166    pub proposer_id: ProposerId<Types>,
167    pub fee_recipient: WalletAddress<Types>,
168    pub size: u64,
169    pub block_reward: Vec<MonetaryValue>,
170}
171
172/// NANOS_PER_MILLI represents the conversion of the milli prefix to the
173/// nano prefix.  This is a constant that can be utilized to easily convert
174/// between the two values.
175const NANOS_PER_MILLI: i128 = 1_000_000;
176
177impl<Types: NodeType> TryFrom<BlockQueryData<Types>> for BlockDetail<Types>
178where
179    BlockQueryData<Types>: HeightIndexed,
180    Payload<Types>: QueryablePayload<Types>,
181    Header<Types>: BlockHeader<Types> + ExplorerHeader<Types>,
182    BalanceAmount<Types>: Into<MonetaryValue>,
183{
184    type Error = TimestampConversionError;
185
186    fn try_from(value: BlockQueryData<Types>) -> Result<Self, Self::Error> {
187        let milliseconds = i128::from(value.header.timestamp_millis());
188
189        Ok(Self {
190            hash: value.hash(),
191            height: value.height(),
192            time: Timestamp(time::OffsetDateTime::from_unix_timestamp_nanos(
193                milliseconds * NANOS_PER_MILLI,
194            )?),
195            num_transactions: value.num_transactions,
196            proposer_id: value.header().proposer_id(),
197            fee_recipient: value.header().fee_info_account(),
198            size: value.size,
199            block_reward: vec![value.header().fee_info_balance().into()],
200        })
201    }
202}
203
204/// [BlockSummary] is a struct that represents a summary overview of a specific
205/// block.  It does not have all of the details of a [BlockDetail], but it is
206/// useful for displaying information in a list of Blocks.
207#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
208#[serde(bound = "")]
209pub struct BlockSummary<Types: NodeType>
210where
211    Header<Types>: ExplorerHeader<Types>,
212{
213    pub hash: BlockHash<Types>,
214    pub height: u64,
215    pub proposer_id: ProposerId<Types>,
216    pub num_transactions: u64,
217    pub size: u64,
218    pub time: Timestamp,
219}
220
221/// [TimestampConversionError] represents an error that has occurred when
222/// attempting to convert a timestamp from a specific format to another.
223/// It is primarily used when attempting to deserialize a [Timestamp] from
224/// its serialized string representation.
225#[derive(Debug, PartialEq, Eq)]
226pub enum TimestampConversionError {
227    TimeError(time::error::ComponentRange),
228    IntError(TryFromIntError),
229}
230
231impl Display for TimestampConversionError {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        match self {
234            TimestampConversionError::TimeError(err) => write!(f, "{err:?}"),
235            TimestampConversionError::IntError(err) => write!(f, "{err:?}"),
236        }
237    }
238}
239
240impl std::error::Error for TimestampConversionError {
241    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
242        match self {
243            TimestampConversionError::TimeError(err) => Some(err),
244            TimestampConversionError::IntError(err) => Some(err),
245        }
246    }
247}
248
249impl From<time::error::ComponentRange> for TimestampConversionError {
250    fn from(value: time::error::ComponentRange) -> Self {
251        Self::TimeError(value)
252    }
253}
254
255impl From<TryFromIntError> for TimestampConversionError {
256    fn from(value: TryFromIntError) -> Self {
257        Self::IntError(value)
258    }
259}
260
261impl From<TimestampConversionError> for crate::QueryError {
262    fn from(value: TimestampConversionError) -> Self {
263        Self::Error {
264            message: format!("{value:?}"),
265        }
266    }
267}
268
269impl<Types: NodeType> TryFrom<BlockQueryData<Types>> for BlockSummary<Types>
270where
271    BlockQueryData<Types>: HeightIndexed,
272    Payload<Types>: QueryablePayload<Types>,
273    Header<Types>: BlockHeader<Types> + ExplorerHeader<Types>,
274{
275    type Error = TimestampConversionError;
276
277    fn try_from(value: BlockQueryData<Types>) -> Result<Self, Self::Error> {
278        let milliseconds = i128::from(value.header.timestamp_millis());
279
280        Ok(Self {
281            hash: value.hash(),
282            height: value.height(),
283            proposer_id: value.header().proposer_id(),
284            num_transactions: value.num_transactions,
285            size: value.size,
286            time: Timestamp(time::OffsetDateTime::from_unix_timestamp_nanos(
287                milliseconds * NANOS_PER_MILLI,
288            )?),
289        })
290    }
291}
292
293/// [FeeAttribution] represents a specific attribution of fees for a specific
294/// purpose.
295///
296/// The current documentation lists attribution as potentially being
297/// accountable for the following entries:
298/// - Sequencer
299/// - DA Layer
300/// - Ethereum Mainnet
301#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
302pub struct FeeAttribution {
303    pub target: String,
304    pub fees: Vec<MonetaryValue>,
305}
306
307/// [TransactionDetail] is a struct that represents the details of a specific
308/// transaction / payload contained within a Block.
309#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
310#[serde(bound = "")]
311pub struct TransactionDetail<Types: NodeType> {
312    pub hash: TransactionHash<Types>,
313    pub height: u64,
314    pub block_confirmed: bool,
315    pub offset: u64,
316    pub num_transactions: u64,
317    pub size: u64,
318    pub time: Timestamp,
319    pub sequencing_fees: Vec<MonetaryValue>,
320    pub fee_details: Vec<FeeAttribution>,
321}
322
323/// [TransactionDetailResponse] is a struct that represents the information
324/// returned concerning a request for a Transaction Detail. It contains the
325/// data payloads separately from the details of the Transaction itself.
326#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
327#[serde(bound = "")]
328pub struct TransactionDetailResponse<Types: NodeType> {
329    pub details: TransactionDetail<Types>,
330    pub data: Vec<Transaction<Types>>,
331}
332
333/// [TransactionSummary] is a struct that represents a summary overview of a
334/// specific transaction / payload contained within a Block. It does not have
335/// all of the details of a [TransactionDetail], but it is useful for displaying
336/// information in a list of Transactions.
337#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
338#[serde(bound = "")]
339pub struct TransactionSummary<Types: NodeType>
340where
341    Header<Types>: ExplorerHeader<Types>,
342{
343    pub hash: TransactionHash<Types>,
344    pub rollups: Vec<NamespaceId<Types>>,
345    pub height: u64,
346    pub offset: u64,
347    pub num_transactions: u64,
348    pub time: Timestamp,
349}
350
351impl<Types: NodeType>
352    TryFrom<(
353        &BlockQueryData<Types>,
354        usize,
355        <Types as NodeType>::Transaction,
356    )> for TransactionSummary<Types>
357where
358    BlockQueryData<Types>: HeightIndexed,
359    Payload<Types>: QueryablePayload<Types>,
360    Header<Types>: QueryableHeader<Types> + ExplorerHeader<Types>,
361    Transaction<Types>: ExplorerTransaction<Types>,
362{
363    type Error = TimestampConversionError;
364
365    fn try_from(
366        (block, offset, transaction): (
367            &BlockQueryData<Types>,
368            usize,
369            <Types as NodeType>::Transaction,
370        ),
371    ) -> Result<Self, Self::Error> {
372        let milliseconds = i128::from(block.header.timestamp_millis());
373
374        Ok(Self {
375            hash: transaction.commitment(),
376            height: block.height(),
377            offset: offset as u64,
378            num_transactions: block.num_transactions,
379            time: Timestamp(time::OffsetDateTime::from_unix_timestamp_nanos(
380                milliseconds * NANOS_PER_MILLI,
381            )?),
382            rollups: vec![transaction.namespace_id()],
383        })
384    }
385}
386
387impl<Types: NodeType>
388    TryFrom<(
389        &BlockQueryData<Types>,
390        usize,
391        <Types as NodeType>::Transaction,
392    )> for TransactionDetailResponse<Types>
393where
394    BlockQueryData<Types>: HeightIndexed,
395    Payload<Types>: QueryablePayload<Types>,
396    Header<Types>: QueryableHeader<Types> + ExplorerHeader<Types>,
397    <Types as NodeType>::Transaction: ExplorerTransaction<Types>,
398{
399    type Error = TimestampConversionError;
400
401    fn try_from(
402        (block, offset, transaction): (
403            &BlockQueryData<Types>,
404            usize,
405            <Types as NodeType>::Transaction,
406        ),
407    ) -> Result<Self, Self::Error> {
408        let milliseconds = i128::from(block.header.timestamp_millis());
409
410        Ok(Self {
411            details: TransactionDetail {
412                hash: transaction.commitment(),
413                height: block.height(),
414                block_confirmed: true,
415                offset: offset as u64,
416                num_transactions: block.num_transactions,
417                size: transaction.payload_size(),
418                time: Timestamp(time::OffsetDateTime::from_unix_timestamp_nanos(
419                    milliseconds * NANOS_PER_MILLI,
420                )?),
421                sequencing_fees: vec![],
422                fee_details: vec![],
423            },
424            data: vec![transaction],
425        })
426    }
427}
428
429/// GetBlockSummariesRequest is a struct that represents an incoming request
430/// for Block Summaries.  This isn't sent on the line, but an endpoint will
431/// be mapped to this struct in order for the request to be processed.
432#[derive(Debug, PartialEq, Eq)]
433pub struct GetBlockSummariesRequest<Types: NodeType>(pub BlockRange<Types>);
434
435/// [TransactionSummaryFilter] represents the various filters that can be
436/// applied when retrieving a list of [TransactionSummary] entries.
437#[derive(Debug, Deserialize, Serialize)]
438#[serde(bound = "")]
439pub enum TransactionSummaryFilter<Types>
440where
441    Types: NodeType,
442    Header<Types>: QueryableHeader<Types>,
443{
444    None,
445    RollUp(NamespaceId<Types>),
446    Block(usize),
447}
448
449/// GetTransactionSummariesRequest is a struct that represents an incoming
450/// request for Transaction Summaries.  This isn't sent on the line, but an
451/// endpoint will be mapped to this struct in order for the request to be
452/// processed.
453#[derive(Debug)]
454pub struct GetTransactionSummariesRequest<Types>
455where
456    Types: NodeType,
457    Header<Types>: QueryableHeader<Types>,
458{
459    pub range: TransactionRange<Types>,
460    pub filter: TransactionSummaryFilter<Types>,
461}
462
463impl<Types> Default for GetTransactionSummariesRequest<Types>
464where
465    Types: NodeType,
466    Header<Types>: QueryableHeader<Types>,
467{
468    fn default() -> Self {
469        Self {
470            range: TransactionRange {
471                target: TransactionIdentifier::Latest,
472                num_transactions: NonZeroUsize::new(20).unwrap(),
473            },
474            filter: TransactionSummaryFilter::None,
475        }
476    }
477}
478
479/// [GenesisOverview] provides a summary overview of the block chain since
480/// it's genesis. At a high level it includes the total number of unique
481/// rollups, transactions, and blocks that are in the block chain.
482#[derive(Debug, Serialize, Deserialize)]
483pub struct GenesisOverview {
484    pub rollups: u64,
485    pub transactions: u64,
486    pub blocks: u64,
487    // pub sequencer_nodes: u64,
488}
489
490/// [ExplorerHistograms] provides a series of data points that can be used to
491/// draw simple histograms for the Block Explorer.  The data returned is meant
492/// to be an optimal packing of the values being returned.
493///
494/// It contains data for the last N blocks, indicated by the length of the
495/// vectors contained within the struct.  All of the vectors **MUST** have the
496/// same length.  The labels of the graph points is the `block_heights` vector.
497/// The remaining data points are the `block_time`, `block_size`, and
498/// `block_transactions` for those `block_heights`.
499#[derive(Debug, Serialize, Deserialize)]
500pub struct ExplorerHistograms {
501    pub block_time: VecDeque<Option<f64>>,
502    pub block_size: VecDeque<Option<u64>>,
503    pub block_transactions: VecDeque<u64>,
504    pub block_heights: VecDeque<u64>,
505}
506
507/// [ExplorerSummary] is a struct that represents an at-a-glance snapshot of
508/// the Block Chain.  It contains some helpful information that can be used
509/// to display a simple health check of the Block Chain.
510///
511/// It contains the latest block for reference, the most recent blocks, the
512/// most recent transactions, some statistics concerning the total number
513/// of elements contained within the chain, and some histograms that can be
514/// used to draw graphs for the Block Explorer.
515#[derive(Debug, Serialize, Deserialize)]
516#[serde(bound = "")]
517pub struct ExplorerSummary<Types: NodeType>
518where
519    Header<Types>: ExplorerHeader<Types>,
520    Transaction<Types>: ExplorerTransaction<Types>,
521{
522    pub latest_block: BlockDetail<Types>,
523    pub genesis_overview: GenesisOverview,
524    pub latest_blocks: Vec<BlockSummary<Types>>,
525    pub latest_transactions: Vec<TransactionSummary<Types>>,
526    //  Most Active Rollups
527    pub histograms: ExplorerHistograms,
528}
529
530/// [SearchResult] is a struct that represents the results of executing a
531/// search query against the chain.  It contains a list of blocks and
532/// transactions that match the search query.
533#[derive(Debug, Serialize, Deserialize)]
534#[serde(bound = "")]
535pub struct SearchResult<Types: NodeType>
536where
537    Header<Types>: ExplorerHeader<Types>,
538    Transaction<Types>: ExplorerTransaction<Types>,
539{
540    pub blocks: Vec<BlockSummary<Types>>,
541    pub transactions: Vec<TransactionSummary<Types>>,
542}
543
544/// [GetBlockDetailError] represents an error that has occurred in response to
545/// the `get_block_detail` request.
546#[derive(Debug, Clone, Serialize, Deserialize)]
547#[serde(untagged)]
548pub enum GetBlockDetailError {
549    Unimplemented(Unimplemented),
550    BlockNotFound(NotFound),
551    QueryError(QueryError),
552}
553
554impl GetBlockDetailError {
555    pub fn status(&self) -> StatusCode {
556        match self {
557            GetBlockDetailError::Unimplemented(err) => err.status(),
558            GetBlockDetailError::QueryError(err) => err.status(),
559            GetBlockDetailError::BlockNotFound(err) => err.status(),
560        }
561    }
562}
563
564impl Display for GetBlockDetailError {
565    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
566        match self {
567            GetBlockDetailError::Unimplemented(err) => write!(f, "{err}"),
568            GetBlockDetailError::QueryError(err) => write!(f, "{err}"),
569            GetBlockDetailError::BlockNotFound(err) => write!(f, "{err}"),
570        }
571    }
572}
573
574impl ExplorerAPIError for GetBlockDetailError {
575    fn code(&self) -> &str {
576        match self {
577            GetBlockDetailError::Unimplemented(err) => err.code(),
578            GetBlockDetailError::QueryError(err) => err.code(),
579            GetBlockDetailError::BlockNotFound(err) => err.code(),
580        }
581    }
582}
583
584impl std::error::Error for GetBlockDetailError {
585    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
586        match self {
587            GetBlockDetailError::Unimplemented(err) => Some(err),
588            GetBlockDetailError::QueryError(err) => Some(err),
589            _ => None,
590        }
591    }
592}
593
594impl From<crate::QueryError> for GetBlockDetailError {
595    fn from(value: crate::QueryError) -> Self {
596        GetBlockDetailError::QueryError(QueryError { error: value })
597    }
598}
599
600/// [GetBlockSummariesError] represents an error that has occurred in response
601/// to the [GetBlockSummariesRequest] request.
602#[derive(Debug, Clone, Serialize, Deserialize)]
603#[serde(untagged)]
604pub enum GetBlockSummariesError {
605    Unimplemented(Unimplemented),
606    InvalidLimit(InvalidLimit),
607    TargetNotFound(NotFound),
608    QueryError(QueryError),
609}
610
611impl GetBlockSummariesError {
612    pub fn status(&self) -> StatusCode {
613        match self {
614            GetBlockSummariesError::Unimplemented(err) => err.status(),
615            GetBlockSummariesError::InvalidLimit(err) => err.status(),
616            GetBlockSummariesError::QueryError(err) => err.status(),
617            GetBlockSummariesError::TargetNotFound(err) => err.status(),
618        }
619    }
620}
621
622impl Display for GetBlockSummariesError {
623    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
624        match self {
625            GetBlockSummariesError::Unimplemented(err) => write!(f, "{err}"),
626            GetBlockSummariesError::InvalidLimit(err) => write!(f, "{err}"),
627            GetBlockSummariesError::QueryError(err) => write!(f, "{err}"),
628            GetBlockSummariesError::TargetNotFound(err) => write!(f, "{err}"),
629        }
630    }
631}
632
633impl ExplorerAPIError for GetBlockSummariesError {
634    fn code(&self) -> &str {
635        match self {
636            GetBlockSummariesError::Unimplemented(err) => err.code(),
637            GetBlockSummariesError::InvalidLimit(err) => err.code(),
638            GetBlockSummariesError::QueryError(err) => err.code(),
639            GetBlockSummariesError::TargetNotFound(err) => err.code(),
640        }
641    }
642}
643
644impl std::error::Error for GetBlockSummariesError {
645    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
646        match self {
647            GetBlockSummariesError::Unimplemented(err) => Some(err),
648            GetBlockSummariesError::InvalidLimit(err) => Some(err),
649            GetBlockSummariesError::QueryError(err) => Some(err),
650            _ => None,
651        }
652    }
653}
654
655impl From<crate::QueryError> for GetBlockSummariesError {
656    fn from(value: crate::QueryError) -> Self {
657        GetBlockSummariesError::QueryError(QueryError { error: value })
658    }
659}
660
661/// [GetTransactionDetailError] represents an error that has occurred in
662/// response to the `get_transaction_detail` request.
663#[derive(Debug, Clone, Serialize, Deserialize)]
664#[serde(untagged)]
665pub enum GetTransactionDetailError {
666    Unimplemented(Unimplemented),
667    TransactionNotFound(NotFound),
668    QueryError(QueryError),
669}
670
671impl GetTransactionDetailError {
672    pub fn status(&self) -> StatusCode {
673        match self {
674            GetTransactionDetailError::Unimplemented(err) => err.status(),
675            GetTransactionDetailError::QueryError(err) => err.status(),
676            GetTransactionDetailError::TransactionNotFound(err) => err.status(),
677        }
678    }
679}
680
681impl Display for GetTransactionDetailError {
682    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
683        match self {
684            GetTransactionDetailError::Unimplemented(err) => write!(f, "{err}"),
685            GetTransactionDetailError::QueryError(err) => write!(f, "{err}"),
686            GetTransactionDetailError::TransactionNotFound(err) => write!(f, "{err}"),
687        }
688    }
689}
690
691impl ExplorerAPIError for GetTransactionDetailError {
692    fn code(&self) -> &str {
693        match self {
694            GetTransactionDetailError::Unimplemented(err) => err.code(),
695            GetTransactionDetailError::QueryError(err) => err.code(),
696            GetTransactionDetailError::TransactionNotFound(err) => err.code(),
697        }
698    }
699}
700
701impl std::error::Error for GetTransactionDetailError {
702    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
703        match self {
704            GetTransactionDetailError::Unimplemented(err) => Some(err),
705            GetTransactionDetailError::QueryError(err) => Some(err),
706            _ => None,
707        }
708    }
709}
710
711// Implement implicit conversion between these errors for the branch operator.
712
713impl From<crate::QueryError> for GetTransactionDetailError {
714    fn from(value: crate::QueryError) -> Self {
715        GetTransactionDetailError::QueryError(QueryError { error: value })
716    }
717}
718
719impl From<TimestampConversionError> for GetTransactionDetailError {
720    fn from(value: TimestampConversionError) -> Self {
721        GetTransactionDetailError::QueryError(QueryError {
722            error: value.into(),
723        })
724    }
725}
726
727/// [GetTransactionSummariesError] represents an error that has occurred in
728/// response to the [GetTransactionSummariesRequest] request.
729#[derive(Debug, Clone, Serialize, Deserialize)]
730#[serde(untagged)]
731pub enum GetTransactionSummariesError {
732    Unimplemented(Unimplemented),
733    InvalidLimit(InvalidLimit),
734    TargetNotFound(NotFound),
735    QueryError(QueryError),
736}
737
738impl GetTransactionSummariesError {
739    pub fn status(&self) -> StatusCode {
740        match self {
741            GetTransactionSummariesError::Unimplemented(err) => err.status(),
742            GetTransactionSummariesError::InvalidLimit(err) => err.status(),
743            GetTransactionSummariesError::QueryError(err) => err.status(),
744            GetTransactionSummariesError::TargetNotFound(err) => err.status(),
745        }
746    }
747}
748
749impl Display for GetTransactionSummariesError {
750    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
751        match self {
752            GetTransactionSummariesError::Unimplemented(err) => write!(f, "{err}"),
753            GetTransactionSummariesError::InvalidLimit(err) => write!(f, "{err}"),
754            GetTransactionSummariesError::QueryError(err) => write!(f, "{err}"),
755            GetTransactionSummariesError::TargetNotFound(err) => write!(f, "{err}"),
756        }
757    }
758}
759
760impl ExplorerAPIError for GetTransactionSummariesError {
761    fn code(&self) -> &str {
762        match self {
763            GetTransactionSummariesError::Unimplemented(err) => err.code(),
764            GetTransactionSummariesError::InvalidLimit(err) => err.code(),
765            GetTransactionSummariesError::QueryError(err) => err.code(),
766            GetTransactionSummariesError::TargetNotFound(err) => err.code(),
767        }
768    }
769}
770
771impl std::error::Error for GetTransactionSummariesError {
772    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
773        match self {
774            GetTransactionSummariesError::Unimplemented(err) => Some(err),
775            GetTransactionSummariesError::InvalidLimit(err) => Some(err),
776            GetTransactionSummariesError::QueryError(err) => Some(err),
777            _ => None,
778        }
779    }
780}
781
782impl From<crate::QueryError> for GetTransactionSummariesError {
783    fn from(value: crate::QueryError) -> Self {
784        GetTransactionSummariesError::QueryError(QueryError { error: value })
785    }
786}
787
788/// [GetExplorerSummaryError] represents an error that has occurred in response
789/// to the `get_explorer_summary` request.
790#[derive(Debug, Clone, Serialize, Deserialize)]
791#[serde(untagged)]
792pub enum GetExplorerSummaryError {
793    Unimplemented(Unimplemented),
794    QueryError(QueryError),
795    GetBlockDetailError(GetBlockDetailError),
796    GetBlockSummariesError(GetBlockSummariesError),
797    GetTransactionSummariesError(GetTransactionSummariesError),
798}
799
800impl GetExplorerSummaryError {
801    pub fn status(&self) -> StatusCode {
802        match self {
803            GetExplorerSummaryError::QueryError(err) => err.status(),
804            GetExplorerSummaryError::Unimplemented(err) => err.status(),
805            GetExplorerSummaryError::GetBlockDetailError(err) => err.status(),
806            GetExplorerSummaryError::GetBlockSummariesError(err) => err.status(),
807            GetExplorerSummaryError::GetTransactionSummariesError(err) => err.status(),
808        }
809    }
810}
811
812impl Display for GetExplorerSummaryError {
813    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
814        match self {
815            GetExplorerSummaryError::QueryError(err) => write!(f, "{err}"),
816            GetExplorerSummaryError::Unimplemented(err) => write!(f, "{err}"),
817            GetExplorerSummaryError::GetBlockDetailError(err) => write!(f, "{err}"),
818            GetExplorerSummaryError::GetBlockSummariesError(err) => write!(f, "{err}"),
819            GetExplorerSummaryError::GetTransactionSummariesError(err) => write!(f, "{err}"),
820        }
821    }
822}
823
824impl ExplorerAPIError for GetExplorerSummaryError {
825    fn code(&self) -> &str {
826        match self {
827            GetExplorerSummaryError::QueryError(err) => err.code(),
828            GetExplorerSummaryError::Unimplemented(err) => err.code(),
829            GetExplorerSummaryError::GetBlockDetailError(err) => err.code(),
830            GetExplorerSummaryError::GetBlockSummariesError(err) => err.code(),
831            GetExplorerSummaryError::GetTransactionSummariesError(err) => err.code(),
832        }
833    }
834}
835
836impl std::error::Error for GetExplorerSummaryError {
837    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
838        match self {
839            GetExplorerSummaryError::Unimplemented(err) => Some(err),
840            GetExplorerSummaryError::QueryError(err) => Some(err),
841            GetExplorerSummaryError::GetBlockDetailError(err) => Some(err),
842            GetExplorerSummaryError::GetBlockSummariesError(err) => Some(err),
843            GetExplorerSummaryError::GetTransactionSummariesError(err) => Some(err),
844        }
845    }
846}
847
848// Implement implicit conversion between these errors for the branch operator.
849
850impl From<crate::QueryError> for GetExplorerSummaryError {
851    fn from(value: crate::QueryError) -> Self {
852        GetExplorerSummaryError::QueryError(QueryError { error: value })
853    }
854}
855
856impl From<GetBlockDetailError> for GetExplorerSummaryError {
857    fn from(value: GetBlockDetailError) -> Self {
858        GetExplorerSummaryError::GetBlockDetailError(value)
859    }
860}
861
862impl From<GetBlockSummariesError> for GetExplorerSummaryError {
863    fn from(value: GetBlockSummariesError) -> Self {
864        GetExplorerSummaryError::GetBlockSummariesError(value)
865    }
866}
867
868impl From<GetTransactionSummariesError> for GetExplorerSummaryError {
869    fn from(value: GetTransactionSummariesError) -> Self {
870        GetExplorerSummaryError::GetTransactionSummariesError(value)
871    }
872}
873
874/// [GetSearchResultsError] represents an error that has occurred in response
875/// to the `get_search_results` request.
876#[derive(Debug, Clone, Serialize, Deserialize)]
877#[serde(untagged)]
878pub enum GetSearchResultsError {
879    Unimplemented(Unimplemented),
880    QueryError(QueryError),
881    InvalidQuery(BadQuery),
882}
883
884impl GetSearchResultsError {
885    pub fn status(&self) -> StatusCode {
886        match self {
887            GetSearchResultsError::QueryError(err) => err.status(),
888            GetSearchResultsError::Unimplemented(err) => err.status(),
889            GetSearchResultsError::InvalidQuery(err) => err.status(),
890        }
891    }
892}
893
894impl Display for GetSearchResultsError {
895    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
896        match self {
897            GetSearchResultsError::QueryError(err) => write!(f, "{err}"),
898            GetSearchResultsError::Unimplemented(err) => write!(f, "{err}"),
899            GetSearchResultsError::InvalidQuery(err) => write!(f, "{err}"),
900        }
901    }
902}
903
904impl ExplorerAPIError for GetSearchResultsError {
905    fn code(&self) -> &str {
906        match self {
907            GetSearchResultsError::QueryError(err) => err.code(),
908            GetSearchResultsError::Unimplemented(err) => err.code(),
909            GetSearchResultsError::InvalidQuery(err) => err.code(),
910        }
911    }
912}
913
914impl std::error::Error for GetSearchResultsError {
915    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
916        match self {
917            GetSearchResultsError::Unimplemented(err) => Some(err),
918            GetSearchResultsError::QueryError(err) => Some(err),
919            GetSearchResultsError::InvalidQuery(err) => Some(err),
920        }
921    }
922}
923
924impl From<crate::QueryError> for GetSearchResultsError {
925    fn from(value: crate::QueryError) -> Self {
926        GetSearchResultsError::QueryError(QueryError { error: value })
927    }
928}