Skip to main content

near_client_primitives/
types.rs

1use near_primitives::block::Block;
2use near_primitives::hash::CryptoHash;
3use near_primitives::merkle::MerklePath;
4use near_primitives::network::PeerId;
5use near_primitives::sharding::ChunkHash;
6use near_primitives::types::{
7    AccountId, BlockHeight, BlockHeightDelta, BlockReference, EpochId, EpochReference,
8    MaybeBlockId, ShardId, TransactionOrReceiptId,
9};
10use near_primitives::views::{
11    EpochSyncStatusView, ExecutionOutcomeWithIdView, LightClientBlockLiteView, QueryRequest,
12    StateChangesRequestView, StateSyncStatusView, SyncStatusView,
13};
14pub use near_primitives::views::{StatusResponse, StatusSyncInfo};
15use near_time::Duration;
16use std::collections::HashMap;
17use std::sync::Arc;
18
19/// Combines errors coming from chain, tx pool and block producer.
20#[derive(Debug, thiserror::Error)]
21pub enum Error {
22    #[error("Chain: {0}")]
23    Chain(#[from] near_chain_primitives::Error),
24    #[error("Chunk: {0}")]
25    Chunk(#[from] near_chunks_primitives::Error),
26    #[error("Block Producer: {0}")]
27    BlockProducer(String),
28    #[error("Chunk Producer: {0}")]
29    ChunkProducer(String),
30    #[error("Other: {0}")]
31    Other(String),
32}
33
34impl From<near_primitives::errors::EpochError> for Error {
35    fn from(err: near_primitives::errors::EpochError) -> Self {
36        Error::Chain(err.into())
37    }
38}
39
40/// Various status of syncing a specific shard.
41#[derive(Clone, Copy, Debug)]
42pub enum ShardSyncStatus {
43    StateDownloadHeader,
44    StateDownloadParts { done: u64, total: u64 },
45    StateApplyScheduling,
46    StateApplyInProgress { done: u64, total: u64 },
47    StateApplyFinalizing,
48    StateSyncDone,
49}
50
51impl ShardSyncStatus {
52    pub fn repr(&self) -> u8 {
53        match self {
54            // NOTE: This is used in metrics.
55            // Do not alter the order of existing values.
56            // Avoid reusing values for different states.
57            // When introducing a new state, always assign a unique, new value to prevent confusion.
58            ShardSyncStatus::StateDownloadHeader => 0,
59            ShardSyncStatus::StateDownloadParts { .. } => 1,
60            ShardSyncStatus::StateApplyScheduling => 2,
61            ShardSyncStatus::StateApplyInProgress { .. } => 3,
62            ShardSyncStatus::StateApplyFinalizing => 4,
63            ShardSyncStatus::StateSyncDone => 5,
64        }
65    }
66}
67
68/// Manually implement compare for ShardSyncStatus to compare only based on variant name
69impl PartialEq<Self> for ShardSyncStatus {
70    fn eq(&self, other: &Self) -> bool {
71        std::mem::discriminant(self) == std::mem::discriminant(other)
72    }
73}
74
75impl Eq for ShardSyncStatus {}
76
77impl ToString for ShardSyncStatus {
78    fn to_string(&self) -> String {
79        match self {
80            ShardSyncStatus::StateDownloadHeader => "header".to_string(),
81            ShardSyncStatus::StateDownloadParts { done, total } => {
82                format!("parts ({done}/{total})")
83            }
84            ShardSyncStatus::StateApplyScheduling => "apply scheduling".to_string(),
85            ShardSyncStatus::StateApplyInProgress { done, total } => {
86                format!("apply in progress ({done}/{total})")
87            }
88            ShardSyncStatus::StateApplyFinalizing => "apply finalizing".to_string(),
89            ShardSyncStatus::StateSyncDone => "done".to_string(),
90        }
91    }
92}
93
94#[derive(Clone, Debug)]
95pub struct StateSyncStatus {
96    pub sync_hash: CryptoHash,
97    pub sync_status: HashMap<ShardId, ShardSyncStatus>,
98    pub download_tasks: Vec<String>,
99    pub computation_tasks: Vec<String>,
100}
101
102impl StateSyncStatus {
103    pub fn new(sync_hash: CryptoHash) -> Self {
104        Self {
105            sync_hash,
106            sync_status: HashMap::new(),
107            download_tasks: Vec::new(),
108            computation_tasks: Vec::new(),
109        }
110    }
111}
112
113#[derive(Clone, Debug)]
114pub enum EpochSyncStatus {
115    /// Epoch sync decided but no request sent yet.
116    NotStarted,
117    /// Awaiting response from peer.
118    InProgress {
119        source_peer_height: BlockHeight,
120        source_peer_id: PeerId,
121        attempt_time: near_time::Utc,
122    },
123    /// Epoch sync proof applied successfully.
124    Done,
125}
126
127/// Various status sync can be in, whether it's fast sync or archival.
128#[derive(Clone, Debug, strum::AsRefStr)]
129pub enum SyncStatus {
130    /// Initial state. Not enough peers to do anything yet.
131    AwaitingPeers,
132    /// Not syncing / Done syncing.
133    NoSync,
134    /// Syncing using light-client headers to a recent epoch.
135    /// The inner status tracks the sub-state of epoch sync.
136    EpochSync(EpochSyncStatus),
137    /// Downloading block headers for fast sync.
138    HeaderSync {
139        /// Height at the beginning of header sync.
140        /// Used only for reporting the progress of the sync.
141        start_height: BlockHeight,
142        /// Current header head height.
143        current_height: BlockHeight,
144        /// Highest height of our peers.
145        highest_height: BlockHeight,
146    },
147    /// State sync, with different states of state sync for different shards.
148    StateSync(StateSyncStatus),
149    /// Download and process blocks until the head reaches the head of the network.
150    BlockSync {
151        /// Header head height at the beginning.
152        /// Used only for reporting the progress of the sync.
153        start_height: BlockHeight,
154        /// Current head height.
155        current_height: BlockHeight,
156        /// Highest height of our peers.
157        highest_height: BlockHeight,
158    },
159}
160
161impl SyncStatus {
162    /// Get a string representation of the status variant
163    pub fn as_variant_name(&self) -> &str {
164        self.as_ref()
165    }
166
167    /// True if currently engaged in syncing the chain.
168    pub fn is_syncing(&self) -> bool {
169        match self {
170            SyncStatus::NoSync => false,
171            _ => true,
172        }
173    }
174
175    pub fn repr(&self) -> u8 {
176        match self {
177            // NOTE: This is used in metrics.
178            // Do not alter the order of existing values.
179            // Avoid reusing values for different states.
180            // When introducing a new state, always assign a unique, new value to prevent confusion.
181            // Represent NoSync as 0 because it is the state of a normal well-behaving node.
182            SyncStatus::NoSync => 0,
183            SyncStatus::AwaitingPeers => 1,
184            SyncStatus::EpochSync(_) => 2,
185            SyncStatus::HeaderSync { .. } => 4,
186            SyncStatus::StateSync(_) => 5,
187            SyncStatus::BlockSync { .. } => 7,
188        }
189    }
190
191    pub fn start_height(&self) -> Option<BlockHeight> {
192        match self {
193            SyncStatus::HeaderSync { start_height, .. } => Some(*start_height),
194            SyncStatus::BlockSync { start_height, .. } => Some(*start_height),
195            _ => None,
196        }
197    }
198
199    pub fn update(&mut self, new_value: Self) {
200        let _span =
201            tracing::debug_span!(target: "sync", "update_sync_status", old_value = ?self, ?new_value)
202                .entered();
203        *self = new_value;
204    }
205}
206
207impl From<EpochSyncStatus> for EpochSyncStatusView {
208    fn from(status: EpochSyncStatus) -> Self {
209        match status {
210            EpochSyncStatus::NotStarted => EpochSyncStatusView::NotStarted,
211            EpochSyncStatus::InProgress { source_peer_height, source_peer_id, attempt_time } => {
212                EpochSyncStatusView::InProgress {
213                    source_peer_height,
214                    source_peer_id: source_peer_id.to_string(),
215                    attempt_time: attempt_time.to_string(),
216                }
217            }
218            EpochSyncStatus::Done => EpochSyncStatusView::Done,
219        }
220    }
221}
222
223impl From<StateSyncStatus> for StateSyncStatusView {
224    fn from(status: StateSyncStatus) -> Self {
225        StateSyncStatusView {
226            sync_hash: status.sync_hash,
227            shard_sync_status: status
228                .sync_status
229                .iter()
230                .map(|(shard_id, shard_sync_status)| (*shard_id, shard_sync_status.to_string()))
231                .collect(),
232            download_tasks: status.download_tasks,
233            computation_tasks: status.computation_tasks,
234        }
235    }
236}
237
238impl From<SyncStatus> for SyncStatusView {
239    fn from(status: SyncStatus) -> Self {
240        match status {
241            SyncStatus::AwaitingPeers => SyncStatusView::AwaitingPeers,
242            SyncStatus::NoSync => SyncStatusView::NoSync,
243            SyncStatus::EpochSync(status) => SyncStatusView::EpochSync(status.into()),
244            SyncStatus::HeaderSync { start_height, current_height, highest_height } => {
245                SyncStatusView::HeaderSync { start_height, current_height, highest_height }
246            }
247            SyncStatus::StateSync(status) => SyncStatusView::StateSync(status.into()),
248            SyncStatus::BlockSync { start_height, current_height, highest_height } => {
249                SyncStatusView::BlockSync { start_height, current_height, highest_height }
250            }
251        }
252    }
253}
254
255/// Actor message requesting block by id, hash or sync state.
256#[derive(Clone, Debug)]
257pub struct GetBlock(pub BlockReference);
258
259#[derive(thiserror::Error, Debug)]
260pub enum GetBlockError {
261    #[error("IO Error: {error_message}")]
262    IOError { error_message: String },
263    #[error(
264        "Block either has never been observed on the node or has been garbage collected: {error_message}"
265    )]
266    UnknownBlock { error_message: String },
267    #[error("There are no fully synchronized blocks yet")]
268    NotSyncedYet,
269    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
270    // expected cases, we cannot statically guarantee that no other errors will be returned
271    // in the future.
272    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
273    #[error(
274        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
275    )]
276    Unreachable { error_message: String },
277}
278
279impl From<near_chain_primitives::Error> for GetBlockError {
280    fn from(error: near_chain_primitives::Error) -> Self {
281        match error {
282            near_chain_primitives::Error::IOErr(error) => {
283                Self::IOError { error_message: error.to_string() }
284            }
285            near_chain_primitives::Error::DBNotFoundErr(error_message) => {
286                Self::UnknownBlock { error_message }
287            }
288            _ => Self::Unreachable { error_message: error.to_string() },
289        }
290    }
291}
292
293impl GetBlock {
294    pub fn latest() -> Self {
295        Self(BlockReference::latest())
296    }
297}
298
299/// Get block with the block merkle tree. Used for testing
300#[derive(Debug)]
301pub struct GetBlockWithMerkleTree(pub BlockReference);
302
303impl GetBlockWithMerkleTree {
304    pub fn latest() -> Self {
305        Self(BlockReference::latest())
306    }
307}
308
309/// Actor message requesting a chunk by chunk hash and block hash + shard id.
310#[derive(Clone, Debug)]
311pub enum GetChunk {
312    Height(BlockHeight, ShardId),
313    BlockHash(CryptoHash, ShardId),
314    ChunkHash(ChunkHash),
315}
316
317/// Actor message requesting a chunk by chunk hash and block hash + shard id.
318/// The difference between this and `GetChunk` is that it returns the actual `ShardChunk`
319/// instead of a `ChunkView`
320#[derive(Debug)]
321pub enum GetShardChunk {
322    Height(BlockHeight, ShardId),
323    BlockHash(CryptoHash, ShardId),
324    ChunkHash(ChunkHash),
325}
326
327#[derive(thiserror::Error, Debug)]
328pub enum GetChunkError {
329    #[error("IO Error: {error_message}")]
330    IOError { error_message: String },
331    #[error(
332        "Block either has never been observed on the node or has been garbage collected: {error_message}"
333    )]
334    UnknownBlock { error_message: String },
335    #[error("Shard ID {shard_id} is invalid")]
336    InvalidShardId { shard_id: ShardId },
337    #[error("Chunk with hash {chunk_hash:?} has never been observed on this node")]
338    UnknownChunk { chunk_hash: ChunkHash },
339    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
340    // expected cases, we cannot statically guarantee that no other errors will be returned
341    // in the future.
342    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
343    #[error(
344        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
345    )]
346    Unreachable { error_message: String },
347}
348
349impl From<near_chain_primitives::Error> for GetChunkError {
350    fn from(error: near_chain_primitives::Error) -> Self {
351        match error {
352            near_chain_primitives::Error::IOErr(error) => {
353                Self::IOError { error_message: error.to_string() }
354            }
355            near_chain_primitives::Error::DBNotFoundErr(error_message) => {
356                Self::UnknownBlock { error_message }
357            }
358            near_chain_primitives::Error::InvalidShardId(shard_id) => {
359                Self::InvalidShardId { shard_id }
360            }
361            near_chain_primitives::Error::ChunkMissing(chunk_hash) => {
362                Self::UnknownChunk { chunk_hash }
363            }
364            _ => Self::Unreachable { error_message: error.to_string() },
365        }
366    }
367}
368
369/// Queries client for given path / data.
370#[derive(Clone, Debug)]
371pub struct Query {
372    pub block_reference: BlockReference,
373    pub request: QueryRequest,
374}
375
376impl Query {
377    pub fn new(block_reference: BlockReference, request: QueryRequest) -> Self {
378        Query { block_reference, request }
379    }
380}
381
382#[derive(thiserror::Error, Debug)]
383pub enum QueryError {
384    #[error("There are no fully synchronized blocks on the node yet")]
385    NoSyncedBlocks,
386    #[error("The node does not track the shard ID {requested_shard_id}")]
387    UnavailableShard { requested_shard_id: near_primitives::types::ShardId },
388    #[error("Account ID {requested_account_id} is invalid")]
389    InvalidAccount {
390        requested_account_id: near_primitives::types::AccountId,
391        block_height: near_primitives::types::BlockHeight,
392        block_hash: near_primitives::hash::CryptoHash,
393    },
394    #[error("Account {requested_account_id} does not exist while viewing at block #{block_height}")]
395    UnknownAccount {
396        requested_account_id: near_primitives::types::AccountId,
397        block_height: near_primitives::types::BlockHeight,
398        block_hash: near_primitives::hash::CryptoHash,
399    },
400    #[error(
401        "Contract code for contract ID {contract_account_id} has never been observed on the node at block #{block_height}"
402    )]
403    NoContractCode {
404        contract_account_id: near_primitives::types::AccountId,
405        block_height: near_primitives::types::BlockHeight,
406        block_hash: near_primitives::hash::CryptoHash,
407    },
408    #[error("State of contract {contract_account_id} is too large to be viewed")]
409    TooLargeContractState {
410        contract_account_id: near_primitives::types::AccountId,
411        block_height: near_primitives::types::BlockHeight,
412        block_hash: near_primitives::hash::CryptoHash,
413    },
414    #[error(
415        "Access key for public key {public_key} does not exist while viewing at block #{block_height}"
416    )]
417    UnknownAccessKey {
418        public_key: near_crypto::PublicKey,
419        block_height: near_primitives::types::BlockHeight,
420        block_hash: near_primitives::hash::CryptoHash,
421    },
422    #[error(
423        "Gas key for public key {public_key} does not exist while viewing at block #{block_height}"
424    )]
425    UnknownGasKey {
426        public_key: near_crypto::PublicKey,
427        block_height: near_primitives::types::BlockHeight,
428        block_hash: near_primitives::hash::CryptoHash,
429    },
430    #[error("Function call returned an error: {vm_error}")]
431    ContractExecutionError {
432        vm_error: String,
433        error: near_primitives::errors::FunctionCallError,
434        block_height: near_primitives::types::BlockHeight,
435        block_hash: near_primitives::hash::CryptoHash,
436    },
437    #[error("The node reached its limits. Try again later. More details: {error_message}")]
438    InternalError { error_message: String },
439    #[error(
440        "The data for block #{block_height} is garbage collected on this node, use an archival node to fetch historical data"
441    )]
442    GarbageCollectedBlock {
443        block_height: near_primitives::types::BlockHeight,
444        block_hash: near_primitives::hash::CryptoHash,
445    },
446    #[error(
447        "Block either has never been observed on the node or has been garbage collected: {block_reference:?}"
448    )]
449    UnknownBlock { block_reference: near_primitives::types::BlockReference },
450    #[error(
451        "Global contract code with identifier {identifier:?} has never been observed on the node"
452    )]
453    NoGlobalContractCode {
454        identifier: near_primitives::action::GlobalContractIdentifier,
455        block_height: near_primitives::types::BlockHeight,
456        block_hash: near_primitives::hash::CryptoHash,
457    },
458    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
459    // expected cases, we cannot statically guarantee that no other errors will be returned
460    // in the future.
461    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
462    #[error(
463        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
464    )]
465    Unreachable { error_message: String },
466}
467
468#[derive(Debug)]
469pub struct Status {
470    pub is_health_check: bool,
471    // If true - return more detailed information about the current status (recent blocks etc).
472    pub detailed: bool,
473}
474
475#[derive(thiserror::Error, Debug)]
476pub enum StatusError {
477    #[error("Node is syncing")]
478    NodeIsSyncing,
479    #[error("No blocks for {elapsed:?}")]
480    NoNewBlocks { elapsed: Duration },
481    #[error("Epoch Out Of Bounds {epoch_id:?}")]
482    EpochOutOfBounds { epoch_id: near_primitives::types::EpochId },
483    #[error("The node reached its limits. Try again later. More details: {error_message}")]
484    InternalError { error_message: String },
485    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
486    // expected cases, we cannot statically guarantee that no other errors will be returned
487    // in the future.
488    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
489    #[error(
490        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
491    )]
492    Unreachable { error_message: String },
493}
494
495impl From<near_chain_primitives::error::Error> for StatusError {
496    fn from(error: near_chain_primitives::error::Error) -> Self {
497        match error {
498            near_chain_primitives::error::Error::IOErr(error) => {
499                Self::InternalError { error_message: error.to_string() }
500            }
501            near_chain_primitives::error::Error::DBNotFoundErr(error_message)
502            | near_chain_primitives::error::Error::ValidatorError(error_message) => {
503                Self::InternalError { error_message }
504            }
505            near_chain_primitives::error::Error::EpochOutOfBounds(epoch_id) => {
506                Self::EpochOutOfBounds { epoch_id }
507            }
508            _ => Self::Unreachable { error_message: error.to_string() },
509        }
510    }
511}
512
513#[derive(Debug)]
514pub struct GetNextLightClientBlock {
515    pub last_block_hash: CryptoHash,
516}
517
518#[derive(thiserror::Error, Debug)]
519pub enum GetNextLightClientBlockError {
520    #[error("Internal error: {error_message}")]
521    InternalError { error_message: String },
522    #[error(
523        "Block either has never been observed on the node or has been garbage collected: {error_message}"
524    )]
525    UnknownBlock { error_message: String },
526    #[error("Epoch Out Of Bounds {epoch_id:?}")]
527    EpochOutOfBounds { epoch_id: near_primitives::types::EpochId },
528    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
529    // expected cases, we cannot statically guarantee that no other errors will be returned
530    // in the future.
531    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
532    #[error(
533        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
534    )]
535    Unreachable { error_message: String },
536}
537
538impl From<near_chain_primitives::error::Error> for GetNextLightClientBlockError {
539    fn from(error: near_chain_primitives::error::Error) -> Self {
540        match error {
541            near_chain_primitives::error::Error::DBNotFoundErr(error_message) => {
542                Self::UnknownBlock { error_message }
543            }
544            near_chain_primitives::error::Error::IOErr(error) => {
545                Self::InternalError { error_message: error.to_string() }
546            }
547            near_chain_primitives::error::Error::EpochOutOfBounds(epoch_id) => {
548                Self::EpochOutOfBounds { epoch_id }
549            }
550            _ => Self::Unreachable { error_message: error.to_string() },
551        }
552    }
553}
554
555#[derive(Debug)]
556pub struct GetNetworkInfo {}
557
558#[derive(Debug)]
559pub struct GetGasPrice {
560    pub block_id: MaybeBlockId,
561}
562
563#[derive(thiserror::Error, Debug)]
564pub enum GetGasPriceError {
565    #[error("Internal error: {error_message}")]
566    InternalError { error_message: String },
567    #[error(
568        "Block either has never been observed on the node or has been garbage collected: {error_message}"
569    )]
570    UnknownBlock { error_message: String },
571    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
572    // expected cases, we cannot statically guarantee that no other errors will be returned
573    // in the future.
574    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
575    #[error(
576        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
577    )]
578    Unreachable { error_message: String },
579}
580
581impl From<near_chain_primitives::Error> for GetGasPriceError {
582    fn from(error: near_chain_primitives::Error) -> Self {
583        match error {
584            near_chain_primitives::Error::IOErr(error) => {
585                Self::InternalError { error_message: error.to_string() }
586            }
587            near_chain_primitives::Error::DBNotFoundErr(error_message) => {
588                Self::UnknownBlock { error_message }
589            }
590            _ => Self::Unreachable { error_message: error.to_string() },
591        }
592    }
593}
594
595#[derive(Clone, Debug)]
596pub struct PeerInfo {
597    pub id: PeerId,
598    pub addr: Option<std::net::SocketAddr>,
599    pub account_id: Option<AccountId>,
600}
601
602#[derive(Clone, Debug)]
603pub struct KnownProducer {
604    pub account_id: AccountId,
605    pub addr: Option<std::net::SocketAddr>,
606    pub peer_id: PeerId,
607    pub next_hops: Option<Vec<PeerId>>,
608}
609
610#[derive(Debug)]
611pub struct NetworkInfoResponse {
612    pub connected_peers: Vec<PeerInfo>,
613    pub num_connected_peers: usize,
614    pub peer_max_count: u32,
615    pub sent_bytes_per_sec: u64,
616    pub received_bytes_per_sec: u64,
617    /// Accounts of known block and chunk producers from routing table.
618    pub known_producers: Vec<KnownProducer>,
619}
620
621/// Status of given transaction including all the subsequent receipts.
622#[derive(Debug)]
623pub struct TxStatus {
624    pub tx_hash: CryptoHash,
625    pub signer_account_id: AccountId,
626    pub fetch_receipt: bool,
627}
628
629#[derive(Debug)]
630pub enum TxStatusError {
631    ChainError(near_chain_primitives::Error),
632    MissingTransaction(CryptoHash),
633    InternalError(String),
634    TimeoutError,
635}
636
637impl From<near_chain_primitives::Error> for TxStatusError {
638    fn from(error: near_chain_primitives::Error) -> Self {
639        Self::ChainError(error)
640    }
641}
642
643#[derive(Debug)]
644pub struct GetValidatorInfo {
645    pub epoch_reference: EpochReference,
646}
647
648#[derive(thiserror::Error, Debug)]
649pub enum GetValidatorInfoError {
650    #[error("IO Error: {0}")]
651    IOError(String),
652    #[error("Unknown epoch")]
653    UnknownEpoch,
654    #[error("Validator info unavailable")]
655    ValidatorInfoUnavailable,
656    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
657    // expected cases, we cannot statically guarantee that no other errors will be returned
658    // in the future.
659    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
660    #[error(
661        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {0}"
662    )]
663    Unreachable(String),
664}
665
666impl From<near_chain_primitives::Error> for GetValidatorInfoError {
667    fn from(error: near_chain_primitives::Error) -> Self {
668        match error {
669            near_chain_primitives::Error::DBNotFoundErr(_)
670            | near_chain_primitives::Error::EpochOutOfBounds(_) => Self::UnknownEpoch,
671            near_chain_primitives::Error::IOErr(s) => Self::IOError(s.to_string()),
672            _ => Self::Unreachable(error.to_string()),
673        }
674    }
675}
676
677#[derive(Debug)]
678pub struct GetValidatorOrdered {
679    pub block_id: MaybeBlockId,
680}
681
682#[derive(Debug)]
683pub struct GetStateChanges {
684    pub block_hash: CryptoHash,
685    pub state_changes_request: StateChangesRequestView,
686}
687
688#[derive(thiserror::Error, Debug)]
689pub enum GetStateChangesError {
690    #[error("IO Error: {error_message}")]
691    IOError { error_message: String },
692    #[error(
693        "Block either has never been observed on the node or has been garbage collected: {error_message}"
694    )]
695    UnknownBlock { error_message: String },
696    #[error("There are no fully synchronized blocks yet")]
697    NotSyncedYet,
698    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
699    // expected cases, we cannot statically guarantee that no other errors will be returned
700    // in the future.
701    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
702    #[error(
703        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
704    )]
705    Unreachable { error_message: String },
706}
707
708impl From<near_chain_primitives::Error> for GetStateChangesError {
709    fn from(error: near_chain_primitives::Error) -> Self {
710        match error {
711            near_chain_primitives::Error::IOErr(error) => {
712                Self::IOError { error_message: error.to_string() }
713            }
714            near_chain_primitives::Error::DBNotFoundErr(error_message) => {
715                Self::UnknownBlock { error_message }
716            }
717            _ => Self::Unreachable { error_message: error.to_string() },
718        }
719    }
720}
721
722#[derive(Debug)]
723pub struct GetStateChangesInBlock {
724    pub block_hash: CryptoHash,
725}
726
727/// Probe whether the given shard's chunk at `block_hash` was applied on this
728/// node (i.e. `ChunkExtra` exists). Used by the sharded RPC coordinator to
729/// verify a peer actually has the data before trusting its response.
730#[derive(Debug)]
731pub struct GetChunkExtraExists {
732    pub block_hash: CryptoHash,
733    pub shard_uid: near_primitives::shard_layout::ShardUId,
734}
735
736#[derive(Debug)]
737pub struct GetStateChangesWithCauseInBlock {
738    pub block_hash: CryptoHash,
739}
740
741#[derive(Debug)]
742pub struct GetStateChangesWithCauseInBlockForTrackedShards {
743    pub block_hash: CryptoHash,
744    pub epoch_id: EpochId,
745}
746
747#[derive(Debug)]
748pub struct GetExecutionOutcome {
749    pub id: TransactionOrReceiptId,
750}
751
752#[derive(thiserror::Error, Debug)]
753pub enum GetExecutionOutcomeError {
754    #[error(
755        "Block either has never been observed on the node or has been garbage collected: {error_message}"
756    )]
757    UnknownBlock { error_message: String },
758    #[error(
759        "Inconsistent state. Total number of shards is {number_or_shards} but the execution outcome is in shard {execution_outcome_shard_id}"
760    )]
761    InconsistentState {
762        number_or_shards: usize,
763        execution_outcome_shard_id: near_primitives::types::ShardId,
764    },
765    #[error("{transaction_or_receipt_id} has not been confirmed")]
766    NotConfirmed { transaction_or_receipt_id: near_primitives::hash::CryptoHash },
767    #[error("{transaction_or_receipt_id} does not exist")]
768    UnknownTransactionOrReceipt { transaction_or_receipt_id: near_primitives::hash::CryptoHash },
769    #[error("Node doesn't track the shard where {transaction_or_receipt_id} is executed")]
770    UnavailableShard {
771        transaction_or_receipt_id: near_primitives::hash::CryptoHash,
772        shard_id: near_primitives::types::ShardId,
773    },
774    #[error("Internal error: {error_message}")]
775    InternalError { error_message: String },
776    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
777    // expected cases, we cannot statically guarantee that no other errors will be returned
778    // in the future.
779    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
780    #[error(
781        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
782    )]
783    Unreachable { error_message: String },
784}
785
786impl From<TxStatusError> for GetExecutionOutcomeError {
787    fn from(error: TxStatusError) -> Self {
788        match error {
789            TxStatusError::ChainError(err) => {
790                Self::InternalError { error_message: err.to_string() }
791            }
792            _ => Self::Unreachable { error_message: format!("{:?}", error) },
793        }
794    }
795}
796
797impl From<near_chain_primitives::error::Error> for GetExecutionOutcomeError {
798    fn from(error: near_chain_primitives::error::Error) -> Self {
799        match error {
800            near_chain_primitives::Error::IOErr(error) => {
801                Self::InternalError { error_message: error.to_string() }
802            }
803            near_chain_primitives::Error::DBNotFoundErr(error_message) => {
804                Self::UnknownBlock { error_message }
805            }
806            _ => Self::Unreachable { error_message: error.to_string() },
807        }
808    }
809}
810
811#[derive(Debug)]
812pub struct GetExecutionOutcomeResponse {
813    pub outcome_proof: ExecutionOutcomeWithIdView,
814    pub outcome_root_proof: MerklePath,
815}
816
817#[derive(Debug)]
818pub struct GetExecutionOutcomesForBlock {
819    pub block_hash: CryptoHash,
820}
821
822#[derive(Debug)]
823pub struct GetProcessedReceiptIds {
824    pub block_hash: CryptoHash,
825    pub shard_id: ShardId,
826}
827
828#[derive(thiserror::Error, Debug)]
829pub enum GetProcessedReceiptIdsError {
830    #[error("IO Error: {error_message}")]
831    IOError { error_message: String },
832    #[error("Block or shard data not found: {error_message}")]
833    UnknownBlock { error_message: String },
834    #[error(
835        "It is a bug if you receive this error type, please, report this incident: \
836         https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
837    )]
838    Unreachable { error_message: String },
839}
840
841impl From<near_chain_primitives::error::Error> for GetProcessedReceiptIdsError {
842    fn from(error: near_chain_primitives::error::Error) -> Self {
843        match error {
844            near_chain_primitives::Error::IOErr(error) => {
845                Self::IOError { error_message: error.to_string() }
846            }
847            near_chain_primitives::Error::DBNotFoundErr(error_message) => {
848                Self::UnknownBlock { error_message }
849            }
850            _ => Self::Unreachable { error_message: error.to_string() },
851        }
852    }
853}
854
855#[derive(Debug)]
856pub struct GetBlockProof {
857    pub block_hash: CryptoHash,
858    pub head_block_hash: CryptoHash,
859}
860
861pub struct GetBlockProofResponse {
862    pub block_header_lite: LightClientBlockLiteView,
863    pub proof: MerklePath,
864}
865
866#[derive(thiserror::Error, Debug)]
867pub enum GetBlockProofError {
868    #[error(
869        "Block either has never been observed on the node or has been garbage collected: {error_message}"
870    )]
871    UnknownBlock { error_message: String },
872    #[error("Internal error: {error_message}")]
873    InternalError { error_message: String },
874    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
875    // expected cases, we cannot statically guarantee that no other errors will be returned
876    // in the future.
877    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
878    #[error(
879        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {error_message}"
880    )]
881    Unreachable { error_message: String },
882}
883
884impl From<near_chain_primitives::error::Error> for GetBlockProofError {
885    fn from(error: near_chain_primitives::error::Error) -> Self {
886        match error {
887            near_chain_primitives::error::Error::DBNotFoundErr(error_message) => {
888                Self::UnknownBlock { error_message }
889            }
890            near_chain_primitives::error::Error::Other(error_message) => {
891                Self::InternalError { error_message }
892            }
893            err => Self::Unreachable { error_message: err.to_string() },
894        }
895    }
896}
897
898#[derive(Debug)]
899pub struct GetReceipt {
900    pub receipt_id: CryptoHash,
901}
902
903#[derive(thiserror::Error, Debug)]
904pub enum GetReceiptError {
905    #[error("IO Error: {0}")]
906    IOError(String),
907    #[error("Receipt with id {0} has never been observed on this node")]
908    UnknownReceipt(near_primitives::hash::CryptoHash),
909    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
910    // expected cases, we cannot statically guarantee that no other errors will be returned
911    // in the future.
912    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
913    #[error(
914        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {0}"
915    )]
916    Unreachable(String),
917}
918
919impl From<near_chain_primitives::Error> for GetReceiptError {
920    fn from(error: near_chain_primitives::Error) -> Self {
921        match error {
922            near_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()),
923            _ => Self::Unreachable(error.to_string()),
924        }
925    }
926}
927
928#[derive(Debug)]
929pub struct GetReceiptToTx {
930    pub receipt_id: CryptoHash,
931    /// Block height near where receipt was created. Enables hint mode:
932    /// handler falls back to `±window` scan when local `ReceiptToTx` column
933    /// misses mid-walk. `shard_id` narrows first scan; omit → all tracked
934    /// shards at hint height. `window` overrides default scan range.
935    pub block_height: Option<BlockHeight>,
936    pub shard_id: Option<ShardId>,
937    pub window: Option<BlockHeightDelta>,
938}
939
940#[derive(Debug)]
941pub struct GetReceiptToTxResponse {
942    pub transaction_hash: CryptoHash,
943    pub sender_account_id: AccountId,
944}
945
946#[derive(thiserror::Error, Debug)]
947pub enum GetReceiptToTxError {
948    #[error("Receipt with id {0} has never been observed on this node")]
949    UnknownReceipt(CryptoHash),
950    #[error("depth limit {limit} exceeded when resolving receipt {receipt_id}")]
951    DepthExceeded { receipt_id: CryptoHash, limit: u32 },
952    #[error("this node does not support receipt-to-tx lookup: {0}")]
953    Unsupported(String),
954    #[error("execution outcomes are not stored on this node (save_tx_outcomes=false)")]
955    OutcomesNotStored,
956    #[error("requested window {requested} exceeds maximum {maximum}")]
957    WindowTooLarge { requested: BlockHeightDelta, maximum: BlockHeightDelta },
958    #[error("malformed hint: {0}")]
959    MalformedHint(String),
960    #[error("hint-scan budget exceeded: {scanned} outcomes scanned, limit {limit}")]
961    BudgetExceeded { scanned: u64, limit: u64 },
962    #[error("internal error: {0}")]
963    InternalError(String),
964}
965
966#[derive(Debug)]
967pub struct GetProtocolConfig(pub BlockReference);
968
969#[derive(thiserror::Error, Debug)]
970pub enum GetProtocolConfigError {
971    #[error("IO Error: {0}")]
972    IOError(String),
973    #[error("Block has never been observed: {0}")]
974    UnknownBlock(String),
975    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
976    // expected cases, we cannot statically guarantee that no other errors will be returned
977    // in the future.
978    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
979    #[error(
980        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {0}"
981    )]
982    Unreachable(String),
983}
984
985impl From<near_chain_primitives::Error> for GetProtocolConfigError {
986    fn from(error: near_chain_primitives::Error) -> Self {
987        match error {
988            near_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()),
989            near_chain_primitives::Error::DBNotFoundErr(s) => Self::UnknownBlock(s),
990            _ => Self::Unreachable(error.to_string()),
991        }
992    }
993}
994
995#[derive(Debug)]
996pub struct GetMaintenanceWindows {
997    pub account_id: AccountId,
998}
999
1000#[derive(thiserror::Error, Debug)]
1001pub enum GetMaintenanceWindowsError {
1002    #[error("IO Error: {0}")]
1003    IOError(String),
1004    #[error(
1005        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {0}"
1006    )]
1007    Unreachable(String),
1008}
1009
1010impl From<near_chain_primitives::Error> for GetMaintenanceWindowsError {
1011    fn from(error: near_chain_primitives::Error) -> Self {
1012        match error {
1013            near_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()),
1014            _ => Self::Unreachable(error.to_string()),
1015        }
1016    }
1017}
1018
1019#[derive(Debug)]
1020pub struct GetClientConfig {}
1021
1022#[derive(thiserror::Error, Debug)]
1023pub enum GetClientConfigError {
1024    #[error("IO Error: {0}")]
1025    IOError(String),
1026    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
1027    // expected cases, we cannot statically guarantee that no other errors will be returned
1028    // in the future.
1029    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
1030    #[error(
1031        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {0}"
1032    )]
1033    Unreachable(String),
1034}
1035
1036impl From<near_chain_primitives::Error> for GetClientConfigError {
1037    fn from(error: near_chain_primitives::Error) -> Self {
1038        match error {
1039            near_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()),
1040            _ => Self::Unreachable(error.to_string()),
1041        }
1042    }
1043}
1044
1045#[derive(Debug)]
1046pub struct GetSplitStorageInfo {}
1047
1048#[derive(thiserror::Error, Debug)]
1049pub enum GetSplitStorageInfoError {
1050    #[error("IO Error: {0}")]
1051    IOError(String),
1052    // NOTE: Currently, the underlying errors are too broad, and while we tried to handle
1053    // expected cases, we cannot statically guarantee that no other errors will be returned
1054    // in the future.
1055    // TODO #3851: Remove this variant once we can exhaustively match all the underlying errors
1056    #[error(
1057        "It is a bug if you receive this error type, please, report this incident: https://github.com/near/nearcore/issues/new/choose. Details: {0}"
1058    )]
1059    Unreachable(String),
1060}
1061
1062impl From<near_chain_primitives::Error> for GetSplitStorageInfoError {
1063    fn from(error: near_chain_primitives::Error) -> Self {
1064        match error {
1065            near_chain_primitives::Error::IOErr(error) => Self::IOError(error.to_string()),
1066            _ => Self::Unreachable(error.to_string()),
1067        }
1068    }
1069}
1070
1071impl From<std::io::Error> for GetSplitStorageInfoError {
1072    fn from(error: std::io::Error) -> Self {
1073        Self::IOError(error.to_string())
1074    }
1075}
1076
1077#[cfg(feature = "sandbox")]
1078#[derive(Debug)]
1079pub enum SandboxMessage {
1080    SandboxPatchState(Vec<near_primitives::state_record::StateRecord>),
1081    SandboxPatchStateStatus,
1082    SandboxFastForward(near_primitives::types::BlockHeightDelta),
1083    SandboxFastForwardStatus,
1084}
1085
1086#[cfg(feature = "sandbox")]
1087#[derive(Eq, PartialEq, Debug)]
1088pub enum SandboxResponse {
1089    SandboxPatchStateFinished(bool),
1090    SandboxFastForwardFinished(bool),
1091    SandboxFastForwardFailed(String),
1092    SandboxNoResponse,
1093}
1094
1095/// Notification that a new block has been postprocessed by Client.
1096#[derive(Debug, Clone)]
1097pub struct BlockNotificationMessage {
1098    pub block: Arc<Block>,
1099}