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}