exonum_explorer/
api.rs

1// Copyright 2020 The Exonum Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//   http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Types used in the explorer API.
16//!
17//! The types are bundled together with the explorer (rather than the explorer service)
18//! in order to ease dependency management for client apps.
19
20use chrono::{DateTime, Utc};
21use exonum::{
22    blockchain::{Block, CallProof},
23    crypto::Hash,
24    helpers::Height,
25    merkledb::BinaryValue,
26    messages::{Precommit, Verified},
27    runtime::{AnyTx, CallInfo, ExecutionStatus, InstanceId},
28};
29use serde_derive::{Deserialize, Serialize};
30
31use std::ops::Range;
32
33use crate::median_precommits_time;
34
35pub mod websocket;
36
37/// The maximum number of blocks to return per blocks request, in this way
38/// the parameter limits the maximum execution time for such requests.
39pub const MAX_BLOCKS_PER_REQUEST: usize = 1000;
40
41/// Information on blocks coupled with the corresponding range in the blockchain.
42#[derive(Debug, Serialize, Deserialize, PartialEq)]
43#[non_exhaustive]
44pub struct BlocksRange {
45    /// Exclusive range of blocks.
46    pub range: Range<Height>,
47    /// Blocks in the range.
48    pub blocks: Vec<BlockInfo>,
49}
50
51impl BlocksRange {
52    /// Creates a new range of blocks.
53    #[doc(hidden)] // not stabilized; used in the explorer service
54    pub fn new(range: Range<Height>, blocks: Vec<BlockInfo>) -> Self {
55        Self { range, blocks }
56    }
57}
58
59/// Information about a transaction included in the block.
60#[derive(Debug, PartialEq, Serialize, Deserialize)]
61#[non_exhaustive]
62pub struct TxInfo {
63    /// Transaction hash.
64    pub tx_hash: Hash,
65    /// Information to call.
66    pub call_info: CallInfo,
67}
68
69/// Information about a block in the blockchain.
70#[derive(Debug, PartialEq, Serialize, Deserialize)]
71#[non_exhaustive]
72pub struct BlockInfo {
73    /// Block header as recorded in the blockchain.
74    #[serde(flatten)]
75    pub block: Block,
76
77    /// Precommits authorizing the block.
78    #[serde(skip_serializing_if = "Option::is_none")]
79    pub precommits: Option<Vec<Verified<Precommit>>>,
80
81    /// Info of transactions in the block.
82    #[serde(skip_serializing_if = "Option::is_none")]
83    pub txs: Option<Vec<TxInfo>>,
84
85    /// Median time from the block precommits.
86    #[serde(skip_serializing_if = "Option::is_none")]
87    pub time: Option<DateTime<Utc>>,
88}
89
90impl From<crate::BlockInfo<'_>> for BlockInfo {
91    fn from(inner: crate::BlockInfo<'_>) -> Self {
92        Self {
93            block: inner.header().clone(),
94            precommits: Some(inner.precommits().to_vec()),
95            txs: Some(
96                inner
97                    .transaction_hashes()
98                    .iter()
99                    .enumerate()
100                    .map(|(idx, &tx_hash)| TxInfo {
101                        tx_hash,
102                        call_info: inner
103                            .transaction(idx)
104                            .unwrap()
105                            .message()
106                            .payload()
107                            .call_info
108                            .clone(),
109                    })
110                    .collect(),
111            ),
112            time: Some(median_precommits_time(&inner.precommits())),
113        }
114    }
115}
116
117impl BlockInfo {
118    /// Creates a summary of the block.
119    #[doc(hidden)] // not stabilized; intended for use in explorer service
120    pub fn summary(block: crate::BlockInfo<'_>, query: &BlocksQuery) -> Self {
121        BlockInfo {
122            txs: None,
123
124            time: if query.add_blocks_time {
125                Some(median_precommits_time(&block.precommits()))
126            } else {
127                None
128            },
129
130            precommits: if query.add_precommits {
131                Some(block.precommits().to_vec())
132            } else {
133                None
134            },
135
136            block: block.into_header(),
137        }
138    }
139}
140
141/// Blocks in range parameters.
142#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
143#[non_exhaustive]
144pub struct BlocksQuery {
145    /// The number of blocks to return. Should not be greater than `MAX_BLOCKS_PER_REQUEST`.
146    pub count: usize,
147    /// The maximum height of the returned blocks.
148    ///
149    /// The blocks are returned in reverse order,
150    /// starting from the latest and at least up to the `latest - count + 1`.
151    /// The default value is the height of the latest block in the blockchain.
152    pub latest: Option<Height>,
153    /// The minimum height of the returned blocks. The default value is `Height(0)` (the genesis
154    /// block).
155    ///
156    /// Note that `earliest` has the least priority compared to `latest` and `count`;
157    /// it can only truncate the list of otherwise returned blocks if some of them have a lesser
158    /// height.
159    pub earliest: Option<Height>,
160    /// If true, then only non-empty blocks are returned. The default value is false.
161    #[serde(default)]
162    pub skip_empty_blocks: bool,
163    /// If true, then the `time` field in each returned block will contain the median time from the
164    /// block precommits.
165    #[serde(default)]
166    pub add_blocks_time: bool,
167    /// If true, then the `precommits` field in each returned block will contain precommits for the
168    /// block stored by the node.
169    #[serde(default)]
170    pub add_precommits: bool,
171}
172
173/// Block query parameters.
174#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
175#[non_exhaustive]
176pub struct BlockQuery {
177    /// The height of the desired block.
178    pub height: Height,
179}
180
181impl BlockQuery {
182    /// Creates a new block query with the given height.
183    pub fn new(height: Height) -> Self {
184        Self { height }
185    }
186}
187
188/// Raw transaction in hex representation.
189#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
190#[non_exhaustive]
191pub struct TransactionHex {
192    /// The hex value of the transaction to be broadcasted.
193    pub tx_body: String,
194}
195
196impl TransactionHex {
197    /// Creates hex representation from the provided transaction.
198    pub fn new(transaction: &Verified<AnyTx>) -> Self {
199        Self {
200            tx_body: hex::encode(transaction.to_bytes()),
201        }
202    }
203}
204
205/// Response to a request to broadcast a transaction over the blockchain network.
206#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
207#[non_exhaustive]
208pub struct TransactionResponse {
209    /// The hash digest of the transaction.
210    pub tx_hash: Hash,
211}
212
213impl TransactionResponse {
214    /// Creates a response based on provided transaction hash.
215    pub fn new(tx_hash: Hash) -> Self {
216        Self { tx_hash }
217    }
218}
219
220/// Transaction query parameters.
221#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
222#[non_exhaustive]
223pub struct TransactionQuery {
224    /// The hash of the transaction to be searched.
225    pub hash: Hash,
226}
227
228impl TransactionQuery {
229    /// Creates a new transaction query with the given height.
230    pub fn new(hash: Hash) -> Self {
231        Self { hash }
232    }
233}
234
235/// Query parameters to check the execution status of a transaction.
236#[derive(Debug, Serialize, Deserialize, Clone)]
237#[non_exhaustive]
238pub struct TransactionStatusQuery {
239    /// The hash of the transaction to be searched.
240    pub hash: Hash,
241    /// Whether to return the status with a cryptographic proof of authenticity.
242    #[serde(default)]
243    pub with_proof: bool,
244}
245
246impl TransactionStatusQuery {
247    /// Creates a new query.
248    pub fn new(hash: Hash) -> Self {
249        Self {
250            hash,
251            with_proof: false,
252        }
253    }
254
255    /// Requests to return a call status with a cryptographic proof of authenticity.
256    pub fn with_proof(mut self) -> Self {
257        self.with_proof = true;
258        self
259    }
260}
261
262/// Query parameters to check the execution status of a `before_transactions` or
263/// `after_transactions` call.
264#[derive(Debug, Serialize, Deserialize, Clone)]
265#[non_exhaustive]
266pub struct CallStatusQuery {
267    /// Height of a block.
268    pub height: Height,
269    /// Numerical service identifier.
270    pub service_id: InstanceId,
271    /// Whether to return the status with a cryptographic proof of authenticity.
272    #[serde(default)]
273    pub with_proof: bool,
274}
275
276impl CallStatusQuery {
277    /// Creates a new query.
278    pub fn new(height: Height, service_id: InstanceId) -> Self {
279        Self {
280            height,
281            service_id,
282            with_proof: false,
283        }
284    }
285
286    /// Requests to return a call status with a cryptographic proof of authenticity.
287    pub fn with_proof(mut self) -> Self {
288        self.with_proof = true;
289        self
290    }
291}
292
293/// Call status response.
294///
295/// This enum is serialized in JSON untagged. Hence, if the consumer knows the type of the response,
296/// she may parse it directly to `ExecutionStatus` or `CallProof`.
297#[derive(Debug, Serialize, Deserialize, Clone)]
298#[serde(untagged)]
299pub enum CallStatusResponse {
300    /// Simple response.
301    Simple(ExecutionStatus),
302    /// Response with a cryptographic proof of authenticity.
303    Proof(CallProof),
304}