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    availability::{
31        BlockQueryData, NamespaceId, QueryableHeader, QueryablePayload, TransactionHash,
32    },
33    node::BlockHash,
34    types::HeightIndexed,
35    Header, Payload, Resolvable, Transaction,
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
172impl<Types: NodeType> TryFrom<BlockQueryData<Types>> for BlockDetail<Types>
173where
174    BlockQueryData<Types>: HeightIndexed,
175    Payload<Types>: QueryablePayload<Types>,
176    Header<Types>: BlockHeader<Types> + ExplorerHeader<Types>,
177    BalanceAmount<Types>: Into<MonetaryValue>,
178{
179    type Error = TimestampConversionError;
180
181    fn try_from(value: BlockQueryData<Types>) -> Result<Self, Self::Error> {
182        let seconds = i64::try_from(value.header.timestamp())?;
183
184        Ok(Self {
185            hash: value.hash(),
186            height: value.height(),
187            time: Timestamp(time::OffsetDateTime::from_unix_timestamp(seconds)?),
188            num_transactions: value.num_transactions,
189            proposer_id: value.header().proposer_id(),
190            fee_recipient: value.header().fee_info_account(),
191            size: value.size,
192            block_reward: vec![value.header().fee_info_balance().into()],
193        })
194    }
195}
196
197/// [BlockSummary] is a struct that represents a summary overview of a specific
198/// block.  It does not have all of the details of a [BlockDetail], but it is
199/// useful for displaying information in a list of Blocks.
200#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
201#[serde(bound = "")]
202pub struct BlockSummary<Types: NodeType>
203where
204    Header<Types>: ExplorerHeader<Types>,
205{
206    pub hash: BlockHash<Types>,
207    pub height: u64,
208    pub proposer_id: ProposerId<Types>,
209    pub num_transactions: u64,
210    pub size: u64,
211    pub time: Timestamp,
212}
213
214/// [TimestampConversionError] represents an error that has occurred when
215/// attempting to convert a timestamp from a specific format to another.
216/// It is primarily used when attempting to deserialize a [Timestamp] from
217/// its serialized string representation.
218#[derive(Debug, PartialEq, Eq)]
219pub enum TimestampConversionError {
220    TimeError(time::error::ComponentRange),
221    IntError(TryFromIntError),
222}
223
224impl Display for TimestampConversionError {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        match self {
227            TimestampConversionError::TimeError(err) => write!(f, "{err:?}"),
228            TimestampConversionError::IntError(err) => write!(f, "{err:?}"),
229        }
230    }
231}
232
233impl std::error::Error for TimestampConversionError {
234    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
235        match self {
236            TimestampConversionError::TimeError(err) => Some(err),
237            TimestampConversionError::IntError(err) => Some(err),
238        }
239    }
240}
241
242impl From<time::error::ComponentRange> for TimestampConversionError {
243    fn from(value: time::error::ComponentRange) -> Self {
244        Self::TimeError(value)
245    }
246}
247
248impl From<TryFromIntError> for TimestampConversionError {
249    fn from(value: TryFromIntError) -> Self {
250        Self::IntError(value)
251    }
252}
253
254impl From<TimestampConversionError> for crate::QueryError {
255    fn from(value: TimestampConversionError) -> Self {
256        Self::Error {
257            message: format!("{value:?}"),
258        }
259    }
260}
261
262impl<Types: NodeType> TryFrom<BlockQueryData<Types>> for BlockSummary<Types>
263where
264    BlockQueryData<Types>: HeightIndexed,
265    Payload<Types>: QueryablePayload<Types>,
266    Header<Types>: BlockHeader<Types> + ExplorerHeader<Types>,
267{
268    type Error = TimestampConversionError;
269
270    fn try_from(value: BlockQueryData<Types>) -> Result<Self, Self::Error> {
271        let seconds = i64::try_from(value.header.timestamp())?;
272
273        Ok(Self {
274            hash: value.hash(),
275            height: value.height(),
276            proposer_id: value.header().proposer_id(),
277            num_transactions: value.num_transactions,
278            size: value.size,
279            time: Timestamp(time::OffsetDateTime::from_unix_timestamp(seconds)?),
280        })
281    }
282}
283
284/// [FeeAttribution] represents a specific attribution of fees for a specific
285/// purpose.
286///
287/// The current documentation lists attribution as potentially being
288/// accountable for the following entries:
289/// - Sequencer
290/// - DA Layer
291/// - Ethereum Mainnet
292#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
293pub struct FeeAttribution {
294    pub target: String,
295    pub fees: Vec<MonetaryValue>,
296}
297
298/// [TransactionDetail] is a struct that represents the details of a specific
299/// transaction / payload contained within a Block.
300#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
301#[serde(bound = "")]
302pub struct TransactionDetail<Types: NodeType> {
303    pub hash: TransactionHash<Types>,
304    pub height: u64,
305    pub block_confirmed: bool,
306    pub offset: u64,
307    pub num_transactions: u64,
308    pub size: u64,
309    pub time: Timestamp,
310    pub sequencing_fees: Vec<MonetaryValue>,
311    pub fee_details: Vec<FeeAttribution>,
312}
313
314/// [TransactionDetailResponse] is a struct that represents the information
315/// returned concerning a request for a Transaction Detail. It contains the
316/// data payloads separately from the details of the Transaction itself.
317#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
318#[serde(bound = "")]
319pub struct TransactionDetailResponse<Types: NodeType> {
320    pub details: TransactionDetail<Types>,
321    pub data: Vec<Transaction<Types>>,
322}
323
324/// [TransactionSummary] is a struct that represents a summary overview of a
325/// specific transaction / payload contained within a Block. It does not have
326/// all of the details of a [TransactionDetail], but it is useful for displaying
327/// information in a list of Transactions.
328#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
329#[serde(bound = "")]
330pub struct TransactionSummary<Types: NodeType>
331where
332    Header<Types>: ExplorerHeader<Types>,
333{
334    pub hash: TransactionHash<Types>,
335    pub rollups: Vec<NamespaceId<Types>>,
336    pub height: u64,
337    pub offset: u64,
338    pub num_transactions: u64,
339    pub time: Timestamp,
340}
341
342impl<Types: NodeType>
343    TryFrom<(
344        &BlockQueryData<Types>,
345        usize,
346        <Types as NodeType>::Transaction,
347    )> for TransactionSummary<Types>
348where
349    BlockQueryData<Types>: HeightIndexed,
350    Payload<Types>: QueryablePayload<Types>,
351    Header<Types>: QueryableHeader<Types> + ExplorerHeader<Types>,
352    Transaction<Types>: ExplorerTransaction<Types>,
353{
354    type Error = TimestampConversionError;
355
356    fn try_from(
357        (block, offset, transaction): (
358            &BlockQueryData<Types>,
359            usize,
360            <Types as NodeType>::Transaction,
361        ),
362    ) -> Result<Self, Self::Error> {
363        let seconds = i64::try_from(block.header.timestamp())?;
364
365        Ok(Self {
366            hash: transaction.commitment(),
367            height: block.height(),
368            offset: offset as u64,
369            num_transactions: block.num_transactions,
370            time: Timestamp(time::OffsetDateTime::from_unix_timestamp(seconds)?),
371            rollups: vec![transaction.namespace_id()],
372        })
373    }
374}
375
376impl<Types: NodeType>
377    TryFrom<(
378        &BlockQueryData<Types>,
379        usize,
380        <Types as NodeType>::Transaction,
381    )> for TransactionDetailResponse<Types>
382where
383    BlockQueryData<Types>: HeightIndexed,
384    Payload<Types>: QueryablePayload<Types>,
385    Header<Types>: QueryableHeader<Types> + ExplorerHeader<Types>,
386    <Types as NodeType>::Transaction: ExplorerTransaction<Types>,
387{
388    type Error = TimestampConversionError;
389
390    fn try_from(
391        (block, offset, transaction): (
392            &BlockQueryData<Types>,
393            usize,
394            <Types as NodeType>::Transaction,
395        ),
396    ) -> Result<Self, Self::Error> {
397        let seconds = i64::try_from(block.header.timestamp())?;
398
399        Ok(Self {
400            details: TransactionDetail {
401                hash: transaction.commitment(),
402                height: block.height(),
403                block_confirmed: true,
404                offset: offset as u64,
405                num_transactions: block.num_transactions,
406                size: transaction.payload_size(),
407                time: Timestamp(time::OffsetDateTime::from_unix_timestamp(seconds)?),
408                sequencing_fees: vec![],
409                fee_details: vec![],
410            },
411            data: vec![transaction],
412        })
413    }
414}
415
416/// GetBlockSummariesRequest is a struct that represents an incoming request
417/// for Block Summaries.  This isn't sent on the line, but an endpoint will
418/// be mapped to this struct in order for the request to be processed.
419#[derive(Debug, PartialEq, Eq)]
420pub struct GetBlockSummariesRequest<Types: NodeType>(pub BlockRange<Types>);
421
422/// [TransactionSummaryFilter] represents the various filters that can be
423/// applied when retrieving a list of [TransactionSummary] entries.
424#[derive(Debug, Deserialize, Serialize)]
425#[serde(bound = "")]
426pub enum TransactionSummaryFilter<Types>
427where
428    Types: NodeType,
429    Header<Types>: QueryableHeader<Types>,
430{
431    None,
432    RollUp(NamespaceId<Types>),
433    Block(usize),
434}
435
436/// GetTransactionSummariesRequest is a struct that represents an incoming
437/// request for Transaction Summaries.  This isn't sent on the line, but an
438/// endpoint will be mapped to this struct in order for the request to be
439/// processed.
440#[derive(Debug)]
441pub struct GetTransactionSummariesRequest<Types>
442where
443    Types: NodeType,
444    Header<Types>: QueryableHeader<Types>,
445{
446    pub range: TransactionRange<Types>,
447    pub filter: TransactionSummaryFilter<Types>,
448}
449
450impl<Types> Default for GetTransactionSummariesRequest<Types>
451where
452    Types: NodeType,
453    Header<Types>: QueryableHeader<Types>,
454{
455    fn default() -> Self {
456        Self {
457            range: TransactionRange {
458                target: TransactionIdentifier::Latest,
459                num_transactions: NonZeroUsize::new(20).unwrap(),
460            },
461            filter: TransactionSummaryFilter::None,
462        }
463    }
464}
465
466/// [GenesisOverview] provides a summary overview of the block chain since
467/// it's genesis. At a high level it includes the total number of unique
468/// rollups, transactions, and blocks that are in the block chain.
469#[derive(Debug, Serialize, Deserialize)]
470pub struct GenesisOverview {
471    pub rollups: u64,
472    pub transactions: u64,
473    pub blocks: u64,
474    // pub sequencer_nodes: u64,
475}
476
477/// [ExplorerHistograms] provides a series of data points that can be used to
478/// draw simple histograms for the Block Explorer.  The data returned is meant
479/// to be an optimal packing of the values being returned.
480///
481/// It contains data for the last N blocks, indicated by the length of the
482/// vectors contained within the struct.  All of the vectors **MUST** have the
483/// same length.  The labels of the graph points is the `block_heights` vector.
484/// The remaining data points are the `block_time`, `block_size`, and
485/// `block_transactions` for those `block_heights`.
486#[derive(Debug, Serialize, Deserialize)]
487pub struct ExplorerHistograms {
488    pub block_time: VecDeque<Option<u64>>,
489    pub block_size: VecDeque<Option<u64>>,
490    pub block_transactions: VecDeque<u64>,
491    pub block_heights: VecDeque<u64>,
492}
493
494/// [ExplorerSummary] is a struct that represents an at-a-glance snapshot of
495/// the Block Chain.  It contains some helpful information that can be used
496/// to display a simple health check of the Block Chain.
497///
498/// It contains the latest block for reference, the most recent blocks, the
499/// most recent transactions, some statistics concerning the total number
500/// of elements contained within the chain, and some histograms that can be
501/// used to draw graphs for the Block Explorer.
502#[derive(Debug, Serialize, Deserialize)]
503#[serde(bound = "")]
504pub struct ExplorerSummary<Types: NodeType>
505where
506    Header<Types>: ExplorerHeader<Types>,
507    Transaction<Types>: ExplorerTransaction<Types>,
508{
509    pub latest_block: BlockDetail<Types>,
510    pub genesis_overview: GenesisOverview,
511    pub latest_blocks: Vec<BlockSummary<Types>>,
512    pub latest_transactions: Vec<TransactionSummary<Types>>,
513    //  Most Active Rollups
514    pub histograms: ExplorerHistograms,
515}
516
517/// [SearchResult] is a struct that represents the results of executing a
518/// search query against the chain.  It contains a list of blocks and
519/// transactions that match the search query.
520#[derive(Debug, Serialize, Deserialize)]
521#[serde(bound = "")]
522pub struct SearchResult<Types: NodeType>
523where
524    Header<Types>: ExplorerHeader<Types>,
525    Transaction<Types>: ExplorerTransaction<Types>,
526{
527    pub blocks: Vec<BlockSummary<Types>>,
528    pub transactions: Vec<TransactionSummary<Types>>,
529}
530
531/// [GetBlockDetailError] represents an error that has occurred in response to
532/// the `get_block_detail` request.
533#[derive(Debug, Clone, Serialize, Deserialize)]
534#[serde(untagged)]
535pub enum GetBlockDetailError {
536    Unimplemented(Unimplemented),
537    BlockNotFound(NotFound),
538    QueryError(QueryError),
539}
540
541impl GetBlockDetailError {
542    pub fn status(&self) -> StatusCode {
543        match self {
544            GetBlockDetailError::Unimplemented(err) => err.status(),
545            GetBlockDetailError::QueryError(err) => err.status(),
546            GetBlockDetailError::BlockNotFound(err) => err.status(),
547        }
548    }
549}
550
551impl Display for GetBlockDetailError {
552    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
553        match self {
554            GetBlockDetailError::Unimplemented(err) => write!(f, "{err}"),
555            GetBlockDetailError::QueryError(err) => write!(f, "{err}"),
556            GetBlockDetailError::BlockNotFound(err) => write!(f, "{err}"),
557        }
558    }
559}
560
561impl ExplorerAPIError for GetBlockDetailError {
562    fn code(&self) -> &str {
563        match self {
564            GetBlockDetailError::Unimplemented(err) => err.code(),
565            GetBlockDetailError::QueryError(err) => err.code(),
566            GetBlockDetailError::BlockNotFound(err) => err.code(),
567        }
568    }
569}
570
571impl std::error::Error for GetBlockDetailError {
572    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
573        match self {
574            GetBlockDetailError::Unimplemented(err) => Some(err),
575            GetBlockDetailError::QueryError(err) => Some(err),
576            _ => None,
577        }
578    }
579}
580
581impl From<crate::QueryError> for GetBlockDetailError {
582    fn from(value: crate::QueryError) -> Self {
583        GetBlockDetailError::QueryError(QueryError { error: value })
584    }
585}
586
587/// [GetBlockSummariesError] represents an error that has occurred in response
588/// to the [GetBlockSummariesRequest] request.
589#[derive(Debug, Clone, Serialize, Deserialize)]
590#[serde(untagged)]
591pub enum GetBlockSummariesError {
592    Unimplemented(Unimplemented),
593    InvalidLimit(InvalidLimit),
594    TargetNotFound(NotFound),
595    QueryError(QueryError),
596}
597
598impl GetBlockSummariesError {
599    pub fn status(&self) -> StatusCode {
600        match self {
601            GetBlockSummariesError::Unimplemented(err) => err.status(),
602            GetBlockSummariesError::InvalidLimit(err) => err.status(),
603            GetBlockSummariesError::QueryError(err) => err.status(),
604            GetBlockSummariesError::TargetNotFound(err) => err.status(),
605        }
606    }
607}
608
609impl Display for GetBlockSummariesError {
610    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
611        match self {
612            GetBlockSummariesError::Unimplemented(err) => write!(f, "{err}"),
613            GetBlockSummariesError::InvalidLimit(err) => write!(f, "{err}"),
614            GetBlockSummariesError::QueryError(err) => write!(f, "{err}"),
615            GetBlockSummariesError::TargetNotFound(err) => write!(f, "{err}"),
616        }
617    }
618}
619
620impl ExplorerAPIError for GetBlockSummariesError {
621    fn code(&self) -> &str {
622        match self {
623            GetBlockSummariesError::Unimplemented(err) => err.code(),
624            GetBlockSummariesError::InvalidLimit(err) => err.code(),
625            GetBlockSummariesError::QueryError(err) => err.code(),
626            GetBlockSummariesError::TargetNotFound(err) => err.code(),
627        }
628    }
629}
630
631impl std::error::Error for GetBlockSummariesError {
632    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
633        match self {
634            GetBlockSummariesError::Unimplemented(err) => Some(err),
635            GetBlockSummariesError::InvalidLimit(err) => Some(err),
636            GetBlockSummariesError::QueryError(err) => Some(err),
637            _ => None,
638        }
639    }
640}
641
642impl From<crate::QueryError> for GetBlockSummariesError {
643    fn from(value: crate::QueryError) -> Self {
644        GetBlockSummariesError::QueryError(QueryError { error: value })
645    }
646}
647
648/// [GetTransactionDetailError] represents an error that has occurred in
649/// response to the `get_transaction_detail` request.
650#[derive(Debug, Clone, Serialize, Deserialize)]
651#[serde(untagged)]
652pub enum GetTransactionDetailError {
653    Unimplemented(Unimplemented),
654    TransactionNotFound(NotFound),
655    QueryError(QueryError),
656}
657
658impl GetTransactionDetailError {
659    pub fn status(&self) -> StatusCode {
660        match self {
661            GetTransactionDetailError::Unimplemented(err) => err.status(),
662            GetTransactionDetailError::QueryError(err) => err.status(),
663            GetTransactionDetailError::TransactionNotFound(err) => err.status(),
664        }
665    }
666}
667
668impl Display for GetTransactionDetailError {
669    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
670        match self {
671            GetTransactionDetailError::Unimplemented(err) => write!(f, "{err}"),
672            GetTransactionDetailError::QueryError(err) => write!(f, "{err}"),
673            GetTransactionDetailError::TransactionNotFound(err) => write!(f, "{err}"),
674        }
675    }
676}
677
678impl ExplorerAPIError for GetTransactionDetailError {
679    fn code(&self) -> &str {
680        match self {
681            GetTransactionDetailError::Unimplemented(err) => err.code(),
682            GetTransactionDetailError::QueryError(err) => err.code(),
683            GetTransactionDetailError::TransactionNotFound(err) => err.code(),
684        }
685    }
686}
687
688impl std::error::Error for GetTransactionDetailError {
689    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
690        match self {
691            GetTransactionDetailError::Unimplemented(err) => Some(err),
692            GetTransactionDetailError::QueryError(err) => Some(err),
693            _ => None,
694        }
695    }
696}
697
698// Implement implicit conversion between these errors for the branch operator.
699
700impl From<crate::QueryError> for GetTransactionDetailError {
701    fn from(value: crate::QueryError) -> Self {
702        GetTransactionDetailError::QueryError(QueryError { error: value })
703    }
704}
705
706impl From<TimestampConversionError> for GetTransactionDetailError {
707    fn from(value: TimestampConversionError) -> Self {
708        GetTransactionDetailError::QueryError(QueryError {
709            error: value.into(),
710        })
711    }
712}
713
714/// [GetTransactionSummariesError] represents an error that has occurred in
715/// response to the [GetTransactionSummariesRequest] request.
716#[derive(Debug, Clone, Serialize, Deserialize)]
717#[serde(untagged)]
718pub enum GetTransactionSummariesError {
719    Unimplemented(Unimplemented),
720    InvalidLimit(InvalidLimit),
721    TargetNotFound(NotFound),
722    QueryError(QueryError),
723}
724
725impl GetTransactionSummariesError {
726    pub fn status(&self) -> StatusCode {
727        match self {
728            GetTransactionSummariesError::Unimplemented(err) => err.status(),
729            GetTransactionSummariesError::InvalidLimit(err) => err.status(),
730            GetTransactionSummariesError::QueryError(err) => err.status(),
731            GetTransactionSummariesError::TargetNotFound(err) => err.status(),
732        }
733    }
734}
735
736impl Display for GetTransactionSummariesError {
737    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
738        match self {
739            GetTransactionSummariesError::Unimplemented(err) => write!(f, "{err}"),
740            GetTransactionSummariesError::InvalidLimit(err) => write!(f, "{err}"),
741            GetTransactionSummariesError::QueryError(err) => write!(f, "{err}"),
742            GetTransactionSummariesError::TargetNotFound(err) => write!(f, "{err}"),
743        }
744    }
745}
746
747impl ExplorerAPIError for GetTransactionSummariesError {
748    fn code(&self) -> &str {
749        match self {
750            GetTransactionSummariesError::Unimplemented(err) => err.code(),
751            GetTransactionSummariesError::InvalidLimit(err) => err.code(),
752            GetTransactionSummariesError::QueryError(err) => err.code(),
753            GetTransactionSummariesError::TargetNotFound(err) => err.code(),
754        }
755    }
756}
757
758impl std::error::Error for GetTransactionSummariesError {
759    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
760        match self {
761            GetTransactionSummariesError::Unimplemented(err) => Some(err),
762            GetTransactionSummariesError::InvalidLimit(err) => Some(err),
763            GetTransactionSummariesError::QueryError(err) => Some(err),
764            _ => None,
765        }
766    }
767}
768
769impl From<crate::QueryError> for GetTransactionSummariesError {
770    fn from(value: crate::QueryError) -> Self {
771        GetTransactionSummariesError::QueryError(QueryError { error: value })
772    }
773}
774
775/// [GetExplorerSummaryError] represents an error that has occurred in response
776/// to the `get_explorer_summary` request.
777#[derive(Debug, Clone, Serialize, Deserialize)]
778#[serde(untagged)]
779pub enum GetExplorerSummaryError {
780    Unimplemented(Unimplemented),
781    QueryError(QueryError),
782    GetBlockDetailError(GetBlockDetailError),
783    GetBlockSummariesError(GetBlockSummariesError),
784    GetTransactionSummariesError(GetTransactionSummariesError),
785}
786
787impl GetExplorerSummaryError {
788    pub fn status(&self) -> StatusCode {
789        match self {
790            GetExplorerSummaryError::QueryError(err) => err.status(),
791            GetExplorerSummaryError::Unimplemented(err) => err.status(),
792            GetExplorerSummaryError::GetBlockDetailError(err) => err.status(),
793            GetExplorerSummaryError::GetBlockSummariesError(err) => err.status(),
794            GetExplorerSummaryError::GetTransactionSummariesError(err) => err.status(),
795        }
796    }
797}
798
799impl Display for GetExplorerSummaryError {
800    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
801        match self {
802            GetExplorerSummaryError::QueryError(err) => write!(f, "{err}"),
803            GetExplorerSummaryError::Unimplemented(err) => write!(f, "{err}"),
804            GetExplorerSummaryError::GetBlockDetailError(err) => write!(f, "{err}"),
805            GetExplorerSummaryError::GetBlockSummariesError(err) => write!(f, "{err}"),
806            GetExplorerSummaryError::GetTransactionSummariesError(err) => write!(f, "{err}"),
807        }
808    }
809}
810
811impl ExplorerAPIError for GetExplorerSummaryError {
812    fn code(&self) -> &str {
813        match self {
814            GetExplorerSummaryError::QueryError(err) => err.code(),
815            GetExplorerSummaryError::Unimplemented(err) => err.code(),
816            GetExplorerSummaryError::GetBlockDetailError(err) => err.code(),
817            GetExplorerSummaryError::GetBlockSummariesError(err) => err.code(),
818            GetExplorerSummaryError::GetTransactionSummariesError(err) => err.code(),
819        }
820    }
821}
822
823impl std::error::Error for GetExplorerSummaryError {
824    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
825        match self {
826            GetExplorerSummaryError::Unimplemented(err) => Some(err),
827            GetExplorerSummaryError::QueryError(err) => Some(err),
828            GetExplorerSummaryError::GetBlockDetailError(err) => Some(err),
829            GetExplorerSummaryError::GetBlockSummariesError(err) => Some(err),
830            GetExplorerSummaryError::GetTransactionSummariesError(err) => Some(err),
831        }
832    }
833}
834
835// Implement implicit conversion between these errors for the branch operator.
836
837impl From<crate::QueryError> for GetExplorerSummaryError {
838    fn from(value: crate::QueryError) -> Self {
839        GetExplorerSummaryError::QueryError(QueryError { error: value })
840    }
841}
842
843impl From<GetBlockDetailError> for GetExplorerSummaryError {
844    fn from(value: GetBlockDetailError) -> Self {
845        GetExplorerSummaryError::GetBlockDetailError(value)
846    }
847}
848
849impl From<GetBlockSummariesError> for GetExplorerSummaryError {
850    fn from(value: GetBlockSummariesError) -> Self {
851        GetExplorerSummaryError::GetBlockSummariesError(value)
852    }
853}
854
855impl From<GetTransactionSummariesError> for GetExplorerSummaryError {
856    fn from(value: GetTransactionSummariesError) -> Self {
857        GetExplorerSummaryError::GetTransactionSummariesError(value)
858    }
859}
860
861/// [GetSearchResultsError] represents an error that has occurred in response
862/// to the `get_search_results` request.
863#[derive(Debug, Clone, Serialize, Deserialize)]
864#[serde(untagged)]
865pub enum GetSearchResultsError {
866    Unimplemented(Unimplemented),
867    QueryError(QueryError),
868    InvalidQuery(BadQuery),
869}
870
871impl GetSearchResultsError {
872    pub fn status(&self) -> StatusCode {
873        match self {
874            GetSearchResultsError::QueryError(err) => err.status(),
875            GetSearchResultsError::Unimplemented(err) => err.status(),
876            GetSearchResultsError::InvalidQuery(err) => err.status(),
877        }
878    }
879}
880
881impl Display for GetSearchResultsError {
882    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
883        match self {
884            GetSearchResultsError::QueryError(err) => write!(f, "{err}"),
885            GetSearchResultsError::Unimplemented(err) => write!(f, "{err}"),
886            GetSearchResultsError::InvalidQuery(err) => write!(f, "{err}"),
887        }
888    }
889}
890
891impl ExplorerAPIError for GetSearchResultsError {
892    fn code(&self) -> &str {
893        match self {
894            GetSearchResultsError::QueryError(err) => err.code(),
895            GetSearchResultsError::Unimplemented(err) => err.code(),
896            GetSearchResultsError::InvalidQuery(err) => err.code(),
897        }
898    }
899}
900
901impl std::error::Error for GetSearchResultsError {
902    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
903        match self {
904            GetSearchResultsError::Unimplemented(err) => Some(err),
905            GetSearchResultsError::QueryError(err) => Some(err),
906            GetSearchResultsError::InvalidQuery(err) => Some(err),
907        }
908    }
909}
910
911impl From<crate::QueryError> for GetSearchResultsError {
912    fn from(value: crate::QueryError) -> Self {
913        GetSearchResultsError::QueryError(QueryError { error: value })
914    }
915}