Skip to main content

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