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