near_primitives/
views.rs

1//! This module defines "stable" internal API to view internal data using view_client.
2//!
3//! These types should only change when we cannot avoid this. Thus, when the counterpart internal
4//! type gets changed, the view should preserve the old shape and only re-map the necessary bits
5//! from the source structure in the relevant `From<SourceStruct>` impl.
6use crate::account::{AccessKey, AccessKeyPermission, Account, FunctionCallPermission};
7use crate::action::delegate::{DelegateAction, SignedDelegateAction};
8use crate::action::{
9    DeployGlobalContractAction, GlobalContractDeployMode, GlobalContractIdentifier,
10    UseGlobalContractAction,
11};
12use crate::bandwidth_scheduler::BandwidthRequests;
13use crate::block::{Block, BlockHeader, Tip};
14use crate::block_header::BlockHeaderInnerLite;
15use crate::challenge::SlashedValidator;
16use crate::congestion_info::{CongestionInfo, CongestionInfoV1};
17use crate::errors::TxExecutionError;
18use crate::hash::{CryptoHash, hash};
19use crate::merkle::{MerklePath, combine_hash};
20use crate::network::PeerId;
21use crate::receipt::{
22    ActionReceipt, DataReceipt, DataReceiver, GlobalContractDistributionReceipt, Receipt,
23    ReceiptEnum, ReceiptV1,
24};
25use crate::serialize::dec_format;
26use crate::sharding::shard_chunk_header_inner::ShardChunkHeaderInnerV4;
27use crate::sharding::{
28    ChunkHash, ShardChunk, ShardChunkHeader, ShardChunkHeaderInner, ShardChunkHeaderInnerV2,
29    ShardChunkHeaderInnerV3, ShardChunkHeaderV3,
30};
31use crate::stateless_validation::chunk_endorsements_bitmap::ChunkEndorsementsBitmap;
32use crate::transaction::{
33    Action, AddKeyAction, CreateAccountAction, DeleteAccountAction, DeleteKeyAction,
34    DeployContractAction, ExecutionMetadata, ExecutionOutcome, ExecutionOutcomeWithIdAndProof,
35    ExecutionStatus, FunctionCallAction, PartialExecutionOutcome, PartialExecutionStatus,
36    SignedTransaction, StakeAction, TransferAction,
37};
38use crate::types::{
39    AccountId, AccountWithPublicKey, Balance, BlockHeight, EpochHeight, EpochId, FunctionArgs, Gas,
40    Nonce, NumBlocks, ShardId, StateChangeCause, StateChangeKind, StateChangeValue,
41    StateChangeWithCause, StateChangesRequest, StateRoot, StorageUsage, StoreKey, StoreValue,
42    ValidatorKickoutReason,
43};
44use crate::version::{ProtocolVersion, Version};
45use borsh::{BorshDeserialize, BorshSerialize};
46use near_crypto::{PublicKey, Signature};
47use near_fmt::{AbbrBytes, Slice};
48use near_parameters::config::CongestionControlConfig;
49use near_parameters::view::CongestionControlConfigView;
50use near_parameters::{ActionCosts, ExtCosts};
51use near_primitives_core::account::AccountContract;
52use near_schema_checker_lib::ProtocolSchema;
53use near_time::Utc;
54use serde_with::base64::Base64;
55use serde_with::serde_as;
56use std::collections::HashMap;
57use std::fmt;
58use std::ops::Range;
59use std::sync::Arc;
60use strum::IntoEnumIterator;
61use validator_stake_view::ValidatorStakeView;
62
63/// A view of the account
64#[derive(serde::Serialize, serde::Deserialize, Debug, Eq, PartialEq, Clone)]
65#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
66pub struct AccountView {
67    #[serde(with = "dec_format")]
68    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
69    pub amount: Balance,
70    #[serde(with = "dec_format")]
71    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
72    pub locked: Balance,
73    pub code_hash: CryptoHash,
74    pub storage_usage: StorageUsage,
75    /// TODO(2271): deprecated.
76    #[serde(default)]
77    pub storage_paid_at: BlockHeight,
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub global_contract_hash: Option<CryptoHash>,
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub global_contract_account_id: Option<AccountId>,
82}
83
84/// A view of the contract code.
85#[serde_as]
86#[derive(serde::Serialize, serde::Deserialize, PartialEq, Eq, Debug, Clone)]
87#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
88pub struct ContractCodeView {
89    #[serde(rename = "code_base64")]
90    #[serde_as(as = "Base64")]
91    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
92    pub code: Vec<u8>,
93    pub hash: CryptoHash,
94}
95
96impl From<&Account> for AccountView {
97    fn from(account: &Account) -> Self {
98        let (global_contract_hash, global_contract_account_id) =
99            match account.contract().into_owned() {
100                AccountContract::Global(contract) => (Some(contract), None),
101                AccountContract::GlobalByAccount(account_id) => (None, Some(account_id)),
102                AccountContract::Local(_) | AccountContract::None => (None, None),
103            };
104        AccountView {
105            amount: account.amount(),
106            locked: account.locked(),
107            code_hash: account.local_contract_hash().unwrap_or_default(),
108            storage_usage: account.storage_usage(),
109            storage_paid_at: 0,
110            global_contract_hash,
111            global_contract_account_id,
112        }
113    }
114}
115
116impl From<Account> for AccountView {
117    fn from(account: Account) -> Self {
118        (&account).into()
119    }
120}
121
122impl From<&AccountView> for Account {
123    fn from(view: &AccountView) -> Self {
124        let contract = match &view.global_contract_account_id {
125            Some(account_id) => AccountContract::GlobalByAccount(account_id.clone()),
126            None => match view.global_contract_hash {
127                Some(hash) => AccountContract::Global(hash),
128                None => AccountContract::from_local_code_hash(view.code_hash),
129            },
130        };
131        Account::new(view.amount, view.locked, contract, view.storage_usage)
132    }
133}
134
135impl From<AccountView> for Account {
136    fn from(view: AccountView) -> Self {
137        (&view).into()
138    }
139}
140
141#[derive(
142    BorshSerialize,
143    BorshDeserialize,
144    Debug,
145    Eq,
146    PartialEq,
147    Clone,
148    serde::Serialize,
149    serde::Deserialize,
150)]
151#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
152pub enum AccessKeyPermissionView {
153    FunctionCall {
154        #[serde(with = "dec_format")]
155        #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
156        allowance: Option<Balance>,
157        receiver_id: String,
158        method_names: Vec<String>,
159    },
160    FullAccess,
161}
162
163impl From<AccessKeyPermission> for AccessKeyPermissionView {
164    fn from(permission: AccessKeyPermission) -> Self {
165        match permission {
166            AccessKeyPermission::FunctionCall(func_call) => AccessKeyPermissionView::FunctionCall {
167                allowance: func_call.allowance,
168                receiver_id: func_call.receiver_id,
169                method_names: func_call.method_names,
170            },
171            AccessKeyPermission::FullAccess => AccessKeyPermissionView::FullAccess,
172        }
173    }
174}
175
176impl From<AccessKeyPermissionView> for AccessKeyPermission {
177    fn from(view: AccessKeyPermissionView) -> Self {
178        match view {
179            AccessKeyPermissionView::FunctionCall { allowance, receiver_id, method_names } => {
180                AccessKeyPermission::FunctionCall(FunctionCallPermission {
181                    allowance,
182                    receiver_id,
183                    method_names,
184                })
185            }
186            AccessKeyPermissionView::FullAccess => AccessKeyPermission::FullAccess,
187        }
188    }
189}
190
191#[derive(
192    BorshSerialize,
193    BorshDeserialize,
194    Debug,
195    Eq,
196    PartialEq,
197    Clone,
198    serde::Serialize,
199    serde::Deserialize,
200)]
201#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
202pub struct AccessKeyView {
203    pub nonce: Nonce,
204    pub permission: AccessKeyPermissionView,
205}
206
207impl From<AccessKey> for AccessKeyView {
208    fn from(access_key: AccessKey) -> Self {
209        Self { nonce: access_key.nonce, permission: access_key.permission.into() }
210    }
211}
212
213impl From<AccessKeyView> for AccessKey {
214    fn from(view: AccessKeyView) -> Self {
215        Self { nonce: view.nonce, permission: view.permission.into() }
216    }
217}
218
219/// Item of the state, key and value are serialized in base64 and proof for inclusion of given state item.
220#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
221#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
222pub struct StateItem {
223    pub key: StoreKey,
224    pub value: StoreValue,
225}
226
227#[serde_as]
228#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
229#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
230pub struct ViewStateResult {
231    pub values: Vec<StateItem>,
232    #[serde_as(as = "Vec<Base64>")]
233    #[serde(default, skip_serializing_if = "Vec::is_empty")]
234    #[cfg_attr(feature = "schemars", schemars(with = "Vec<String>"))]
235    pub proof: Vec<Arc<[u8]>>,
236}
237
238#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone, Default)]
239#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
240pub struct CallResult {
241    pub result: Vec<u8>,
242    pub logs: Vec<String>,
243}
244
245#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
246pub struct QueryError {
247    pub error: String,
248    pub logs: Vec<String>,
249}
250
251#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
252#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
253pub struct AccessKeyInfoView {
254    pub public_key: PublicKey,
255    pub access_key: AccessKeyView,
256}
257
258#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
259#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
260pub struct AccessKeyList {
261    pub keys: Vec<AccessKeyInfoView>,
262}
263
264impl FromIterator<AccessKeyInfoView> for AccessKeyList {
265    fn from_iter<I: IntoIterator<Item = AccessKeyInfoView>>(iter: I) -> Self {
266        Self { keys: iter.into_iter().collect() }
267    }
268}
269
270// cspell:words deepsize
271#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))]
272#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
273#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
274pub struct KnownPeerStateView {
275    pub peer_id: PeerId,
276    pub status: String,
277    pub addr: String,
278    pub first_seen: i64,
279    pub last_seen: i64,
280    pub last_attempt: Option<(i64, String)>,
281}
282
283#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))]
284#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
285#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
286pub struct ConnectionInfoView {
287    pub peer_id: PeerId,
288    pub addr: String,
289    pub time_established: i64,
290    pub time_connected_until: i64,
291}
292
293#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))]
294#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
295#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
296pub struct SnapshotHostInfoView {
297    pub peer_id: PeerId,
298    pub sync_hash: CryptoHash,
299    pub epoch_height: u64,
300    pub shards: Vec<u64>,
301}
302
303#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))]
304#[derive(Debug, PartialEq, Eq, Clone)]
305pub enum QueryResponseKind {
306    ViewAccount(AccountView),
307    ViewCode(ContractCodeView),
308    ViewState(ViewStateResult),
309    CallResult(CallResult),
310    AccessKey(AccessKeyView),
311    AccessKeyList(AccessKeyList),
312}
313
314#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
315#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
316#[serde(tag = "request_type", rename_all = "snake_case")]
317pub enum QueryRequest {
318    ViewAccount {
319        account_id: AccountId,
320    },
321    ViewCode {
322        account_id: AccountId,
323    },
324    ViewState {
325        account_id: AccountId,
326        #[serde(rename = "prefix_base64")]
327        prefix: StoreKey,
328        #[serde(default, skip_serializing_if = "is_false")]
329        include_proof: bool,
330    },
331    ViewAccessKey {
332        account_id: AccountId,
333        public_key: PublicKey,
334    },
335    ViewAccessKeyList {
336        account_id: AccountId,
337    },
338    CallFunction {
339        account_id: AccountId,
340        method_name: String,
341        #[serde(rename = "args_base64")]
342        args: FunctionArgs,
343    },
344    ViewGlobalContractCode {
345        code_hash: CryptoHash,
346    },
347    ViewGlobalContractCodeByAccountId {
348        account_id: AccountId,
349    },
350}
351
352fn is_false(v: &bool) -> bool {
353    !*v
354}
355
356#[derive(Debug, PartialEq, Eq, Clone)]
357pub struct QueryResponse {
358    pub kind: QueryResponseKind,
359    pub block_height: BlockHeight,
360    pub block_hash: CryptoHash,
361}
362
363#[derive(serde::Serialize, serde::Deserialize, Debug)]
364#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
365pub struct StatusSyncInfo {
366    pub latest_block_hash: CryptoHash,
367    pub latest_block_height: BlockHeight,
368    pub latest_state_root: CryptoHash,
369    #[serde(with = "near_time::serde_utc_as_iso")]
370    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
371    pub latest_block_time: Utc,
372    pub syncing: bool,
373    pub earliest_block_hash: Option<CryptoHash>,
374    pub earliest_block_height: Option<BlockHeight>,
375    #[serde(with = "near_time::serde_opt_utc_as_iso")]
376    #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
377    pub earliest_block_time: Option<Utc>,
378    pub epoch_id: Option<EpochId>,
379    pub epoch_start_height: Option<BlockHeight>,
380}
381
382// TODO: add more information to ValidatorInfo
383#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
384#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
385pub struct ValidatorInfo {
386    pub account_id: AccountId,
387}
388
389#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
390#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
391pub struct PeerInfoView {
392    pub addr: String,
393    pub account_id: Option<AccountId>,
394    pub height: Option<BlockHeight>,
395    pub block_hash: Option<CryptoHash>,
396    pub is_highest_block_invalid: bool,
397    pub tracked_shards: Vec<ShardId>,
398    pub archival: bool,
399    pub peer_id: PublicKey,
400    pub received_bytes_per_sec: u64,
401    pub sent_bytes_per_sec: u64,
402    pub last_time_peer_requested_millis: u64,
403    pub last_time_received_message_millis: u64,
404    pub connection_established_time_millis: u64,
405    pub is_outbound_peer: bool,
406    /// Connection nonce.
407    pub nonce: u64,
408}
409
410/// Information about a Producer: its account name, peer_id and a list of connected peers that
411/// the node can use to send message for this producer.
412#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
413#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
414pub struct KnownProducerView {
415    pub account_id: AccountId,
416    pub peer_id: PublicKey,
417    pub next_hops: Option<Vec<PublicKey>>,
418}
419
420#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
421#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
422pub struct Tier1ProxyView {
423    pub addr: std::net::SocketAddr,
424    pub peer_id: PublicKey,
425}
426
427#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
428#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
429pub struct AccountDataView {
430    pub peer_id: PublicKey,
431    pub proxies: Vec<Tier1ProxyView>,
432    pub account_key: PublicKey,
433    #[serde(with = "near_time::serde_utc_as_iso")]
434    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
435    pub timestamp: Utc,
436}
437
438#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
439#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
440pub struct NetworkInfoView {
441    pub peer_max_count: u32,
442    pub num_connected_peers: usize,
443    pub connected_peers: Vec<PeerInfoView>,
444    pub known_producers: Vec<KnownProducerView>,
445    pub tier1_accounts_keys: Vec<PublicKey>,
446    pub tier1_accounts_data: Vec<AccountDataView>,
447    pub tier1_connections: Vec<PeerInfoView>,
448}
449
450#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
451#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
452pub enum SyncStatusView {
453    /// Initial state. Not enough peers to do anything yet.
454    AwaitingPeers,
455    /// Not syncing / Done syncing.
456    NoSync,
457    /// Syncing using light-client headers to a recent epoch
458    EpochSync {
459        source_peer_height: BlockHeight,
460        source_peer_id: String,
461        attempt_time: String,
462    },
463    EpochSyncDone,
464    /// Downloading block headers for fast sync.
465    HeaderSync {
466        start_height: BlockHeight,
467        current_height: BlockHeight,
468        highest_height: BlockHeight,
469    },
470    /// State sync, with different states of state sync for different shards.
471    StateSync(StateSyncStatusView),
472    /// Sync state across all shards is done.
473    StateSyncDone,
474    /// Download and process blocks until the head reaches the head of the network.
475    BlockSync {
476        start_height: BlockHeight,
477        current_height: BlockHeight,
478        highest_height: BlockHeight,
479    },
480}
481
482#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
483#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
484pub struct StateSyncStatusView {
485    pub sync_hash: CryptoHash,
486    pub shard_sync_status: HashMap<ShardId, String>,
487    pub download_tasks: Vec<String>,
488    pub computation_tasks: Vec<String>,
489}
490
491#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
492#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
493pub struct PeerStoreView {
494    pub peer_states: Vec<KnownPeerStateView>,
495}
496
497#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
498#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
499pub struct RecentOutboundConnectionsView {
500    pub recent_outbound_connections: Vec<ConnectionInfoView>,
501}
502
503#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
504#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
505pub struct SnapshotHostsView {
506    pub hosts: Vec<SnapshotHostInfoView>,
507}
508
509#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
510#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
511pub struct EdgeView {
512    pub peer0: PeerId,
513    pub peer1: PeerId,
514    pub nonce: u64,
515}
516
517#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
518#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
519pub struct NetworkGraphView {
520    pub edges: Vec<EdgeView>,
521    pub next_hops: HashMap<PeerId, Vec<PeerId>>,
522}
523
524#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
525#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
526pub struct LabeledEdgeView {
527    pub peer0: u32,
528    pub peer1: u32,
529    pub nonce: u64,
530}
531
532#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Default)]
533#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
534pub struct EdgeCacheView {
535    pub peer_labels: HashMap<PeerId, u32>,
536    pub spanning_trees: HashMap<u32, Vec<LabeledEdgeView>>,
537}
538
539#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
540#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
541pub struct PeerDistancesView {
542    pub distance: Vec<Option<u32>>,
543    pub min_nonce: u64,
544}
545
546#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Default)]
547#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
548pub struct NetworkRoutesView {
549    pub edge_cache: EdgeCacheView,
550    pub local_edges: HashMap<PeerId, EdgeView>,
551    pub peer_distances: HashMap<PeerId, PeerDistancesView>,
552    pub my_distances: HashMap<PeerId, u32>,
553}
554
555#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
556#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
557pub struct ShardSyncDownloadView {
558    pub downloads: Vec<DownloadStatusView>,
559    pub status: String,
560}
561
562#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
563#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
564pub struct DownloadStatusView {
565    pub error: bool,
566    pub done: bool,
567}
568
569#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
570#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
571pub struct CatchupStatusView {
572    // This is the first block of the epoch that we are catching up
573    pub sync_block_hash: CryptoHash,
574    pub sync_block_height: BlockHeight,
575    // Status of all shards that need to sync
576    pub shard_sync_status: HashMap<ShardId, String>,
577    // Blocks that we need to catchup, if it is empty, it means catching up is done
578    pub blocks_to_catchup: Vec<BlockStatusView>,
579}
580
581#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
582#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
583pub struct RequestedStatePartsView {
584    // This is the first block of the epoch that was requested
585    pub block_hash: CryptoHash,
586    // All the part ids of the shards that were requested
587    pub shard_requested_parts: HashMap<ShardId, Vec<PartElapsedTimeView>>,
588}
589
590#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq)]
591#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
592pub struct BlockStatusView {
593    pub height: BlockHeight,
594    pub hash: CryptoHash,
595}
596
597impl BlockStatusView {
598    pub fn new(height: &BlockHeight, hash: &CryptoHash) -> BlockStatusView {
599        Self { height: *height, hash: *hash }
600    }
601}
602
603impl From<Tip> for BlockStatusView {
604    fn from(tip: Tip) -> Self {
605        Self { height: tip.height, hash: tip.last_block_hash }
606    }
607}
608
609#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone)]
610#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
611pub struct PartElapsedTimeView {
612    pub part_id: u64,
613    pub elapsed_ms: u128,
614}
615
616impl PartElapsedTimeView {
617    pub fn new(part_id: &u64, elapsed_ms: u128) -> PartElapsedTimeView {
618        Self { part_id: *part_id, elapsed_ms }
619    }
620}
621
622#[derive(serde::Serialize, serde::Deserialize, Debug)]
623#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
624pub struct BlockByChunksView {
625    pub height: BlockHeight,
626    pub hash: CryptoHash,
627    pub block_status: String,
628    pub chunk_status: String,
629}
630
631#[derive(serde::Serialize, serde::Deserialize, Debug)]
632#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
633pub struct ChainProcessingInfo {
634    pub num_blocks_in_processing: usize,
635    pub num_orphans: usize,
636    pub num_blocks_missing_chunks: usize,
637    /// contains processing info of recent blocks, ordered by height high to low
638    pub blocks_info: Vec<BlockProcessingInfo>,
639    /// contains processing info of chunks that we don't know which block it belongs to yet
640    pub floating_chunks_info: Vec<ChunkProcessingInfo>,
641}
642
643#[derive(serde::Serialize, serde::Deserialize, Debug)]
644#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
645pub struct BlockProcessingInfo {
646    pub height: BlockHeight,
647    pub hash: CryptoHash,
648    #[serde(with = "near_time::serde_utc_as_iso")]
649    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
650    pub received_timestamp: Utc,
651    /// Time (in ms) between when the block was first received and when it was processed
652    pub in_progress_ms: u128,
653    /// Time (in ms) that the block spent in the orphan pool. If the block was never put in the
654    /// orphan pool, it is None. If the block is still in the orphan pool, it is since the time
655    /// it was put into the pool until the current time.
656    pub orphaned_ms: Option<u128>,
657    /// Time (in ms) that the block spent in the missing chunks pool. If the block was never put in the
658    /// missing chunks pool, it is None. If the block is still in the missing chunks pool, it is
659    /// since the time it was put into the pool until the current time.
660    pub missing_chunks_ms: Option<u128>,
661    pub block_status: BlockProcessingStatus,
662    /// Only contains new chunks that belong to this block, if the block doesn't produce a new chunk
663    /// for a shard, the corresponding item will be None.
664    pub chunks_info: Vec<Option<ChunkProcessingInfo>>,
665}
666
667#[derive(
668    BorshSerialize,
669    BorshDeserialize,
670    Clone,
671    Debug,
672    PartialEq,
673    Eq,
674    serde::Serialize,
675    serde::Deserialize,
676)]
677#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
678pub enum BlockProcessingStatus {
679    Orphan,
680    WaitingForChunks,
681    InProcessing,
682    Accepted,
683    Error(String),
684    Dropped(DroppedReason),
685    Unknown,
686}
687
688#[derive(
689    BorshSerialize,
690    BorshDeserialize,
691    Clone,
692    Debug,
693    PartialEq,
694    Eq,
695    serde::Serialize,
696    serde::Deserialize,
697)]
698#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
699pub enum DroppedReason {
700    // If the node has already processed a block at this height
701    HeightProcessed,
702    // If the block processing pool is full
703    TooManyProcessingBlocks,
704}
705
706#[derive(serde::Serialize, serde::Deserialize, Debug)]
707#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
708pub struct ChunkProcessingInfo {
709    pub height_created: BlockHeight,
710    pub shard_id: ShardId,
711    pub chunk_hash: ChunkHash,
712    pub prev_block_hash: CryptoHash,
713    /// Account id of the validator who created this chunk
714    /// Theoretically this field should never be None unless there is some database corruption.
715    pub created_by: Option<AccountId>,
716    pub status: ChunkProcessingStatus,
717    /// Timestamp of first time when we request for this chunk.
718    #[serde(with = "near_time::serde_opt_utc_as_iso")]
719    #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
720    pub requested_timestamp: Option<Utc>,
721    /// Timestamp of when the chunk is complete
722    #[serde(with = "near_time::serde_opt_utc_as_iso")]
723    #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
724    pub completed_timestamp: Option<Utc>,
725    /// Time (in millis) that it takes between when the chunk is requested and when it is completed.
726    pub request_duration: Option<u64>,
727    pub chunk_parts_collection: Vec<PartCollectionInfo>,
728}
729
730#[derive(serde::Serialize, serde::Deserialize, Debug)]
731#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
732pub struct PartCollectionInfo {
733    pub part_owner: AccountId,
734    // Time when the part is received through any message
735    #[serde(with = "near_time::serde_opt_utc_as_iso")]
736    #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
737    pub received_time: Option<Utc>,
738    // Time when we receive a PartialEncodedChunkForward containing this part
739    #[serde(with = "near_time::serde_opt_utc_as_iso")]
740    #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
741    pub forwarded_received_time: Option<Utc>,
742    // Time when we receive the PartialEncodedChunk message containing this part
743    #[serde(with = "near_time::serde_opt_utc_as_iso")]
744    #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
745    pub chunk_received_time: Option<Utc>,
746}
747
748#[derive(serde::Serialize, serde::Deserialize, Debug)]
749#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
750pub enum ChunkProcessingStatus {
751    NeedToRequest,
752    Requested,
753    Completed,
754}
755
756#[derive(serde::Serialize, serde::Deserialize, Debug)]
757#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
758pub struct DetailedDebugStatus {
759    pub network_info: NetworkInfoView,
760    pub sync_status: String,
761    pub catchup_status: Vec<CatchupStatusView>,
762    pub current_head_status: BlockStatusView,
763    pub current_header_head_status: BlockStatusView,
764    pub block_production_delay_millis: u64,
765}
766
767// TODO: add more information to status.
768#[derive(serde::Serialize, serde::Deserialize, Debug)]
769#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
770pub struct StatusResponse {
771    /// Binary version.
772    pub version: Version,
773    /// Unique chain id.
774    pub chain_id: String,
775    /// Currently active protocol version.
776    pub protocol_version: u32,
777    /// Latest protocol version that this client supports.
778    pub latest_protocol_version: u32,
779    /// Address for RPC server.  None if node doesn’t have RPC endpoint enabled.
780    #[serde(skip_serializing_if = "Option::is_none")]
781    pub rpc_addr: Option<String>,
782    /// Current epoch validators.
783    pub validators: Vec<ValidatorInfo>,
784    /// Sync status of the node.
785    pub sync_info: StatusSyncInfo,
786    /// Validator id of the node
787    pub validator_account_id: Option<AccountId>,
788    /// Public key of the validator.
789    pub validator_public_key: Option<PublicKey>,
790    /// Public key of the node.
791    pub node_public_key: PublicKey,
792    /// Deprecated; same as `validator_public_key` which you should use instead.
793    pub node_key: Option<PublicKey>,
794    /// Uptime of the node.
795    pub uptime_sec: i64,
796    /// Genesis hash of the chain.
797    pub genesis_hash: CryptoHash,
798    /// Information about last blocks, network, epoch and chain & chunk info.
799    #[serde(skip_serializing_if = "Option::is_none")]
800    pub detailed_debug_status: Option<DetailedDebugStatus>,
801}
802
803#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
804#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
805pub struct BlockHeaderView {
806    pub height: BlockHeight,
807    pub prev_height: Option<BlockHeight>,
808    pub epoch_id: CryptoHash,
809    pub next_epoch_id: CryptoHash,
810    pub hash: CryptoHash,
811    pub prev_hash: CryptoHash,
812    pub prev_state_root: CryptoHash,
813    pub block_body_hash: Option<CryptoHash>,
814    pub chunk_receipts_root: CryptoHash,
815    pub chunk_headers_root: CryptoHash,
816    pub chunk_tx_root: CryptoHash,
817    pub outcome_root: CryptoHash,
818    pub chunks_included: u64,
819    pub challenges_root: CryptoHash,
820    /// Legacy json number. Should not be used.
821    pub timestamp: u64,
822    #[serde(with = "dec_format")]
823    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
824    pub timestamp_nanosec: u64,
825    pub random_value: CryptoHash,
826    pub validator_proposals: Vec<ValidatorStakeView>,
827    pub chunk_mask: Vec<bool>,
828    #[serde(with = "dec_format")]
829    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
830    pub gas_price: Balance,
831    pub block_ordinal: Option<NumBlocks>,
832    /// TODO(2271): deprecated.
833    #[serde(with = "dec_format")]
834    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
835    pub rent_paid: Balance,
836    /// TODO(2271): deprecated.
837    #[serde(with = "dec_format")]
838    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
839    pub validator_reward: Balance,
840    #[serde(with = "dec_format")]
841    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
842    pub total_supply: Balance,
843    // Deprecated
844    pub challenges_result: Vec<SlashedValidator>,
845    pub last_final_block: CryptoHash,
846    pub last_ds_final_block: CryptoHash,
847    pub next_bp_hash: CryptoHash,
848    pub block_merkle_root: CryptoHash,
849    pub epoch_sync_data_hash: Option<CryptoHash>,
850    pub approvals: Vec<Option<Box<Signature>>>,
851    pub signature: Signature,
852    pub latest_protocol_version: ProtocolVersion,
853    pub chunk_endorsements: Option<Vec<Vec<u8>>>,
854}
855
856impl From<BlockHeader> for BlockHeaderView {
857    fn from(header: BlockHeader) -> Self {
858        Self {
859            height: header.height(),
860            prev_height: header.prev_height(),
861            epoch_id: header.epoch_id().0,
862            next_epoch_id: header.next_epoch_id().0,
863            hash: *header.hash(),
864            prev_hash: *header.prev_hash(),
865            prev_state_root: *header.prev_state_root(),
866            block_body_hash: header.block_body_hash(),
867            chunk_receipts_root: *header.prev_chunk_outgoing_receipts_root(),
868            chunk_headers_root: *header.chunk_headers_root(),
869            chunk_tx_root: *header.chunk_tx_root(),
870            chunks_included: header.chunks_included(),
871            challenges_root: CryptoHash::default(),
872            outcome_root: *header.outcome_root(),
873            timestamp: header.raw_timestamp(),
874            timestamp_nanosec: header.raw_timestamp(),
875            random_value: *header.random_value(),
876            validator_proposals: header.prev_validator_proposals().map(Into::into).collect(),
877            chunk_mask: header.chunk_mask().to_vec(),
878            block_ordinal: if header.block_ordinal() != 0 {
879                Some(header.block_ordinal())
880            } else {
881                None
882            },
883            gas_price: header.next_gas_price(),
884            rent_paid: 0,
885            validator_reward: 0,
886            total_supply: header.total_supply(),
887            challenges_result: vec![],
888            last_final_block: *header.last_final_block(),
889            last_ds_final_block: *header.last_ds_final_block(),
890            next_bp_hash: *header.next_bp_hash(),
891            block_merkle_root: *header.block_merkle_root(),
892            epoch_sync_data_hash: header.epoch_sync_data_hash(),
893            approvals: header.approvals().to_vec(),
894            signature: header.signature().clone(),
895            latest_protocol_version: header.latest_protocol_version(),
896            chunk_endorsements: header.chunk_endorsements().map(|bitmap| bitmap.bytes()),
897        }
898    }
899}
900
901impl From<BlockHeaderView> for BlockHeader {
902    fn from(view: BlockHeaderView) -> Self {
903        BlockHeader::from_view(
904            &view.hash,
905            view.latest_protocol_version,
906            view.height,
907            view.prev_hash,
908            view.block_body_hash.unwrap_or_default(),
909            view.prev_state_root,
910            view.chunk_receipts_root,
911            view.chunk_headers_root,
912            view.chunk_tx_root,
913            view.outcome_root,
914            view.timestamp,
915            view.random_value,
916            view.validator_proposals.into_iter().map(|v| v.into_validator_stake()).collect(),
917            view.chunk_mask,
918            view.block_ordinal.unwrap_or(0),
919            EpochId(view.epoch_id),
920            EpochId(view.next_epoch_id),
921            view.gas_price,
922            view.total_supply,
923            view.signature,
924            view.last_final_block,
925            view.last_ds_final_block,
926            view.epoch_sync_data_hash,
927            view.approvals,
928            view.next_bp_hash,
929            view.block_merkle_root,
930            view.prev_height.unwrap_or_default(),
931            view.chunk_endorsements.map(|bytes| ChunkEndorsementsBitmap::from_bytes(bytes)),
932        )
933    }
934}
935
936#[derive(
937    PartialEq,
938    Eq,
939    Debug,
940    Clone,
941    BorshDeserialize,
942    BorshSerialize,
943    serde::Serialize,
944    serde::Deserialize,
945)]
946#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
947pub struct BlockHeaderInnerLiteView {
948    pub height: BlockHeight,
949    pub epoch_id: CryptoHash,
950    pub next_epoch_id: CryptoHash,
951    pub prev_state_root: CryptoHash,
952    pub outcome_root: CryptoHash,
953    /// Legacy json number. Should not be used.
954    pub timestamp: u64,
955    #[serde(with = "dec_format")]
956    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
957    pub timestamp_nanosec: u64,
958    pub next_bp_hash: CryptoHash,
959    pub block_merkle_root: CryptoHash,
960}
961
962impl From<BlockHeader> for BlockHeaderInnerLiteView {
963    fn from(header: BlockHeader) -> Self {
964        let inner_lite = header.inner_lite();
965        BlockHeaderInnerLiteView {
966            height: inner_lite.height,
967            epoch_id: inner_lite.epoch_id.0,
968            next_epoch_id: inner_lite.next_epoch_id.0,
969            prev_state_root: inner_lite.prev_state_root,
970            outcome_root: inner_lite.prev_outcome_root,
971            timestamp: inner_lite.timestamp,
972            timestamp_nanosec: inner_lite.timestamp,
973            next_bp_hash: inner_lite.next_bp_hash,
974            block_merkle_root: inner_lite.block_merkle_root,
975        }
976    }
977}
978
979impl From<BlockHeaderInnerLiteView> for BlockHeaderInnerLite {
980    fn from(view: BlockHeaderInnerLiteView) -> Self {
981        BlockHeaderInnerLite {
982            height: view.height,
983            epoch_id: EpochId(view.epoch_id),
984            next_epoch_id: EpochId(view.next_epoch_id),
985            prev_state_root: view.prev_state_root,
986            prev_outcome_root: view.outcome_root,
987            timestamp: view.timestamp_nanosec,
988            next_bp_hash: view.next_bp_hash,
989            block_merkle_root: view.block_merkle_root,
990        }
991    }
992}
993
994#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
995#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
996pub struct ChunkHeaderView {
997    pub chunk_hash: CryptoHash,
998    pub prev_block_hash: CryptoHash,
999    pub outcome_root: CryptoHash,
1000    pub prev_state_root: StateRoot,
1001    pub encoded_merkle_root: CryptoHash,
1002    pub encoded_length: u64,
1003    pub height_created: BlockHeight,
1004    pub height_included: BlockHeight,
1005    pub shard_id: ShardId,
1006    pub gas_used: Gas,
1007    pub gas_limit: Gas,
1008    /// TODO(2271): deprecated.
1009    #[serde(with = "dec_format")]
1010    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1011    pub rent_paid: Balance,
1012    /// TODO(2271): deprecated.
1013    #[serde(with = "dec_format")]
1014    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1015    pub validator_reward: Balance,
1016    #[serde(with = "dec_format")]
1017    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1018    pub balance_burnt: Balance,
1019    pub outgoing_receipts_root: CryptoHash,
1020    pub tx_root: CryptoHash,
1021    pub validator_proposals: Vec<ValidatorStakeView>,
1022    pub congestion_info: Option<CongestionInfoView>,
1023    pub bandwidth_requests: Option<BandwidthRequests>,
1024    pub signature: Signature,
1025}
1026
1027impl ChunkHeaderView {
1028    pub fn is_new_chunk(&self, block_height: BlockHeight) -> bool {
1029        self.height_included == block_height
1030    }
1031}
1032
1033impl From<ShardChunkHeader> for ChunkHeaderView {
1034    fn from(chunk: ShardChunkHeader) -> Self {
1035        let hash = chunk.chunk_hash();
1036        let signature = chunk.signature().clone();
1037        let height_included = chunk.height_included();
1038        let inner = chunk.take_inner();
1039        ChunkHeaderView {
1040            chunk_hash: hash.0,
1041            prev_block_hash: *inner.prev_block_hash(),
1042            outcome_root: *inner.prev_outcome_root(),
1043            prev_state_root: *inner.prev_state_root(),
1044            encoded_merkle_root: *inner.encoded_merkle_root(),
1045            encoded_length: inner.encoded_length(),
1046            height_created: inner.height_created(),
1047            height_included,
1048            shard_id: inner.shard_id(),
1049            gas_used: inner.prev_gas_used(),
1050            gas_limit: inner.gas_limit(),
1051            rent_paid: 0,
1052            validator_reward: 0,
1053            balance_burnt: inner.prev_balance_burnt(),
1054            outgoing_receipts_root: *inner.prev_outgoing_receipts_root(),
1055            tx_root: *inner.tx_root(),
1056            validator_proposals: inner.prev_validator_proposals().map(Into::into).collect(),
1057            congestion_info: Some(inner.congestion_info().into()),
1058            bandwidth_requests: inner.bandwidth_requests().cloned(),
1059            signature,
1060        }
1061    }
1062}
1063
1064impl From<ChunkHeaderView> for ShardChunkHeader {
1065    fn from(view: ChunkHeaderView) -> Self {
1066        match (view.bandwidth_requests, view.congestion_info) {
1067            (Some(bandwidth_requests), Some(congestion_info)) => {
1068                let mut header = ShardChunkHeaderV3 {
1069                    inner: ShardChunkHeaderInner::V4(ShardChunkHeaderInnerV4 {
1070                        prev_block_hash: view.prev_block_hash,
1071                        prev_state_root: view.prev_state_root,
1072                        prev_outcome_root: view.outcome_root,
1073                        encoded_merkle_root: view.encoded_merkle_root,
1074                        encoded_length: view.encoded_length,
1075                        height_created: view.height_created,
1076                        shard_id: view.shard_id,
1077                        prev_gas_used: view.gas_used,
1078                        gas_limit: view.gas_limit,
1079                        prev_balance_burnt: view.balance_burnt,
1080                        prev_outgoing_receipts_root: view.outgoing_receipts_root,
1081                        tx_root: view.tx_root,
1082                        prev_validator_proposals: view
1083                            .validator_proposals
1084                            .into_iter()
1085                            .map(Into::into)
1086                            .collect(),
1087                        congestion_info: congestion_info.into(),
1088                        bandwidth_requests,
1089                    }),
1090                    height_included: view.height_included,
1091                    signature: view.signature,
1092                    hash: ChunkHash::default(),
1093                };
1094                header.init();
1095                ShardChunkHeader::V3(header)
1096            }
1097            (None, Some(congestion_info)) => {
1098                let mut header = ShardChunkHeaderV3 {
1099                    inner: ShardChunkHeaderInner::V3(ShardChunkHeaderInnerV3 {
1100                        prev_block_hash: view.prev_block_hash,
1101                        prev_state_root: view.prev_state_root,
1102                        prev_outcome_root: view.outcome_root,
1103                        encoded_merkle_root: view.encoded_merkle_root,
1104                        encoded_length: view.encoded_length,
1105                        height_created: view.height_created,
1106                        shard_id: view.shard_id,
1107                        prev_gas_used: view.gas_used,
1108                        gas_limit: view.gas_limit,
1109                        prev_balance_burnt: view.balance_burnt,
1110                        prev_outgoing_receipts_root: view.outgoing_receipts_root,
1111                        tx_root: view.tx_root,
1112                        prev_validator_proposals: view
1113                            .validator_proposals
1114                            .into_iter()
1115                            .map(Into::into)
1116                            .collect(),
1117                        congestion_info: congestion_info.into(),
1118                    }),
1119                    height_included: view.height_included,
1120                    signature: view.signature,
1121                    hash: ChunkHash::default(),
1122                };
1123                header.init();
1124                ShardChunkHeader::V3(header)
1125            }
1126            _ => {
1127                let mut header = ShardChunkHeaderV3 {
1128                    inner: ShardChunkHeaderInner::V2(ShardChunkHeaderInnerV2 {
1129                        prev_block_hash: view.prev_block_hash,
1130                        prev_state_root: view.prev_state_root,
1131                        prev_outcome_root: view.outcome_root,
1132                        encoded_merkle_root: view.encoded_merkle_root,
1133                        encoded_length: view.encoded_length,
1134                        height_created: view.height_created,
1135                        shard_id: view.shard_id,
1136                        prev_gas_used: view.gas_used,
1137                        gas_limit: view.gas_limit,
1138                        prev_balance_burnt: view.balance_burnt,
1139                        prev_outgoing_receipts_root: view.outgoing_receipts_root,
1140                        tx_root: view.tx_root,
1141                        prev_validator_proposals: view
1142                            .validator_proposals
1143                            .into_iter()
1144                            .map(Into::into)
1145                            .collect(),
1146                    }),
1147                    height_included: view.height_included,
1148                    signature: view.signature,
1149                    hash: ChunkHash::default(),
1150                };
1151                header.init();
1152                ShardChunkHeader::V3(header)
1153            }
1154        }
1155    }
1156}
1157
1158#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
1159#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1160pub struct BlockView {
1161    pub author: AccountId,
1162    pub header: BlockHeaderView,
1163    pub chunks: Vec<ChunkHeaderView>,
1164}
1165
1166impl BlockView {
1167    pub fn from_author_block(author: AccountId, block: Block) -> Self {
1168        BlockView {
1169            author,
1170            header: block.header().clone().into(),
1171            chunks: block.chunks().iter_deprecated().cloned().map(Into::into).collect(),
1172        }
1173    }
1174}
1175
1176#[derive(serde::Serialize, serde::Deserialize, Debug)]
1177#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1178pub struct ChunkView {
1179    pub author: AccountId,
1180    pub header: ChunkHeaderView,
1181    pub transactions: Vec<SignedTransactionView>,
1182    pub receipts: Vec<ReceiptView>,
1183}
1184
1185impl ChunkView {
1186    pub fn from_author_chunk(author: AccountId, chunk: ShardChunk) -> Self {
1187        match chunk {
1188            ShardChunk::V1(chunk) => Self {
1189                author,
1190                header: ShardChunkHeader::V1(chunk.header).into(),
1191                transactions: chunk.transactions.into_iter().map(Into::into).collect(),
1192                receipts: chunk.prev_outgoing_receipts.into_iter().map(Into::into).collect(),
1193            },
1194            ShardChunk::V2(chunk) => Self {
1195                author,
1196                header: chunk.header.into(),
1197                transactions: chunk.transactions.into_iter().map(Into::into).collect(),
1198                receipts: chunk.prev_outgoing_receipts.into_iter().map(Into::into).collect(),
1199            },
1200        }
1201    }
1202}
1203
1204#[serde_as]
1205#[derive(
1206    BorshSerialize,
1207    BorshDeserialize,
1208    Clone,
1209    Debug,
1210    PartialEq,
1211    Eq,
1212    serde::Serialize,
1213    serde::Deserialize,
1214)]
1215#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1216pub enum ActionView {
1217    CreateAccount,
1218    DeployContract {
1219        #[serde_as(as = "Base64")]
1220        #[cfg_attr(
1221            feature = "schemars",
1222            schemars(schema_with = "crate::serialize::base64_schema")
1223        )]
1224        code: Vec<u8>,
1225    },
1226    FunctionCall {
1227        method_name: String,
1228        args: FunctionArgs,
1229        gas: Gas,
1230        #[serde(with = "dec_format")]
1231        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1232        deposit: Balance,
1233    },
1234    Transfer {
1235        #[serde(with = "dec_format")]
1236        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1237        deposit: Balance,
1238    },
1239    Stake {
1240        #[serde(with = "dec_format")]
1241        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1242        stake: Balance,
1243        public_key: PublicKey,
1244    },
1245    AddKey {
1246        public_key: PublicKey,
1247        access_key: AccessKeyView,
1248    },
1249    DeleteKey {
1250        public_key: PublicKey,
1251    },
1252    DeleteAccount {
1253        beneficiary_id: AccountId,
1254    },
1255    Delegate {
1256        delegate_action: DelegateAction,
1257        signature: Signature,
1258    },
1259    DeployGlobalContract {
1260        #[serde_as(as = "Base64")]
1261        #[cfg_attr(
1262            feature = "schemars",
1263            schemars(schema_with = "crate::serialize::base64_schema")
1264        )]
1265        code: Vec<u8>,
1266    },
1267    DeployGlobalContractByAccountId {
1268        #[serde_as(as = "Base64")]
1269        #[cfg_attr(
1270            feature = "schemars",
1271            schemars(schema_with = "crate::serialize::base64_schema")
1272        )]
1273        code: Vec<u8>,
1274    },
1275    UseGlobalContract {
1276        code_hash: CryptoHash,
1277    },
1278    UseGlobalContractByAccountId {
1279        account_id: AccountId,
1280    },
1281}
1282
1283impl From<Action> for ActionView {
1284    fn from(action: Action) -> Self {
1285        match action {
1286            Action::CreateAccount(_) => ActionView::CreateAccount,
1287            Action::DeployContract(action) => {
1288                let code = hash(&action.code).as_ref().to_vec();
1289                ActionView::DeployContract { code }
1290            }
1291            Action::FunctionCall(action) => ActionView::FunctionCall {
1292                method_name: action.method_name,
1293                args: action.args.into(),
1294                gas: action.gas,
1295                deposit: action.deposit,
1296            },
1297            Action::Transfer(action) => ActionView::Transfer { deposit: action.deposit },
1298            Action::Stake(action) => {
1299                ActionView::Stake { stake: action.stake, public_key: action.public_key }
1300            }
1301            Action::AddKey(action) => ActionView::AddKey {
1302                public_key: action.public_key,
1303                access_key: action.access_key.into(),
1304            },
1305            Action::DeleteKey(action) => ActionView::DeleteKey { public_key: action.public_key },
1306            Action::DeleteAccount(action) => {
1307                ActionView::DeleteAccount { beneficiary_id: action.beneficiary_id }
1308            }
1309            Action::Delegate(action) => ActionView::Delegate {
1310                delegate_action: action.delegate_action,
1311                signature: action.signature,
1312            },
1313            Action::DeployGlobalContract(action) => {
1314                let code = hash(&action.code).as_ref().to_vec();
1315                match action.deploy_mode {
1316                    GlobalContractDeployMode::CodeHash => ActionView::DeployGlobalContract { code },
1317                    GlobalContractDeployMode::AccountId => {
1318                        ActionView::DeployGlobalContractByAccountId { code }
1319                    }
1320                }
1321            }
1322            Action::UseGlobalContract(action) => match action.contract_identifier {
1323                GlobalContractIdentifier::CodeHash(code_hash) => {
1324                    ActionView::UseGlobalContract { code_hash }
1325                }
1326                GlobalContractIdentifier::AccountId(account_id) => {
1327                    ActionView::UseGlobalContractByAccountId { account_id }
1328                }
1329            },
1330        }
1331    }
1332}
1333
1334impl TryFrom<ActionView> for Action {
1335    type Error = Box<dyn std::error::Error + Send + Sync>;
1336
1337    fn try_from(action_view: ActionView) -> Result<Self, Self::Error> {
1338        Ok(match action_view {
1339            ActionView::CreateAccount => Action::CreateAccount(CreateAccountAction {}),
1340            ActionView::DeployContract { code } => {
1341                Action::DeployContract(DeployContractAction { code })
1342            }
1343            ActionView::FunctionCall { method_name, args, gas, deposit } => {
1344                Action::FunctionCall(Box::new(FunctionCallAction {
1345                    method_name,
1346                    args: args.into(),
1347                    gas,
1348                    deposit,
1349                }))
1350            }
1351            ActionView::Transfer { deposit } => Action::Transfer(TransferAction { deposit }),
1352            ActionView::Stake { stake, public_key } => {
1353                Action::Stake(Box::new(StakeAction { stake, public_key }))
1354            }
1355            ActionView::AddKey { public_key, access_key } => {
1356                Action::AddKey(Box::new(AddKeyAction { public_key, access_key: access_key.into() }))
1357            }
1358            ActionView::DeleteKey { public_key } => {
1359                Action::DeleteKey(Box::new(DeleteKeyAction { public_key }))
1360            }
1361            ActionView::DeleteAccount { beneficiary_id } => {
1362                Action::DeleteAccount(DeleteAccountAction { beneficiary_id })
1363            }
1364            ActionView::Delegate { delegate_action, signature } => {
1365                Action::Delegate(Box::new(SignedDelegateAction { delegate_action, signature }))
1366            }
1367            ActionView::DeployGlobalContract { code } => {
1368                Action::DeployGlobalContract(DeployGlobalContractAction {
1369                    code: code.into(),
1370                    deploy_mode: GlobalContractDeployMode::CodeHash,
1371                })
1372            }
1373            ActionView::DeployGlobalContractByAccountId { code } => {
1374                Action::DeployGlobalContract(DeployGlobalContractAction {
1375                    code: code.into(),
1376                    deploy_mode: GlobalContractDeployMode::AccountId,
1377                })
1378            }
1379            ActionView::UseGlobalContract { code_hash } => {
1380                Action::UseGlobalContract(Box::new(UseGlobalContractAction {
1381                    contract_identifier: GlobalContractIdentifier::CodeHash(code_hash),
1382                }))
1383            }
1384            ActionView::UseGlobalContractByAccountId { account_id } => {
1385                Action::UseGlobalContract(Box::new(UseGlobalContractAction {
1386                    contract_identifier: GlobalContractIdentifier::AccountId(account_id),
1387                }))
1388            }
1389        })
1390    }
1391}
1392
1393#[derive(
1394    BorshSerialize,
1395    BorshDeserialize,
1396    Debug,
1397    PartialEq,
1398    Eq,
1399    Clone,
1400    serde::Serialize,
1401    serde::Deserialize,
1402)]
1403#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1404pub struct SignedTransactionView {
1405    pub signer_id: AccountId,
1406    pub public_key: PublicKey,
1407    pub nonce: Nonce,
1408    pub receiver_id: AccountId,
1409    pub actions: Vec<ActionView>,
1410    // Default value used when deserializing SignedTransactionView which are missing the `priority_fee` field.
1411    // Data which is missing this field was serialized before the introduction of priority_fee.
1412    // priority_fee for Transaction::V0 => None, SignedTransactionView => 0
1413    #[serde(default)]
1414    pub priority_fee: u64,
1415    pub signature: Signature,
1416    pub hash: CryptoHash,
1417}
1418
1419impl From<SignedTransaction> for SignedTransactionView {
1420    fn from(signed_tx: SignedTransaction) -> Self {
1421        let hash = signed_tx.get_hash();
1422        let transaction = signed_tx.transaction;
1423        let priority_fee = transaction.priority_fee().unwrap_or_default();
1424        SignedTransactionView {
1425            signer_id: transaction.signer_id().clone(),
1426            public_key: transaction.public_key().clone(),
1427            nonce: transaction.nonce(),
1428            receiver_id: transaction.receiver_id().clone(),
1429            actions: transaction.take_actions().into_iter().map(|action| action.into()).collect(),
1430            signature: signed_tx.signature,
1431            hash,
1432            priority_fee,
1433        }
1434    }
1435}
1436
1437#[serde_as]
1438#[derive(
1439    BorshSerialize,
1440    BorshDeserialize,
1441    serde::Serialize,
1442    serde::Deserialize,
1443    PartialEq,
1444    Eq,
1445    Clone,
1446    Default,
1447)]
1448#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1449pub enum FinalExecutionStatus {
1450    /// The execution has not yet started.
1451    #[default]
1452    NotStarted,
1453    /// The execution has started and still going.
1454    Started,
1455    /// The execution has failed with the given error.
1456    Failure(TxExecutionError),
1457    /// The execution has succeeded and returned some value or an empty vec encoded in base64.
1458    SuccessValue(
1459        #[serde_as(as = "Base64")]
1460        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1461        Vec<u8>,
1462    ),
1463}
1464
1465impl fmt::Debug for FinalExecutionStatus {
1466    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1467        match self {
1468            FinalExecutionStatus::NotStarted => f.write_str("NotStarted"),
1469            FinalExecutionStatus::Started => f.write_str("Started"),
1470            FinalExecutionStatus::Failure(e) => f.write_fmt(format_args!("Failure({:?})", e)),
1471            FinalExecutionStatus::SuccessValue(v) => {
1472                f.write_fmt(format_args!("SuccessValue({})", AbbrBytes(v)))
1473            }
1474        }
1475    }
1476}
1477
1478#[derive(
1479    BorshSerialize,
1480    BorshDeserialize,
1481    Debug,
1482    PartialEq,
1483    Eq,
1484    Clone,
1485    serde::Serialize,
1486    serde::Deserialize,
1487)]
1488pub enum ServerError {
1489    TxExecutionError(TxExecutionError),
1490    Timeout,
1491    Closed,
1492}
1493
1494#[serde_as]
1495#[derive(
1496    BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone,
1497)]
1498#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1499pub enum ExecutionStatusView {
1500    /// The execution is pending or unknown.
1501    Unknown,
1502    /// The execution has failed.
1503    Failure(TxExecutionError),
1504    /// The final action succeeded and returned some value or an empty vec encoded in base64.
1505    SuccessValue(
1506        #[serde_as(as = "Base64")]
1507        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1508        Vec<u8>,
1509    ),
1510    /// The final action of the receipt returned a promise or the signed transaction was converted
1511    /// to a receipt. Contains the receipt_id of the generated receipt.
1512    SuccessReceiptId(CryptoHash),
1513}
1514
1515impl fmt::Debug for ExecutionStatusView {
1516    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1517        match self {
1518            ExecutionStatusView::Unknown => f.write_str("Unknown"),
1519            ExecutionStatusView::Failure(e) => f.write_fmt(format_args!("Failure({:?})", e)),
1520            ExecutionStatusView::SuccessValue(v) => {
1521                f.write_fmt(format_args!("SuccessValue({})", AbbrBytes(v)))
1522            }
1523            ExecutionStatusView::SuccessReceiptId(receipt_id) => {
1524                f.write_fmt(format_args!("SuccessReceiptId({})", receipt_id))
1525            }
1526        }
1527    }
1528}
1529
1530impl From<ExecutionStatus> for ExecutionStatusView {
1531    fn from(outcome: ExecutionStatus) -> Self {
1532        match outcome {
1533            ExecutionStatus::Unknown => ExecutionStatusView::Unknown,
1534            ExecutionStatus::Failure(e) => ExecutionStatusView::Failure(e),
1535            ExecutionStatus::SuccessValue(v) => ExecutionStatusView::SuccessValue(v),
1536            ExecutionStatus::SuccessReceiptId(receipt_id) => {
1537                ExecutionStatusView::SuccessReceiptId(receipt_id)
1538            }
1539        }
1540    }
1541}
1542
1543#[derive(
1544    BorshSerialize,
1545    BorshDeserialize,
1546    PartialEq,
1547    Clone,
1548    Eq,
1549    Debug,
1550    serde::Serialize,
1551    serde::Deserialize,
1552)]
1553#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1554pub struct CostGasUsed {
1555    pub cost_category: String,
1556    pub cost: String,
1557    #[serde(with = "dec_format")]
1558    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1559    pub gas_used: Gas,
1560}
1561
1562#[derive(
1563    BorshSerialize,
1564    BorshDeserialize,
1565    PartialEq,
1566    Clone,
1567    Eq,
1568    Debug,
1569    serde::Serialize,
1570    serde::Deserialize,
1571)]
1572#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1573pub struct ExecutionMetadataView {
1574    pub version: u32,
1575    pub gas_profile: Option<Vec<CostGasUsed>>,
1576}
1577
1578impl Default for ExecutionMetadataView {
1579    fn default() -> Self {
1580        ExecutionMetadata::V1.into()
1581    }
1582}
1583
1584impl From<ExecutionMetadata> for ExecutionMetadataView {
1585    fn from(metadata: ExecutionMetadata) -> Self {
1586        let version = match metadata {
1587            ExecutionMetadata::V1 => 1,
1588            ExecutionMetadata::V2(_) => 2,
1589            ExecutionMetadata::V3(_) => 3,
1590        };
1591        let mut gas_profile = match metadata {
1592            ExecutionMetadata::V1 => None,
1593            ExecutionMetadata::V2(profile_data) => {
1594                // Add actions, wasm op, and ext costs in groups.
1595
1596                // actions should use the old format, since `ActionCosts`
1597                // includes more detailed entries than were present in the old
1598                // profile
1599                let mut costs: Vec<CostGasUsed> = profile_data
1600                    .legacy_action_costs()
1601                    .into_iter()
1602                    .filter(|&(_, gas)| gas > 0)
1603                    .map(|(name, gas)| CostGasUsed::action(name.to_string(), gas))
1604                    .collect();
1605
1606                // wasm op is a single cost, for historical reasons it is inaccurately displayed as "wasm host"
1607                costs.push(CostGasUsed::wasm_host(
1608                    "WASM_INSTRUCTION".to_string(),
1609                    profile_data.get_wasm_cost(),
1610                ));
1611
1612                // ext costs are 1-to-1, except for those added later which we will display as 0
1613                for ext_cost in ExtCosts::iter() {
1614                    costs.push(CostGasUsed::wasm_host(
1615                        format!("{:?}", ext_cost).to_ascii_uppercase(),
1616                        profile_data.get_ext_cost(ext_cost),
1617                    ));
1618                }
1619
1620                Some(costs)
1621            }
1622            ExecutionMetadata::V3(profile) => {
1623                // Add actions, wasm op, and ext costs in groups.
1624                // actions costs are 1-to-1
1625                let mut costs: Vec<CostGasUsed> = ActionCosts::iter()
1626                    .filter_map(|cost| {
1627                        let gas_used = profile.get_action_cost(cost);
1628                        (gas_used > 0).then(|| {
1629                            CostGasUsed::action(
1630                                format!("{:?}", cost).to_ascii_uppercase(),
1631                                gas_used,
1632                            )
1633                        })
1634                    })
1635                    .collect();
1636
1637                // wasm op is a single cost, for historical reasons it is inaccurately displayed as "wasm host"
1638                let wasm_gas_used = profile.get_wasm_cost();
1639                if wasm_gas_used > 0 {
1640                    costs.push(CostGasUsed::wasm_host(
1641                        "WASM_INSTRUCTION".to_string(),
1642                        wasm_gas_used,
1643                    ));
1644                }
1645
1646                // ext costs are 1-to-1
1647                for ext_cost in ExtCosts::iter() {
1648                    let gas_used = profile.get_ext_cost(ext_cost);
1649                    if gas_used > 0 {
1650                        costs.push(CostGasUsed::wasm_host(
1651                            format!("{:?}", ext_cost).to_ascii_uppercase(),
1652                            gas_used,
1653                        ));
1654                    }
1655                }
1656
1657                Some(costs)
1658            }
1659        };
1660        if let Some(ref mut costs) = gas_profile {
1661            // The order doesn't really matter, but the default one is just
1662            // historical, which is especially unintuitive, so let's sort
1663            // lexicographically.
1664            //
1665            // Can't `sort_by_key` here because lifetime inference in
1666            // closures is limited.
1667            costs.sort_by(|lhs, rhs| {
1668                lhs.cost_category.cmp(&rhs.cost_category).then_with(|| lhs.cost.cmp(&rhs.cost))
1669            });
1670        }
1671        ExecutionMetadataView { version, gas_profile }
1672    }
1673}
1674
1675impl CostGasUsed {
1676    pub fn action(cost: String, gas_used: Gas) -> Self {
1677        Self { cost_category: "ACTION_COST".to_string(), cost, gas_used }
1678    }
1679
1680    pub fn wasm_host(cost: String, gas_used: Gas) -> Self {
1681        Self { cost_category: "WASM_HOST_COST".to_string(), cost, gas_used }
1682    }
1683}
1684
1685#[derive(
1686    BorshSerialize,
1687    BorshDeserialize,
1688    Debug,
1689    Clone,
1690    PartialEq,
1691    Eq,
1692    serde::Serialize,
1693    serde::Deserialize,
1694)]
1695#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1696pub struct ExecutionOutcomeView {
1697    /// Logs from this transaction or receipt.
1698    pub logs: Vec<String>,
1699    /// Receipt IDs generated by this transaction or receipt.
1700    pub receipt_ids: Vec<CryptoHash>,
1701    /// The amount of the gas burnt by the given transaction or receipt.
1702    pub gas_burnt: Gas,
1703    /// The amount of tokens burnt corresponding to the burnt gas amount.
1704    /// This value doesn't always equal to the `gas_burnt` multiplied by the gas price, because
1705    /// the prepaid gas price might be lower than the actual gas price and it creates a deficit.
1706    /// `tokens_burnt` also contains the penalty subtracted from refunds, while
1707    /// `gas_burnt` only contains the gas that we actually burn for the execution.
1708    #[serde(with = "dec_format")]
1709    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1710    pub tokens_burnt: Balance,
1711    /// The id of the account on which the execution happens. For transaction this is signer_id,
1712    /// for receipt this is receiver_id.
1713    pub executor_id: AccountId,
1714    /// Execution status. Contains the result in case of successful execution.
1715    pub status: ExecutionStatusView,
1716    /// Execution metadata, versioned
1717    #[serde(default)]
1718    pub metadata: ExecutionMetadataView,
1719}
1720
1721impl From<ExecutionOutcome> for ExecutionOutcomeView {
1722    fn from(outcome: ExecutionOutcome) -> Self {
1723        Self {
1724            logs: outcome.logs,
1725            receipt_ids: outcome.receipt_ids,
1726            gas_burnt: outcome.gas_burnt,
1727            tokens_burnt: outcome.tokens_burnt,
1728            executor_id: outcome.executor_id,
1729            status: outcome.status.into(),
1730            metadata: outcome.metadata.into(),
1731        }
1732    }
1733}
1734
1735impl From<&ExecutionOutcomeView> for PartialExecutionOutcome {
1736    fn from(outcome: &ExecutionOutcomeView) -> Self {
1737        Self {
1738            receipt_ids: outcome.receipt_ids.clone(),
1739            gas_burnt: outcome.gas_burnt,
1740            tokens_burnt: outcome.tokens_burnt,
1741            executor_id: outcome.executor_id.clone(),
1742            status: outcome.status.clone().into(),
1743        }
1744    }
1745}
1746impl From<ExecutionStatusView> for PartialExecutionStatus {
1747    fn from(status: ExecutionStatusView) -> PartialExecutionStatus {
1748        match status {
1749            ExecutionStatusView::Unknown => PartialExecutionStatus::Unknown,
1750            ExecutionStatusView::Failure(_) => PartialExecutionStatus::Failure,
1751            ExecutionStatusView::SuccessValue(value) => PartialExecutionStatus::SuccessValue(value),
1752            ExecutionStatusView::SuccessReceiptId(id) => {
1753                PartialExecutionStatus::SuccessReceiptId(id)
1754            }
1755        }
1756    }
1757}
1758
1759impl ExecutionOutcomeView {
1760    // Same behavior as ExecutionOutcomeWithId's to_hashes.
1761    pub fn to_hashes(&self, id: CryptoHash) -> Vec<CryptoHash> {
1762        let mut result = Vec::with_capacity(self.logs.len().saturating_add(2));
1763        result.push(id);
1764        result.push(CryptoHash::hash_borsh(&PartialExecutionOutcome::from(self)));
1765        result.extend(self.logs.iter().map(|log| hash(log.as_bytes())));
1766        result
1767    }
1768}
1769
1770#[cfg_attr(feature = "deepsize_feature", derive(deepsize::DeepSizeOf))]
1771#[derive(
1772    BorshSerialize,
1773    BorshDeserialize,
1774    Debug,
1775    PartialEq,
1776    Eq,
1777    Clone,
1778    serde::Serialize,
1779    serde::Deserialize,
1780)]
1781#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1782pub struct ExecutionOutcomeWithIdView {
1783    pub proof: MerklePath,
1784    pub block_hash: CryptoHash,
1785    pub id: CryptoHash,
1786    pub outcome: ExecutionOutcomeView,
1787}
1788
1789impl From<ExecutionOutcomeWithIdAndProof> for ExecutionOutcomeWithIdView {
1790    fn from(outcome_with_id_and_proof: ExecutionOutcomeWithIdAndProof) -> Self {
1791        Self {
1792            proof: outcome_with_id_and_proof.proof,
1793            block_hash: outcome_with_id_and_proof.block_hash,
1794            id: outcome_with_id_and_proof.outcome_with_id.id,
1795            outcome: outcome_with_id_and_proof.outcome_with_id.outcome.into(),
1796        }
1797    }
1798}
1799
1800impl ExecutionOutcomeWithIdView {
1801    pub fn to_hashes(&self) -> Vec<CryptoHash> {
1802        self.outcome.to_hashes(self.id)
1803    }
1804}
1805#[derive(Clone, Debug)]
1806pub struct TxStatusView {
1807    pub execution_outcome: Option<FinalExecutionOutcomeViewEnum>,
1808    pub status: TxExecutionStatus,
1809}
1810
1811#[derive(
1812    BorshSerialize,
1813    BorshDeserialize,
1814    serde::Serialize,
1815    serde::Deserialize,
1816    Clone,
1817    Debug,
1818    Default,
1819    Eq,
1820    PartialEq,
1821)]
1822#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1823#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
1824pub enum TxExecutionStatus {
1825    /// Transaction is waiting to be included into the block
1826    None,
1827    /// Transaction is included into the block. The block may be not finalized yet
1828    Included,
1829    /// Transaction is included into the block +
1830    /// All non-refund transaction receipts finished their execution.
1831    /// The corresponding blocks for tx and each receipt may be not finalized yet
1832    #[default]
1833    ExecutedOptimistic,
1834    /// Transaction is included into finalized block
1835    IncludedFinal,
1836    /// Transaction is included into finalized block +
1837    /// All non-refund transaction receipts finished their execution.
1838    /// The corresponding blocks for each receipt may be not finalized yet
1839    Executed,
1840    /// Transaction is included into finalized block +
1841    /// Execution of all transaction receipts is finalized, including refund receipts
1842    Final,
1843}
1844
1845#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
1846#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1847#[serde(untagged)]
1848// FinalExecutionOutcomeWithReceipt is a superset of FinalExecutionOutcome that includes additional information about receipts.
1849// For proper deserialization we need to have more specific variant first.
1850pub enum FinalExecutionOutcomeViewEnum {
1851    FinalExecutionOutcomeWithReceipt(FinalExecutionOutcomeWithReceiptView),
1852    FinalExecutionOutcome(FinalExecutionOutcomeView),
1853}
1854
1855impl FinalExecutionOutcomeViewEnum {
1856    pub fn into_outcome(self) -> FinalExecutionOutcomeView {
1857        match self {
1858            Self::FinalExecutionOutcome(outcome) => outcome,
1859            Self::FinalExecutionOutcomeWithReceipt(outcome) => outcome.final_outcome,
1860        }
1861    }
1862}
1863
1864impl TxStatusView {
1865    pub fn into_outcome(self) -> Option<FinalExecutionOutcomeView> {
1866        self.execution_outcome.map(|outcome| match outcome {
1867            FinalExecutionOutcomeViewEnum::FinalExecutionOutcome(outcome) => outcome,
1868            FinalExecutionOutcomeViewEnum::FinalExecutionOutcomeWithReceipt(outcome) => {
1869                outcome.final_outcome
1870            }
1871        })
1872    }
1873}
1874
1875/// Execution outcome of the transaction and all the subsequent receipts.
1876/// Could be not finalized yet
1877#[derive(
1878    BorshSerialize, BorshDeserialize, serde::Serialize, serde::Deserialize, PartialEq, Eq, Clone,
1879)]
1880#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1881pub struct FinalExecutionOutcomeView {
1882    /// Execution status defined by chain.rs:get_final_transaction_result
1883    /// FinalExecutionStatus::NotStarted - the tx is not converted to the receipt yet
1884    /// FinalExecutionStatus::Started - we have at least 1 receipt, but the first leaf receipt_id (using dfs) hasn't finished the execution
1885    /// FinalExecutionStatus::Failure - the result of the first leaf receipt_id
1886    /// FinalExecutionStatus::SuccessValue - the result of the first leaf receipt_id
1887    pub status: FinalExecutionStatus,
1888    /// Signed Transaction
1889    pub transaction: SignedTransactionView,
1890    /// The execution outcome of the signed transaction.
1891    pub transaction_outcome: ExecutionOutcomeWithIdView,
1892    /// The execution outcome of receipts.
1893    pub receipts_outcome: Vec<ExecutionOutcomeWithIdView>,
1894}
1895
1896impl fmt::Debug for FinalExecutionOutcomeView {
1897    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1898        f.debug_struct("FinalExecutionOutcome")
1899            .field("status", &self.status)
1900            .field("transaction", &self.transaction)
1901            .field("transaction_outcome", &self.transaction_outcome)
1902            .field("receipts_outcome", &Slice(&self.receipts_outcome))
1903            .finish()
1904    }
1905}
1906
1907/// Final execution outcome of the transaction and all of subsequent the receipts. Also includes
1908/// the generated receipt.
1909#[derive(
1910    BorshSerialize,
1911    BorshDeserialize,
1912    PartialEq,
1913    Eq,
1914    Clone,
1915    Debug,
1916    serde::Serialize,
1917    serde::Deserialize,
1918)]
1919#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1920pub struct FinalExecutionOutcomeWithReceiptView {
1921    /// Final outcome view without receipts
1922    #[serde(flatten)]
1923    pub final_outcome: FinalExecutionOutcomeView,
1924    /// Receipts generated from the transaction
1925    pub receipts: Vec<ReceiptView>,
1926}
1927
1928pub mod validator_stake_view {
1929    pub use super::ValidatorStakeViewV1;
1930    use crate::types::validator_stake::ValidatorStake;
1931    use borsh::{BorshDeserialize, BorshSerialize};
1932    use near_primitives_core::types::AccountId;
1933    use serde::Deserialize;
1934
1935    #[derive(
1936        BorshSerialize, BorshDeserialize, serde::Serialize, Deserialize, Debug, Clone, Eq, PartialEq,
1937    )]
1938    #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1939    #[serde(tag = "validator_stake_struct_version")]
1940    pub enum ValidatorStakeView {
1941        V1(ValidatorStakeViewV1),
1942    }
1943
1944    impl ValidatorStakeView {
1945        pub fn into_validator_stake(self) -> ValidatorStake {
1946            self.into()
1947        }
1948
1949        #[inline]
1950        pub fn take_account_id(self) -> AccountId {
1951            match self {
1952                Self::V1(v1) => v1.account_id,
1953            }
1954        }
1955
1956        #[inline]
1957        pub fn account_id(&self) -> &AccountId {
1958            match self {
1959                Self::V1(v1) => &v1.account_id,
1960            }
1961        }
1962    }
1963
1964    impl From<ValidatorStake> for ValidatorStakeView {
1965        fn from(stake: ValidatorStake) -> Self {
1966            match stake {
1967                ValidatorStake::V1(v1) => Self::V1(ValidatorStakeViewV1 {
1968                    account_id: v1.account_id,
1969                    public_key: v1.public_key,
1970                    stake: v1.stake,
1971                }),
1972            }
1973        }
1974    }
1975
1976    impl From<ValidatorStakeView> for ValidatorStake {
1977        fn from(view: ValidatorStakeView) -> Self {
1978            match view {
1979                ValidatorStakeView::V1(v1) => Self::new_v1(v1.account_id, v1.public_key, v1.stake),
1980            }
1981        }
1982    }
1983}
1984
1985#[derive(
1986    BorshSerialize,
1987    BorshDeserialize,
1988    Debug,
1989    Clone,
1990    Eq,
1991    PartialEq,
1992    serde::Serialize,
1993    serde::Deserialize,
1994)]
1995#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1996pub struct ValidatorStakeViewV1 {
1997    pub account_id: AccountId,
1998    pub public_key: PublicKey,
1999    #[serde(with = "dec_format")]
2000    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2001    pub stake: Balance,
2002}
2003
2004#[derive(
2005    BorshSerialize,
2006    BorshDeserialize,
2007    Clone,
2008    Debug,
2009    PartialEq,
2010    Eq,
2011    serde::Serialize,
2012    serde::Deserialize,
2013)]
2014#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2015pub struct ReceiptView {
2016    pub predecessor_id: AccountId,
2017    pub receiver_id: AccountId,
2018    pub receipt_id: CryptoHash,
2019
2020    pub receipt: ReceiptEnumView,
2021    // Default value used when deserializing ReceiptView which are missing the `priority` field.
2022    // Data which is missing this field was serialized before the introduction of priority.
2023    // For ReceiptV0 ReceiptPriority::NoPriority => 0
2024    #[serde(default)]
2025    pub priority: u64,
2026}
2027
2028#[derive(
2029    BorshSerialize,
2030    BorshDeserialize,
2031    Clone,
2032    Debug,
2033    PartialEq,
2034    Eq,
2035    serde::Serialize,
2036    serde::Deserialize,
2037)]
2038#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2039pub struct DataReceiverView {
2040    pub data_id: CryptoHash,
2041    pub receiver_id: AccountId,
2042}
2043
2044#[serde_as]
2045#[derive(
2046    BorshSerialize,
2047    BorshDeserialize,
2048    Clone,
2049    Debug,
2050    PartialEq,
2051    Eq,
2052    serde::Serialize,
2053    serde::Deserialize,
2054)]
2055#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2056pub enum ReceiptEnumView {
2057    Action {
2058        signer_id: AccountId,
2059        signer_public_key: PublicKey,
2060        #[serde(with = "dec_format")]
2061        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2062        gas_price: Balance,
2063        output_data_receivers: Vec<DataReceiverView>,
2064        input_data_ids: Vec<CryptoHash>,
2065        actions: Vec<ActionView>,
2066        #[serde(default = "default_is_promise")]
2067        is_promise_yield: bool,
2068    },
2069    Data {
2070        data_id: CryptoHash,
2071        #[serde_as(as = "Option<Base64>")]
2072        #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
2073        data: Option<Vec<u8>>,
2074        #[serde(default = "default_is_promise")]
2075        is_promise_resume: bool,
2076    },
2077    GlobalContractDistribution {
2078        id: GlobalContractIdentifier,
2079        target_shard: ShardId,
2080        already_delivered_shards: Vec<ShardId>,
2081        #[serde_as(as = "Base64")]
2082        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2083        code: Vec<u8>,
2084    },
2085}
2086
2087// Default value used when deserializing ReceiptEnumViews which are missing either the
2088// `is_promise_yield` or `is_promise_resume` fields. Data which is missing this field was
2089// serialized before the introduction of yield execution.
2090fn default_is_promise() -> bool {
2091    false
2092}
2093
2094impl From<Receipt> for ReceiptView {
2095    fn from(receipt: Receipt) -> Self {
2096        let is_promise_yield = matches!(receipt.receipt(), ReceiptEnum::PromiseYield(_));
2097        let is_promise_resume = matches!(receipt.receipt(), ReceiptEnum::PromiseResume(_));
2098        let priority = receipt.priority().value();
2099
2100        ReceiptView {
2101            predecessor_id: receipt.predecessor_id().clone(),
2102            receiver_id: receipt.receiver_id().clone(),
2103            receipt_id: *receipt.receipt_id(),
2104            receipt: match receipt.take_receipt() {
2105                ReceiptEnum::Action(action_receipt) | ReceiptEnum::PromiseYield(action_receipt) => {
2106                    ReceiptEnumView::Action {
2107                        signer_id: action_receipt.signer_id,
2108                        signer_public_key: action_receipt.signer_public_key,
2109                        gas_price: action_receipt.gas_price,
2110                        output_data_receivers: action_receipt
2111                            .output_data_receivers
2112                            .into_iter()
2113                            .map(|data_receiver| DataReceiverView {
2114                                data_id: data_receiver.data_id,
2115                                receiver_id: data_receiver.receiver_id,
2116                            })
2117                            .collect(),
2118                        input_data_ids: action_receipt
2119                            .input_data_ids
2120                            .into_iter()
2121                            .map(Into::into)
2122                            .collect(),
2123                        actions: action_receipt.actions.into_iter().map(Into::into).collect(),
2124                        is_promise_yield,
2125                    }
2126                }
2127                ReceiptEnum::Data(data_receipt) | ReceiptEnum::PromiseResume(data_receipt) => {
2128                    ReceiptEnumView::Data {
2129                        data_id: data_receipt.data_id,
2130                        data: data_receipt.data,
2131                        is_promise_resume,
2132                    }
2133                }
2134                ReceiptEnum::GlobalContractDistribution(receipt) => {
2135                    ReceiptEnumView::GlobalContractDistribution {
2136                        id: receipt.id().clone(),
2137                        target_shard: receipt.target_shard(),
2138                        already_delivered_shards: receipt.already_delivered_shards().to_vec(),
2139                        code: hash(receipt.code()).as_bytes().to_vec(),
2140                    }
2141                }
2142            },
2143            priority,
2144        }
2145    }
2146}
2147
2148impl TryFrom<ReceiptView> for Receipt {
2149    type Error = Box<dyn std::error::Error + Send + Sync>;
2150
2151    fn try_from(receipt_view: ReceiptView) -> Result<Self, Self::Error> {
2152        Ok(Receipt::V1(ReceiptV1 {
2153            predecessor_id: receipt_view.predecessor_id,
2154            receiver_id: receipt_view.receiver_id,
2155            receipt_id: receipt_view.receipt_id,
2156            receipt: match receipt_view.receipt {
2157                ReceiptEnumView::Action {
2158                    signer_id,
2159                    signer_public_key,
2160                    gas_price,
2161                    output_data_receivers,
2162                    input_data_ids,
2163                    actions,
2164                    is_promise_yield,
2165                } => {
2166                    let action_receipt = ActionReceipt {
2167                        signer_id,
2168                        signer_public_key,
2169                        gas_price,
2170                        output_data_receivers: output_data_receivers
2171                            .into_iter()
2172                            .map(|data_receiver_view| DataReceiver {
2173                                data_id: data_receiver_view.data_id,
2174                                receiver_id: data_receiver_view.receiver_id,
2175                            })
2176                            .collect(),
2177                        input_data_ids: input_data_ids.into_iter().map(Into::into).collect(),
2178                        actions: actions
2179                            .into_iter()
2180                            .map(TryInto::try_into)
2181                            .collect::<Result<Vec<_>, _>>()?,
2182                    };
2183
2184                    if is_promise_yield {
2185                        ReceiptEnum::PromiseYield(action_receipt)
2186                    } else {
2187                        ReceiptEnum::Action(action_receipt)
2188                    }
2189                }
2190                ReceiptEnumView::Data { data_id, data, is_promise_resume } => {
2191                    let data_receipt = DataReceipt { data_id, data };
2192
2193                    if is_promise_resume {
2194                        ReceiptEnum::PromiseResume(data_receipt)
2195                    } else {
2196                        ReceiptEnum::Data(data_receipt)
2197                    }
2198                }
2199                ReceiptEnumView::GlobalContractDistribution {
2200                    id,
2201                    target_shard,
2202                    already_delivered_shards,
2203                    code,
2204                } => {
2205                    ReceiptEnum::GlobalContractDistribution(GlobalContractDistributionReceipt::new(
2206                        id,
2207                        target_shard,
2208                        already_delivered_shards,
2209                        code.into(),
2210                    ))
2211                }
2212            },
2213            priority: receipt_view.priority,
2214        }))
2215    }
2216}
2217
2218/// Information about this epoch validators and next epoch validators
2219#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone, ProtocolSchema)]
2220#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2221pub struct EpochValidatorInfo {
2222    /// Validators for the current epoch
2223    pub current_validators: Vec<CurrentEpochValidatorInfo>,
2224    /// Validators for the next epoch
2225    pub next_validators: Vec<NextEpochValidatorInfo>,
2226    /// Fishermen for the current epoch
2227    pub current_fishermen: Vec<ValidatorStakeView>,
2228    /// Fishermen for the next epoch
2229    pub next_fishermen: Vec<ValidatorStakeView>,
2230    /// Proposals in the current epoch
2231    pub current_proposals: Vec<ValidatorStakeView>,
2232    /// Kickout in the previous epoch
2233    pub prev_epoch_kickout: Vec<ValidatorKickoutView>,
2234    /// Epoch start block height
2235    pub epoch_start_height: BlockHeight,
2236    /// Epoch height
2237    pub epoch_height: EpochHeight,
2238}
2239
2240#[derive(
2241    BorshSerialize,
2242    BorshDeserialize,
2243    Debug,
2244    PartialEq,
2245    Eq,
2246    Clone,
2247    serde::Serialize,
2248    serde::Deserialize,
2249    ProtocolSchema,
2250)]
2251#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2252pub struct ValidatorKickoutView {
2253    pub account_id: AccountId,
2254    pub reason: ValidatorKickoutReason,
2255}
2256
2257#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Eq, Clone, ProtocolSchema)]
2258#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2259pub struct CurrentEpochValidatorInfo {
2260    pub account_id: AccountId,
2261    pub public_key: PublicKey,
2262    pub is_slashed: bool,
2263    #[serde(with = "dec_format")]
2264    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2265    pub stake: Balance,
2266    /// Shards this validator is assigned to as chunk producer in the current epoch.
2267    #[serde(rename = "shards")]
2268    pub shards_produced: Vec<ShardId>,
2269    pub num_produced_blocks: NumBlocks,
2270    pub num_expected_blocks: NumBlocks,
2271    #[serde(default)]
2272    pub num_produced_chunks: NumBlocks,
2273    #[serde(default)]
2274    pub num_expected_chunks: NumBlocks,
2275    // The following two fields correspond to the shards in the shard array.
2276    #[serde(default)]
2277    pub num_produced_chunks_per_shard: Vec<NumBlocks>,
2278    /// Number of chunks this validator was expected to produce in each shard.
2279    /// Each entry in the array corresponds to the shard in the `shards_produced` array.
2280    #[serde(default)]
2281    pub num_expected_chunks_per_shard: Vec<NumBlocks>,
2282    #[serde(default)]
2283    pub num_produced_endorsements: NumBlocks,
2284    #[serde(default)]
2285    pub num_expected_endorsements: NumBlocks,
2286    #[serde(default)]
2287    pub num_produced_endorsements_per_shard: Vec<NumBlocks>,
2288    /// Number of chunks this validator was expected to validate and endorse in each shard.
2289    /// Each entry in the array corresponds to the shard in the `shards_endorsed` array.
2290    #[serde(default)]
2291    pub num_expected_endorsements_per_shard: Vec<NumBlocks>,
2292    /// Shards this validator is assigned to as chunk validator in the current epoch.
2293    pub shards_endorsed: Vec<ShardId>,
2294}
2295
2296#[derive(
2297    BorshSerialize,
2298    BorshDeserialize,
2299    Debug,
2300    PartialEq,
2301    Eq,
2302    Clone,
2303    serde::Serialize,
2304    serde::Deserialize,
2305    ProtocolSchema,
2306)]
2307#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2308pub struct NextEpochValidatorInfo {
2309    pub account_id: AccountId,
2310    pub public_key: PublicKey,
2311    #[serde(with = "dec_format")]
2312    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2313    pub stake: Balance,
2314    pub shards: Vec<ShardId>,
2315}
2316
2317#[derive(
2318    PartialEq,
2319    Eq,
2320    Debug,
2321    Clone,
2322    BorshDeserialize,
2323    BorshSerialize,
2324    serde::Serialize,
2325    serde::Deserialize,
2326)]
2327#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2328pub struct LightClientBlockView {
2329    pub prev_block_hash: CryptoHash,
2330    pub next_block_inner_hash: CryptoHash,
2331    pub inner_lite: BlockHeaderInnerLiteView,
2332    pub inner_rest_hash: CryptoHash,
2333    pub next_bps: Option<Vec<ValidatorStakeView>>,
2334    pub approvals_after_next: Vec<Option<Box<Signature>>>,
2335}
2336
2337#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, BorshDeserialize, BorshSerialize)]
2338#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2339pub struct LightClientBlockLiteView {
2340    pub prev_block_hash: CryptoHash,
2341    pub inner_rest_hash: CryptoHash,
2342    pub inner_lite: BlockHeaderInnerLiteView,
2343}
2344
2345impl From<BlockHeader> for LightClientBlockLiteView {
2346    fn from(header: BlockHeader) -> Self {
2347        Self {
2348            prev_block_hash: *header.prev_hash(),
2349            inner_rest_hash: hash(&header.inner_rest_bytes()),
2350            inner_lite: header.into(),
2351        }
2352    }
2353}
2354impl LightClientBlockLiteView {
2355    pub fn hash(&self) -> CryptoHash {
2356        let block_header_inner_lite: BlockHeaderInnerLite = self.inner_lite.clone().into();
2357        combine_hash(
2358            &combine_hash(
2359                &hash(&borsh::to_vec(&block_header_inner_lite).unwrap()),
2360                &self.inner_rest_hash,
2361            ),
2362            &self.prev_block_hash,
2363        )
2364    }
2365}
2366
2367#[derive(serde::Serialize, serde::Deserialize, Debug)]
2368#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2369pub struct GasPriceView {
2370    #[serde(with = "dec_format")]
2371    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2372    pub gas_price: Balance,
2373}
2374
2375/// It is a [serializable view] of [`StateChangesRequest`].
2376///
2377/// [serializable view]: ./index.html
2378/// [`StateChangesRequest`]: ../types/struct.StateChangesRequest.html
2379#[derive(Debug, serde::Serialize, serde::Deserialize)]
2380#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2381#[serde(tag = "changes_type", rename_all = "snake_case")]
2382pub enum StateChangesRequestView {
2383    AccountChanges {
2384        account_ids: Vec<AccountId>,
2385    },
2386    SingleAccessKeyChanges {
2387        keys: Vec<AccountWithPublicKey>,
2388    },
2389    AllAccessKeyChanges {
2390        account_ids: Vec<AccountId>,
2391    },
2392    ContractCodeChanges {
2393        account_ids: Vec<AccountId>,
2394    },
2395    DataChanges {
2396        account_ids: Vec<AccountId>,
2397        #[serde(rename = "key_prefix_base64")]
2398        key_prefix: StoreKey,
2399    },
2400}
2401
2402impl From<StateChangesRequestView> for StateChangesRequest {
2403    fn from(request: StateChangesRequestView) -> Self {
2404        match request {
2405            StateChangesRequestView::AccountChanges { account_ids } => {
2406                Self::AccountChanges { account_ids }
2407            }
2408            StateChangesRequestView::SingleAccessKeyChanges { keys } => {
2409                Self::SingleAccessKeyChanges { keys }
2410            }
2411            StateChangesRequestView::AllAccessKeyChanges { account_ids } => {
2412                Self::AllAccessKeyChanges { account_ids }
2413            }
2414            StateChangesRequestView::ContractCodeChanges { account_ids } => {
2415                Self::ContractCodeChanges { account_ids }
2416            }
2417            StateChangesRequestView::DataChanges { account_ids, key_prefix } => {
2418                Self::DataChanges { account_ids, key_prefix }
2419            }
2420        }
2421    }
2422}
2423
2424/// It is a [serializable view] of [`StateChangeKind`].
2425///
2426/// [serializable view]: ./index.html
2427/// [`StateChangeKind`]: ../types/struct.StateChangeKind.html
2428#[derive(Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
2429#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2430#[serde(rename_all = "snake_case", tag = "type")]
2431pub enum StateChangeKindView {
2432    AccountTouched { account_id: AccountId },
2433    AccessKeyTouched { account_id: AccountId },
2434    DataTouched { account_id: AccountId },
2435    ContractCodeTouched { account_id: AccountId },
2436}
2437
2438impl From<StateChangeKind> for StateChangeKindView {
2439    fn from(state_change_kind: StateChangeKind) -> Self {
2440        match state_change_kind {
2441            StateChangeKind::AccountTouched { account_id } => Self::AccountTouched { account_id },
2442            StateChangeKind::AccessKeyTouched { account_id } => {
2443                Self::AccessKeyTouched { account_id }
2444            }
2445            StateChangeKind::DataTouched { account_id } => Self::DataTouched { account_id },
2446            StateChangeKind::ContractCodeTouched { account_id } => {
2447                Self::ContractCodeTouched { account_id }
2448            }
2449        }
2450    }
2451}
2452
2453pub type StateChangesKindsView = Vec<StateChangeKindView>;
2454
2455/// See crate::types::StateChangeCause for details.
2456#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
2457#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2458#[serde(rename_all = "snake_case", tag = "type")]
2459pub enum StateChangeCauseView {
2460    NotWritableToDisk,
2461    InitialState,
2462    TransactionProcessing { tx_hash: CryptoHash },
2463    ActionReceiptProcessingStarted { receipt_hash: CryptoHash },
2464    ActionReceiptGasReward { receipt_hash: CryptoHash },
2465    ReceiptProcessing { receipt_hash: CryptoHash },
2466    PostponedReceipt { receipt_hash: CryptoHash },
2467    UpdatedDelayedReceipts,
2468    ValidatorAccountsUpdate,
2469    Migration,
2470    BandwidthSchedulerStateUpdate,
2471}
2472
2473impl From<StateChangeCause> for StateChangeCauseView {
2474    fn from(state_change_cause: StateChangeCause) -> Self {
2475        match state_change_cause {
2476            StateChangeCause::NotWritableToDisk => Self::NotWritableToDisk,
2477            StateChangeCause::InitialState => Self::InitialState,
2478            StateChangeCause::TransactionProcessing { tx_hash } => {
2479                Self::TransactionProcessing { tx_hash }
2480            }
2481            StateChangeCause::ActionReceiptProcessingStarted { receipt_hash } => {
2482                Self::ActionReceiptProcessingStarted { receipt_hash }
2483            }
2484            StateChangeCause::ActionReceiptGasReward { receipt_hash } => {
2485                Self::ActionReceiptGasReward { receipt_hash }
2486            }
2487            StateChangeCause::ReceiptProcessing { receipt_hash } => {
2488                Self::ReceiptProcessing { receipt_hash }
2489            }
2490            StateChangeCause::PostponedReceipt { receipt_hash } => {
2491                Self::PostponedReceipt { receipt_hash }
2492            }
2493            StateChangeCause::UpdatedDelayedReceipts => Self::UpdatedDelayedReceipts,
2494            StateChangeCause::ValidatorAccountsUpdate => Self::ValidatorAccountsUpdate,
2495            StateChangeCause::Migration => Self::Migration,
2496            // Some nodes on testnet were upgraded to #13155 that included
2497            // unintended removal of enum variants which changes borsh tag for
2498            // BandwidthSchedulerStateUpdate from 11 to 10. So data written by a
2499            // node running broken version will have BandwidthSchedulerStateUpdate written
2500            // with _UnusedReshardingV2 borsh tag. This is a temporary fix, later it should be
2501            // changed to => unreachable!()
2502            StateChangeCause::_UnusedReshardingV2 => Self::BandwidthSchedulerStateUpdate,
2503            StateChangeCause::BandwidthSchedulerStateUpdate => Self::BandwidthSchedulerStateUpdate,
2504        }
2505    }
2506}
2507
2508#[serde_as]
2509#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
2510#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2511#[serde(rename_all = "snake_case", tag = "type", content = "change")]
2512pub enum StateChangeValueView {
2513    AccountUpdate {
2514        account_id: AccountId,
2515        #[serde(flatten)]
2516        account: AccountView,
2517    },
2518    AccountDeletion {
2519        account_id: AccountId,
2520    },
2521    AccessKeyUpdate {
2522        account_id: AccountId,
2523        public_key: PublicKey,
2524        access_key: AccessKeyView,
2525    },
2526    AccessKeyDeletion {
2527        account_id: AccountId,
2528        public_key: PublicKey,
2529    },
2530    DataUpdate {
2531        account_id: AccountId,
2532        #[serde(rename = "key_base64")]
2533        key: StoreKey,
2534        #[serde(rename = "value_base64")]
2535        value: StoreValue,
2536    },
2537    DataDeletion {
2538        account_id: AccountId,
2539        #[serde(rename = "key_base64")]
2540        key: StoreKey,
2541    },
2542    ContractCodeUpdate {
2543        account_id: AccountId,
2544        #[serde(rename = "code_base64")]
2545        #[serde_as(as = "Base64")]
2546        #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2547        code: Vec<u8>,
2548    },
2549    ContractCodeDeletion {
2550        account_id: AccountId,
2551    },
2552}
2553
2554impl From<StateChangeValue> for StateChangeValueView {
2555    fn from(state_change: StateChangeValue) -> Self {
2556        match state_change {
2557            StateChangeValue::AccountUpdate { account_id, account } => {
2558                Self::AccountUpdate { account_id, account: account.into() }
2559            }
2560            StateChangeValue::AccountDeletion { account_id } => {
2561                Self::AccountDeletion { account_id }
2562            }
2563            StateChangeValue::AccessKeyUpdate { account_id, public_key, access_key } => {
2564                Self::AccessKeyUpdate { account_id, public_key, access_key: access_key.into() }
2565            }
2566            StateChangeValue::AccessKeyDeletion { account_id, public_key } => {
2567                Self::AccessKeyDeletion { account_id, public_key }
2568            }
2569            StateChangeValue::DataUpdate { account_id, key, value } => {
2570                Self::DataUpdate { account_id, key, value }
2571            }
2572            StateChangeValue::DataDeletion { account_id, key } => {
2573                Self::DataDeletion { account_id, key }
2574            }
2575            StateChangeValue::ContractCodeUpdate { account_id, code } => {
2576                Self::ContractCodeUpdate { account_id, code }
2577            }
2578            StateChangeValue::ContractCodeDeletion { account_id } => {
2579                Self::ContractCodeDeletion { account_id }
2580            }
2581        }
2582    }
2583}
2584
2585#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
2586#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2587pub struct StateChangeWithCauseView {
2588    pub cause: StateChangeCauseView,
2589    #[serde(flatten)]
2590    pub value: StateChangeValueView,
2591}
2592
2593impl From<StateChangeWithCause> for StateChangeWithCauseView {
2594    fn from(state_change_with_cause: StateChangeWithCause) -> Self {
2595        let StateChangeWithCause { cause, value } = state_change_with_cause;
2596        Self { cause: cause.into(), value: value.into() }
2597    }
2598}
2599
2600pub type StateChangesView = Vec<StateChangeWithCauseView>;
2601
2602/// Maintenance windows view are a vector of maintenance window.
2603pub type MaintenanceWindowsView = Vec<Range<BlockHeight>>;
2604
2605/// Contains the split storage information.
2606#[derive(serde::Serialize, serde::Deserialize, Debug)]
2607#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2608pub struct SplitStorageInfoView {
2609    pub head_height: Option<BlockHeight>,
2610    pub final_head_height: Option<BlockHeight>,
2611    pub cold_head_height: Option<BlockHeight>,
2612
2613    pub hot_db_kind: Option<String>,
2614}
2615
2616#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
2617#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
2618pub struct CongestionInfoView {
2619    #[serde(with = "dec_format")]
2620    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2621    pub delayed_receipts_gas: u128,
2622
2623    #[serde(with = "dec_format")]
2624    #[cfg_attr(feature = "schemars", schemars(with = "String"))]
2625    pub buffered_receipts_gas: u128,
2626
2627    pub receipt_bytes: u64,
2628
2629    pub allowed_shard: u16,
2630}
2631
2632impl From<CongestionInfo> for CongestionInfoView {
2633    fn from(congestion_info: CongestionInfo) -> Self {
2634        match congestion_info {
2635            CongestionInfo::V1(congestion_info) => congestion_info.into(),
2636        }
2637    }
2638}
2639
2640impl From<CongestionInfoV1> for CongestionInfoView {
2641    fn from(congestion_info: CongestionInfoV1) -> Self {
2642        Self {
2643            delayed_receipts_gas: congestion_info.delayed_receipts_gas,
2644            buffered_receipts_gas: congestion_info.buffered_receipts_gas,
2645            receipt_bytes: congestion_info.receipt_bytes,
2646            allowed_shard: congestion_info.allowed_shard,
2647        }
2648    }
2649}
2650
2651impl From<CongestionInfoView> for CongestionInfo {
2652    fn from(congestion_info: CongestionInfoView) -> Self {
2653        CongestionInfo::V1(CongestionInfoV1 {
2654            delayed_receipts_gas: congestion_info.delayed_receipts_gas,
2655            buffered_receipts_gas: congestion_info.buffered_receipts_gas,
2656            receipt_bytes: congestion_info.receipt_bytes,
2657            allowed_shard: congestion_info.allowed_shard,
2658        })
2659    }
2660}
2661
2662impl CongestionInfoView {
2663    pub fn congestion_level(&self, config_view: CongestionControlConfigView) -> f64 {
2664        let congestion_config = CongestionControlConfig::from(config_view);
2665        // Localized means without considering missed chunks congestion. As far
2666        // as clients are concerned, this is the only congestion level that
2667        // matters.
2668        // Missed chunks congestion exists to reduce incoming load after a
2669        // number of chunks were missed. It is not a property of a specific
2670        // chunk but rather a property that changes over time. It is even a bit
2671        // misleading to call it congestion, as it is not a problem with too
2672        // much traffic.
2673        CongestionInfo::from(self.clone()).localized_congestion_level(&congestion_config)
2674    }
2675}
2676
2677#[cfg(test)]
2678#[cfg(not(feature = "nightly"))]
2679mod tests {
2680    use super::{ExecutionMetadataView, FinalExecutionOutcomeViewEnum};
2681    use crate::profile_data_v2::ProfileDataV2;
2682    use crate::profile_data_v3::ProfileDataV3;
2683    use crate::transaction::ExecutionMetadata;
2684
2685    /// The JSON representation used in RPC responses must not remove or rename
2686    /// fields, only adding fields is allowed or we risk breaking clients.
2687    #[test]
2688    fn test_runtime_config_view() {
2689        use near_parameters::{RuntimeConfig, RuntimeConfigStore, RuntimeConfigView};
2690        use near_primitives_core::version::PROTOCOL_VERSION;
2691
2692        let config_store = RuntimeConfigStore::new(None);
2693        let config = config_store.get_config(PROTOCOL_VERSION);
2694        let view = RuntimeConfigView::from(RuntimeConfig::clone(config));
2695        insta::assert_json_snapshot!(&view, { ".wasm_config.vm_kind" => "<REDACTED>"});
2696    }
2697
2698    /// `ExecutionMetadataView` with profile V1 displayed on the RPC should not change.
2699    #[test]
2700    fn test_exec_metadata_v1_view() {
2701        let metadata = ExecutionMetadata::V1;
2702        let view = ExecutionMetadataView::from(metadata);
2703        insta::assert_json_snapshot!(view);
2704    }
2705
2706    /// `ExecutionMetadataView` with profile V2 displayed on the RPC should not change.
2707    #[test]
2708    fn test_exec_metadata_v2_view() {
2709        let metadata = ExecutionMetadata::V2(ProfileDataV2::test());
2710        let view = ExecutionMetadataView::from(metadata);
2711        insta::assert_json_snapshot!(view);
2712    }
2713
2714    /// `ExecutionMetadataView` with profile V3 displayed on the RPC should not change.
2715    #[test]
2716    fn test_exec_metadata_v3_view() {
2717        let metadata = ExecutionMetadata::V3(ProfileDataV3::test().into());
2718        let view = ExecutionMetadataView::from(metadata);
2719        insta::assert_json_snapshot!(view);
2720    }
2721
2722    #[test]
2723    fn test_deserialize_execution_outcome_with_receipt() {
2724        // Real JSON-RPC response for 'EXPERIMENTAL_tx_status' method
2725        // cspell:ignore mainchain
2726        let json = r#"{"final_execution_status":"FINAL","receipts":[{"predecessor_id":"system","priority":0,"receipt":{"Action":{"actions":[{"Transfer":{"deposit":"17930928991009412192152"}}],"gas_price":"0","input_data_ids":[],"is_promise_yield":false,"output_data_receivers":[],"signer_id":"btc-client.testnet","signer_public_key":"ed25519:HM7ax8jJf41JozvanXepzhtD45AeRFcwJQCuLXFuDkjA"}},"receipt_id":"8ZD92cLpoCEU46hPGFfk3VqZpU8s6DoQhZ4pCMqWwDT6","receiver_id":"btc-client.testnet"}],"receipts_outcome":[{"block_hash":"9SP8Y3sVADWNN5QoEB5CsvPUE5HT4o8YfBaCnhLss87K","id":"e2XGEosf843XMiCJHvZufvHKyw419ZYibDBdVJQr9cB","outcome":{"executor_id":"btc-client.testnet","gas_burnt":2906160054161,"logs":["Block hash: 0000000000000000ee617846a3e081ae2f30091451442e1b5fb027d8eba09b3a","Saving to mainchain"],"metadata":{"gas_profile":[{"cost":"BASE","cost_category":"WASM_HOST_COST","gas_used":"8472579552"},{"cost":"CONTRACT_LOADING_BASE","cost_category":"WASM_HOST_COST","gas_used":"35445963"},{"cost":"CONTRACT_LOADING_BYTES","cost_category":"WASM_HOST_COST","gas_used":"413841688515"},{"cost":"LOG_BASE","cost_category":"WASM_HOST_COST","gas_used":"7086626100"},{"cost":"LOG_BYTE","cost_category":"WASM_HOST_COST","gas_used":"1253885145"},{"cost":"READ_CACHED_TRIE_NODE","cost_category":"WASM_HOST_COST","gas_used":"102600000000"},{"cost":"READ_MEMORY_BASE","cost_category":"WASM_HOST_COST","gas_used":"54807127200"},{"cost":"READ_MEMORY_BYTE","cost_category":"WASM_HOST_COST","gas_used":"2873807748"},{"cost":"READ_REGISTER_BASE","cost_category":"WASM_HOST_COST","gas_used":"22654486674"},{"cost":"READ_REGISTER_BYTE","cost_category":"WASM_HOST_COST","gas_used":"51252240"},{"cost":"SHA256_BASE","cost_category":"WASM_HOST_COST","gas_used":"13622910750"},{"cost":"SHA256_BYTE","cost_category":"WASM_HOST_COST","gas_used":"3400546491"},{"cost":"STORAGE_READ_BASE","cost_category":"WASM_HOST_COST","gas_used":"450854766000"},{"cost":"STORAGE_READ_KEY_BYTE","cost_category":"WASM_HOST_COST","gas_used":"4952405280"},{"cost":"STORAGE_READ_VALUE_BYTE","cost_category":"WASM_HOST_COST","gas_used":"1806743610"},{"cost":"STORAGE_WRITE_BASE","cost_category":"WASM_HOST_COST","gas_used":"256786944000"},{"cost":"STORAGE_WRITE_EVICTED_BYTE","cost_category":"WASM_HOST_COST","gas_used":"2826323016"},{"cost":"STORAGE_WRITE_KEY_BYTE","cost_category":"WASM_HOST_COST","gas_used":"5638629360"},{"cost":"STORAGE_WRITE_VALUE_BYTE","cost_category":"WASM_HOST_COST","gas_used":"8685190920"},{"cost":"TOUCHING_TRIE_NODE","cost_category":"WASM_HOST_COST","gas_used":"434752810002"},{"cost":"UTF8_DECODING_BASE","cost_category":"WASM_HOST_COST","gas_used":"6223558122"},{"cost":"UTF8_DECODING_BYTE","cost_category":"WASM_HOST_COST","gas_used":"27700145505"},{"cost":"WASM_INSTRUCTION","cost_category":"WASM_HOST_COST","gas_used":"126491330196"},{"cost":"WRITE_MEMORY_BASE","cost_category":"WASM_HOST_COST","gas_used":"28037948610"},{"cost":"WRITE_MEMORY_BYTE","cost_category":"WASM_HOST_COST","gas_used":"1459941792"},{"cost":"WRITE_REGISTER_BASE","cost_category":"WASM_HOST_COST","gas_used":"28655224860"},{"cost":"WRITE_REGISTER_BYTE","cost_category":"WASM_HOST_COST","gas_used":"2311350912"}],"version":3},"receipt_ids":["8ZD92cLpoCEU46hPGFfk3VqZpU8s6DoQhZ4pCMqWwDT6"],"status":{"SuccessValue":""},"tokens_burnt":"290616005416100000000"},"proof":[{"direction":"Left","hash":"BoQHueiPH9e4C7fkxouV4tFpGZ4hK5fJhKQnPBWwPazS"},{"direction":"Right","hash":"Ayxj8iVFTJMzZa7estoTtuBKJaUNhoipaaM7WmTjkkiS"}]},{"block_hash":"3rEx3xmgLCRgUfSgueD71YNrJYQYNvhtkXaqVQxdmj4U","id":"8ZD92cLpoCEU46hPGFfk3VqZpU8s6DoQhZ4pCMqWwDT6","outcome":{"executor_id":"btc-client.testnet","gas_burnt":223182562500,"logs":[],"metadata":{"gas_profile":[],"version":3},"receipt_ids":[],"status":{"SuccessValue":""},"tokens_burnt":"0"},"proof":[{"direction":"Right","hash":"8yEwg14D2GyyLNnJMxdSLDJKyrKShMAL3zTnf9YpQyPW"},{"direction":"Right","hash":"2UK7BfpHf9fCCsvmfHktDfz6Rh8sFihhN6cuTU3R4BBA"}]}],"status":{"SuccessValue":""},"transaction":{"actions":[{"FunctionCall":{"args":"AQAAAABA0CKoR96WKs+KPP6zPl0flT6XC91eR3uAUgwNAAAAAAAAAAzcZU0cipmejc+wGqnRJchd5uM6qRJM5Oojp3FrkJ2HVmGyZsnyFBlkvks8","deposit":"0","gas":100000000000000,"method_name":"submit_blocks"}}],"hash":"GMUCDLHFJVvmZYXmPf9QeUVAt9r9hXcQP1yL5emPFnvx","nonce":170437577001422,"priority_fee":0,"public_key":"ed25519:HM7ax8jJf41JozvanXepzhtD45AeRFcwJQCuLXFuDkjA","receiver_id":"btc-client.testnet","signature":"ed25519:2Qe3ccPSdzPddk764vm5jt4yXcvXgYQz3WzGF3oXpZLuaRa6ggpD131nSSy3FRVPquCvxYqgMGtdum8TKX3dVqNk","signer_id":"btc-client.testnet"},"transaction_outcome":{"block_hash":"9SP8Y3sVADWNN5QoEB5CsvPUE5HT4o8YfBaCnhLss87K","id":"GMUCDLHFJVvmZYXmPf9QeUVAt9r9hXcQP1yL5emPFnvx","outcome":{"executor_id":"btc-client.testnet","gas_burnt":308276385598,"logs":[],"metadata":{"gas_profile":null,"version":1},"receipt_ids":["e2XGEosf843XMiCJHvZufvHKyw419ZYibDBdVJQr9cB"],"status":{"SuccessReceiptId":"e2XGEosf843XMiCJHvZufvHKyw419ZYibDBdVJQr9cB"},"tokens_burnt":"30827638559800000000"},"proof":[{"direction":"Right","hash":"HDgWEk2okmDdAFAVf6ffxGBH6F6vdLM1X3H5Fmaafe4S"},{"direction":"Right","hash":"Ayxj8iVFTJMzZa7estoTtuBKJaUNhoipaaM7WmTjkkiS"}]}}"#;
2727        let view: FinalExecutionOutcomeViewEnum = serde_json::from_str(json).unwrap();
2728        assert!(matches!(view, FinalExecutionOutcomeViewEnum::FinalExecutionOutcomeWithReceipt(_)));
2729    }
2730
2731    #[test]
2732    fn test_deserialize_execution_outcome_without_receipt() {
2733        // Real JSON-RPC response for 'tx' method
2734        let json = r#"{"final_execution_status":"FINAL","receipts_outcome":[{"block_hash":"9SP8Y3sVADWNN5QoEB5CsvPUE5HT4o8YfBaCnhLss87K","id":"e2XGEosf843XMiCJHvZufvHKyw419ZYibDBdVJQr9cB","outcome":{"executor_id":"btc-client.testnet","gas_burnt":2906160054161,"logs":["Block hash: 0000000000000000ee617846a3e081ae2f30091451442e1b5fb027d8eba09b3a","Saving to mainchain"],"metadata":{"gas_profile":[{"cost":"BASE","cost_category":"WASM_HOST_COST","gas_used":"8472579552"},{"cost":"CONTRACT_LOADING_BASE","cost_category":"WASM_HOST_COST","gas_used":"35445963"},{"cost":"CONTRACT_LOADING_BYTES","cost_category":"WASM_HOST_COST","gas_used":"413841688515"},{"cost":"LOG_BASE","cost_category":"WASM_HOST_COST","gas_used":"7086626100"},{"cost":"LOG_BYTE","cost_category":"WASM_HOST_COST","gas_used":"1253885145"},{"cost":"READ_CACHED_TRIE_NODE","cost_category":"WASM_HOST_COST","gas_used":"102600000000"},{"cost":"READ_MEMORY_BASE","cost_category":"WASM_HOST_COST","gas_used":"54807127200"},{"cost":"READ_MEMORY_BYTE","cost_category":"WASM_HOST_COST","gas_used":"2873807748"},{"cost":"READ_REGISTER_BASE","cost_category":"WASM_HOST_COST","gas_used":"22654486674"},{"cost":"READ_REGISTER_BYTE","cost_category":"WASM_HOST_COST","gas_used":"51252240"},{"cost":"SHA256_BASE","cost_category":"WASM_HOST_COST","gas_used":"13622910750"},{"cost":"SHA256_BYTE","cost_category":"WASM_HOST_COST","gas_used":"3400546491"},{"cost":"STORAGE_READ_BASE","cost_category":"WASM_HOST_COST","gas_used":"450854766000"},{"cost":"STORAGE_READ_KEY_BYTE","cost_category":"WASM_HOST_COST","gas_used":"4952405280"},{"cost":"STORAGE_READ_VALUE_BYTE","cost_category":"WASM_HOST_COST","gas_used":"1806743610"},{"cost":"STORAGE_WRITE_BASE","cost_category":"WASM_HOST_COST","gas_used":"256786944000"},{"cost":"STORAGE_WRITE_EVICTED_BYTE","cost_category":"WASM_HOST_COST","gas_used":"2826323016"},{"cost":"STORAGE_WRITE_KEY_BYTE","cost_category":"WASM_HOST_COST","gas_used":"5638629360"},{"cost":"STORAGE_WRITE_VALUE_BYTE","cost_category":"WASM_HOST_COST","gas_used":"8685190920"},{"cost":"TOUCHING_TRIE_NODE","cost_category":"WASM_HOST_COST","gas_used":"434752810002"},{"cost":"UTF8_DECODING_BASE","cost_category":"WASM_HOST_COST","gas_used":"6223558122"},{"cost":"UTF8_DECODING_BYTE","cost_category":"WASM_HOST_COST","gas_used":"27700145505"},{"cost":"WASM_INSTRUCTION","cost_category":"WASM_HOST_COST","gas_used":"126491330196"},{"cost":"WRITE_MEMORY_BASE","cost_category":"WASM_HOST_COST","gas_used":"28037948610"},{"cost":"WRITE_MEMORY_BYTE","cost_category":"WASM_HOST_COST","gas_used":"1459941792"},{"cost":"WRITE_REGISTER_BASE","cost_category":"WASM_HOST_COST","gas_used":"28655224860"},{"cost":"WRITE_REGISTER_BYTE","cost_category":"WASM_HOST_COST","gas_used":"2311350912"}],"version":3},"receipt_ids":["8ZD92cLpoCEU46hPGFfk3VqZpU8s6DoQhZ4pCMqWwDT6"],"status":{"SuccessValue":""},"tokens_burnt":"290616005416100000000"},"proof":[{"direction":"Left","hash":"BoQHueiPH9e4C7fkxouV4tFpGZ4hK5fJhKQnPBWwPazS"},{"direction":"Right","hash":"Ayxj8iVFTJMzZa7estoTtuBKJaUNhoipaaM7WmTjkkiS"}]},{"block_hash":"3rEx3xmgLCRgUfSgueD71YNrJYQYNvhtkXaqVQxdmj4U","id":"8ZD92cLpoCEU46hPGFfk3VqZpU8s6DoQhZ4pCMqWwDT6","outcome":{"executor_id":"btc-client.testnet","gas_burnt":223182562500,"logs":[],"metadata":{"gas_profile":[],"version":3},"receipt_ids":[],"status":{"SuccessValue":""},"tokens_burnt":"0"},"proof":[{"direction":"Right","hash":"8yEwg14D2GyyLNnJMxdSLDJKyrKShMAL3zTnf9YpQyPW"},{"direction":"Right","hash":"2UK7BfpHf9fCCsvmfHktDfz6Rh8sFihhN6cuTU3R4BBA"}]}],"status":{"SuccessValue":""},"transaction":{"actions":[{"FunctionCall":{"args":"AQAAAABA0CKoR96WKs+KPP6zPl0flT6XC91eR3uAUgwNAAAAAAAAAAzcZU0cipmejc+wGqnRJchd5uM6qRJM5Oojp3FrkJ2HVmGyZsnyFBlkvks8","deposit":"0","gas":100000000000000,"method_name":"submit_blocks"}}],"hash":"GMUCDLHFJVvmZYXmPf9QeUVAt9r9hXcQP1yL5emPFnvx","nonce":170437577001422,"priority_fee":0,"public_key":"ed25519:HM7ax8jJf41JozvanXepzhtD45AeRFcwJQCuLXFuDkjA","receiver_id":"btc-client.testnet","signature":"ed25519:2Qe3ccPSdzPddk764vm5jt4yXcvXgYQz3WzGF3oXpZLuaRa6ggpD131nSSy3FRVPquCvxYqgMGtdum8TKX3dVqNk","signer_id":"btc-client.testnet"},"transaction_outcome":{"block_hash":"9SP8Y3sVADWNN5QoEB5CsvPUE5HT4o8YfBaCnhLss87K","id":"GMUCDLHFJVvmZYXmPf9QeUVAt9r9hXcQP1yL5emPFnvx","outcome":{"executor_id":"btc-client.testnet","gas_burnt":308276385598,"logs":[],"metadata":{"gas_profile":null,"version":1},"receipt_ids":["e2XGEosf843XMiCJHvZufvHKyw419ZYibDBdVJQr9cB"],"status":{"SuccessReceiptId":"e2XGEosf843XMiCJHvZufvHKyw419ZYibDBdVJQr9cB"},"tokens_burnt":"30827638559800000000"},"proof":[{"direction":"Right","hash":"HDgWEk2okmDdAFAVf6ffxGBH6F6vdLM1X3H5Fmaafe4S"},{"direction":"Right","hash":"Ayxj8iVFTJMzZa7estoTtuBKJaUNhoipaaM7WmTjkkiS"}]}}"#;
2735        let view: FinalExecutionOutcomeViewEnum = serde_json::from_str(json).unwrap();
2736        assert!(matches!(view, FinalExecutionOutcomeViewEnum::FinalExecutionOutcome(_)));
2737    }
2738}