near_primitives/
types.rs

1use crate::account::{AccessKey, Account};
2use crate::errors::EpochError;
3use crate::hash::CryptoHash;
4use crate::shard_layout::ShardLayout;
5use crate::stateless_validation::spice_chunk_endorsement::SpiceStoredVerifiedEndorsement;
6use crate::trie_key::TrieKey;
7use borsh::{BorshDeserialize, BorshSerialize};
8pub use chunk_validator_stats::ChunkStats;
9use near_crypto::PublicKey;
10use near_primitives_core::account::GasKey;
11use near_primitives_core::hash::hash;
12/// Reexport primitive types
13pub use near_primitives_core::types::*;
14use near_schema_checker_lib::ProtocolSchema;
15use serde_with::base64::Base64;
16use serde_with::serde_as;
17use std::collections::HashMap;
18use std::sync::Arc;
19use std::sync::LazyLock;
20
21use self::chunk_extra::ChunkExtra;
22
23mod chunk_validator_stats;
24
25/// Hash used by to store state root.
26pub type StateRoot = CryptoHash;
27
28/// An arbitrary static string to make sure that this struct cannot be
29/// serialized to look identical to another serialized struct. For chunk
30/// production we are signing a chunk hash, so we need to make sure that
31/// this signature means something different.
32///
33/// This is a messy workaround until we know what to do with NEP 483.
34pub(crate) type SignatureDifferentiator = String;
35pub(crate) type StaticSignatureDifferentiator = &'static str;
36
37/// Different types of finality.
38#[derive(
39    serde::Serialize, serde::Deserialize, Default, Clone, Debug, PartialEq, Eq, arbitrary::Arbitrary,
40)]
41#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
42pub enum Finality {
43    #[serde(rename = "optimistic")]
44    None,
45    #[serde(rename = "near-final")]
46    DoomSlug,
47    #[serde(rename = "final")]
48    #[default]
49    Final,
50}
51
52/// Account ID with its public key.
53#[derive(Debug, serde::Serialize, serde::Deserialize)]
54#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
55pub struct AccountWithPublicKey {
56    pub account_id: AccountId,
57    pub public_key: PublicKey,
58}
59
60/// Account info for validators
61#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)]
62#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
63pub struct AccountInfo {
64    pub account_id: AccountId,
65    pub public_key: PublicKey,
66    pub amount: Balance,
67}
68
69/// This type is used to mark keys (arrays of bytes) that are queried from store.
70///
71/// NOTE: Currently, this type is only used in the view_client and RPC to be able to transparently
72/// pretty-serialize the bytes arrays as base64-encoded strings (see `serialize.rs`).
73#[serde_as]
74#[derive(
75    serde::Serialize,
76    serde::Deserialize,
77    Clone,
78    Debug,
79    PartialEq,
80    Eq,
81    derive_more::Deref,
82    derive_more::From,
83    derive_more::Into,
84    BorshSerialize,
85    BorshDeserialize,
86)]
87#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
88#[serde(transparent)]
89pub struct StoreKey(
90    #[serde_as(as = "Base64")]
91    #[cfg_attr(feature = "schemars", schemars(schema_with = "crate::serialize::base64_schema"))]
92    Vec<u8>,
93);
94
95/// This type is used to mark values returned from store (arrays of bytes).
96///
97/// NOTE: Currently, this type is only used in the view_client and RPC to be able to transparently
98/// pretty-serialize the bytes arrays as base64-encoded strings (see `serialize.rs`).
99#[serde_as]
100#[derive(
101    serde::Serialize,
102    serde::Deserialize,
103    Clone,
104    Debug,
105    PartialEq,
106    Eq,
107    derive_more::Deref,
108    derive_more::From,
109    derive_more::Into,
110    BorshSerialize,
111    BorshDeserialize,
112)]
113#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
114#[serde(transparent)]
115pub struct StoreValue(
116    #[serde_as(as = "Base64")]
117    #[cfg_attr(feature = "schemars", schemars(schema_with = "crate::serialize::base64_schema"))]
118    Vec<u8>,
119);
120
121/// This type is used to mark function arguments.
122///
123/// NOTE: The main reason for this to exist (except the type-safety) is that the value is
124/// transparently serialized and deserialized as a base64-encoded string when serde is used
125/// (serde_json).
126#[serde_as]
127#[derive(
128    serde::Serialize,
129    serde::Deserialize,
130    Clone,
131    Debug,
132    PartialEq,
133    Eq,
134    derive_more::Deref,
135    derive_more::From,
136    derive_more::Into,
137    BorshSerialize,
138    BorshDeserialize,
139)]
140#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
141#[serde(transparent)]
142pub struct FunctionArgs(
143    #[serde_as(as = "Base64")]
144    #[cfg_attr(feature = "schemars", schemars(schema_with = "crate::serialize::base64_schema"))]
145    Vec<u8>,
146);
147
148/// A structure used to indicate the kind of state changes due to transaction/receipt processing, etc.
149#[derive(Debug, Clone)]
150pub enum StateChangeKind {
151    AccountTouched { account_id: AccountId },
152    AccessKeyTouched { account_id: AccountId },
153    DataTouched { account_id: AccountId },
154    ContractCodeTouched { account_id: AccountId },
155}
156
157pub type StateChangesKinds = Vec<StateChangeKind>;
158
159#[easy_ext::ext(StateChangesKindsExt)]
160impl StateChangesKinds {
161    pub fn from_changes(
162        raw_changes: &mut dyn Iterator<Item = Result<RawStateChangesWithTrieKey, std::io::Error>>,
163    ) -> Result<StateChangesKinds, std::io::Error> {
164        raw_changes
165            .filter_map(|raw_change| {
166                let RawStateChangesWithTrieKey { trie_key, .. } = match raw_change {
167                    Ok(p) => p,
168                    Err(e) => return Some(Err(e)),
169                };
170                match trie_key {
171                    TrieKey::Account { account_id } => {
172                        Some(Ok(StateChangeKind::AccountTouched { account_id }))
173                    }
174                    TrieKey::ContractCode { account_id } => {
175                        Some(Ok(StateChangeKind::ContractCodeTouched { account_id }))
176                    }
177                    TrieKey::AccessKey { account_id, .. } => {
178                        Some(Ok(StateChangeKind::AccessKeyTouched { account_id }))
179                    }
180                    TrieKey::ContractData { account_id, .. } => {
181                        Some(Ok(StateChangeKind::DataTouched { account_id }))
182                    }
183                    _ => None,
184                }
185            })
186            .collect()
187    }
188}
189
190/// A structure used to index state changes due to transaction/receipt processing and other things.
191#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, PartialEq, ProtocolSchema)]
192#[borsh(use_discriminant = true)]
193#[repr(u8)]
194pub enum StateChangeCause {
195    /// A type of update that does not get finalized. Used for verification and execution of
196    /// immutable smart contract methods. Attempt fo finalize a `TrieUpdate` containing such
197    /// change will lead to panic.
198    NotWritableToDisk = 0,
199    /// A type of update that is used to mark the initial storage update, e.g. during genesis
200    /// or in tests setup.
201    InitialState = 1,
202    /// Processing of a transaction.
203    TransactionProcessing { tx_hash: CryptoHash } = 2,
204    /// Before the receipt is going to be processed, inputs get drained from the state, which
205    /// causes state modification.
206    ActionReceiptProcessingStarted { receipt_hash: CryptoHash } = 3,
207    /// Computation of gas reward.
208    ActionReceiptGasReward { receipt_hash: CryptoHash } = 4,
209    /// Processing of a receipt.
210    ReceiptProcessing { receipt_hash: CryptoHash } = 5,
211    /// The given receipt was postponed. This is either a data receipt or an action receipt.
212    /// A `DataReceipt` can be postponed if the corresponding `ActionReceipt` is not received yet,
213    /// or other data dependencies are not satisfied.
214    /// An `ActionReceipt` can be postponed if not all data dependencies are received.
215    PostponedReceipt { receipt_hash: CryptoHash } = 6,
216    /// Updated delayed receipts queue in the state.
217    /// We either processed previously delayed receipts or added more receipts to the delayed queue.
218    UpdatedDelayedReceipts = 7,
219    /// State change that happens when we update validator accounts. Not associated with any
220    /// specific transaction or receipt.
221    ValidatorAccountsUpdate = 8,
222    /// State change that is happens due to migration that happens in first block of an epoch
223    /// after protocol upgrade
224    Migration = 9,
225    /// Deprecated in #13155, we need to keep it to preserve enum variant tags for borsh serialization.
226    _UnusedReshardingV2 = 10,
227    /// Update persistent state kept by Bandwidth Scheduler after running the scheduling algorithm.
228    BandwidthSchedulerStateUpdate = 11,
229}
230
231/// This represents the committed changes in the Trie with a change cause.
232#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, ProtocolSchema)]
233pub struct RawStateChange {
234    pub cause: StateChangeCause,
235    pub data: Option<Vec<u8>>,
236}
237
238/// List of committed changes with a cause for a given TrieKey
239#[derive(Debug, Clone, BorshSerialize, BorshDeserialize, ProtocolSchema)]
240pub struct RawStateChangesWithTrieKey {
241    pub trie_key: TrieKey,
242    pub changes: Vec<RawStateChange>,
243}
244
245/// Consolidate state change of trie_key and the final value the trie key will be changed to
246#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, ProtocolSchema)]
247pub struct ConsolidatedStateChange {
248    pub trie_key: TrieKey,
249    pub value: Option<Vec<u8>>,
250}
251
252/// key that was updated -> list of updates with the corresponding indexing event.
253pub type RawStateChanges = std::collections::BTreeMap<Vec<u8>, RawStateChangesWithTrieKey>;
254
255#[derive(Debug)]
256pub enum StateChangesRequest {
257    AccountChanges { account_ids: Vec<AccountId> },
258    SingleAccessKeyChanges { keys: Vec<AccountWithPublicKey> },
259    SingleGasKeyChanges { keys: Vec<AccountWithPublicKey> },
260    AllAccessKeyChanges { account_ids: Vec<AccountId> },
261    AllGasKeyChanges { account_ids: Vec<AccountId> },
262    ContractCodeChanges { account_ids: Vec<AccountId> },
263    DataChanges { account_ids: Vec<AccountId>, key_prefix: StoreKey },
264}
265
266#[derive(Debug)]
267pub enum StateChangeValue {
268    AccountUpdate { account_id: AccountId, account: Account },
269    AccountDeletion { account_id: AccountId },
270    AccessKeyUpdate { account_id: AccountId, public_key: PublicKey, access_key: AccessKey },
271    AccessKeyDeletion { account_id: AccountId, public_key: PublicKey },
272    GasKeyUpdate { account_id: AccountId, public_key: PublicKey, gas_key: GasKey },
273    GasKeyNonceUpdate { account_id: AccountId, public_key: PublicKey, index: u32, nonce: u64 },
274    GasKeyDeletion { account_id: AccountId, public_key: PublicKey },
275    DataUpdate { account_id: AccountId, key: StoreKey, value: StoreValue },
276    DataDeletion { account_id: AccountId, key: StoreKey },
277    ContractCodeUpdate { account_id: AccountId, code: Vec<u8> },
278    ContractCodeDeletion { account_id: AccountId },
279}
280
281impl StateChangeValue {
282    pub fn affected_account_id(&self) -> &AccountId {
283        match &self {
284            StateChangeValue::AccountUpdate { account_id, .. }
285            | StateChangeValue::AccountDeletion { account_id }
286            | StateChangeValue::AccessKeyUpdate { account_id, .. }
287            | StateChangeValue::AccessKeyDeletion { account_id, .. }
288            | StateChangeValue::GasKeyUpdate { account_id, .. }
289            | StateChangeValue::GasKeyNonceUpdate { account_id, .. }
290            | StateChangeValue::GasKeyDeletion { account_id, .. }
291            | StateChangeValue::DataUpdate { account_id, .. }
292            | StateChangeValue::DataDeletion { account_id, .. }
293            | StateChangeValue::ContractCodeUpdate { account_id, .. }
294            | StateChangeValue::ContractCodeDeletion { account_id } => account_id,
295        }
296    }
297}
298
299#[derive(Debug)]
300pub struct StateChangeWithCause {
301    pub cause: StateChangeCause,
302    pub value: StateChangeValue,
303}
304
305pub type StateChanges = Vec<StateChangeWithCause>;
306
307#[easy_ext::ext(StateChangesExt)]
308impl StateChanges {
309    pub fn from_changes(
310        raw_changes: impl Iterator<Item = Result<RawStateChangesWithTrieKey, std::io::Error>>,
311    ) -> Result<StateChanges, std::io::Error> {
312        let mut state_changes = Self::new();
313
314        for raw_change in raw_changes {
315            let RawStateChangesWithTrieKey { trie_key, changes } = raw_change?;
316
317            match trie_key {
318                TrieKey::Account { account_id } => state_changes.extend(changes.into_iter().map(
319                    |RawStateChange { cause, data }| StateChangeWithCause {
320                        cause,
321                        value: if let Some(change_data) = data {
322                            StateChangeValue::AccountUpdate {
323                                account_id: account_id.clone(),
324                                account: <_>::try_from_slice(&change_data).expect(
325                                    "Failed to parse internally stored account information",
326                                ),
327                            }
328                        } else {
329                            StateChangeValue::AccountDeletion { account_id: account_id.clone() }
330                        },
331                    },
332                )),
333                TrieKey::AccessKey { account_id, public_key } => {
334                    state_changes.extend(changes.into_iter().map(
335                        |RawStateChange { cause, data }| StateChangeWithCause {
336                            cause,
337                            value: if let Some(change_data) = data {
338                                StateChangeValue::AccessKeyUpdate {
339                                    account_id: account_id.clone(),
340                                    public_key: public_key.clone(),
341                                    access_key: <_>::try_from_slice(&change_data)
342                                        .expect("Failed to parse internally stored access key"),
343                                }
344                            } else {
345                                StateChangeValue::AccessKeyDeletion {
346                                    account_id: account_id.clone(),
347                                    public_key: public_key.clone(),
348                                }
349                            },
350                        },
351                    ))
352                }
353                TrieKey::GasKey { account_id, public_key, index } => {
354                    if let Some(index) = index {
355                        state_changes.extend(changes.into_iter().filter_map(
356                            |RawStateChange { cause, data }| {
357                                if let Some(change_data) = data {
358                                    Some(StateChangeWithCause {
359                                        cause,
360                                        value: StateChangeValue::GasKeyNonceUpdate {
361                                            account_id: account_id.clone(),
362                                            public_key: public_key.clone(),
363                                            index,
364                                            nonce: <_>::try_from_slice(&change_data).expect(
365                                                "Failed to parse internally stored gas key nonce",
366                                            ),
367                                        },
368                                    })
369                                } else {
370                                    // Deletion of a nonce can only be done with a corresponding
371                                    // deletion of the gas key, so we don't need to report these.
372                                    None
373                                }
374                            },
375                        ));
376                    } else {
377                        state_changes.extend(changes.into_iter().map(
378                            |RawStateChange { cause, data }| StateChangeWithCause {
379                                cause,
380                                value: if let Some(change_data) = data {
381                                    StateChangeValue::GasKeyUpdate {
382                                        account_id: account_id.clone(),
383                                        public_key: public_key.clone(),
384                                        gas_key: <_>::try_from_slice(&change_data)
385                                            .expect("Failed to parse internally stored gas key"),
386                                    }
387                                } else {
388                                    StateChangeValue::GasKeyDeletion {
389                                        account_id: account_id.clone(),
390                                        public_key: public_key.clone(),
391                                    }
392                                },
393                            },
394                        ));
395                    }
396                }
397                TrieKey::ContractCode { account_id } => {
398                    state_changes.extend(changes.into_iter().map(
399                        |RawStateChange { cause, data }| StateChangeWithCause {
400                            cause,
401                            value: match data {
402                                Some(change_data) => StateChangeValue::ContractCodeUpdate {
403                                    account_id: account_id.clone(),
404                                    code: change_data,
405                                },
406                                None => StateChangeValue::ContractCodeDeletion {
407                                    account_id: account_id.clone(),
408                                },
409                            },
410                        },
411                    ));
412                }
413                TrieKey::ContractData { account_id, key } => {
414                    state_changes.extend(changes.into_iter().map(
415                        |RawStateChange { cause, data }| StateChangeWithCause {
416                            cause,
417                            value: if let Some(change_data) = data {
418                                StateChangeValue::DataUpdate {
419                                    account_id: account_id.clone(),
420                                    key: key.to_vec().into(),
421                                    value: change_data.into(),
422                                }
423                            } else {
424                                StateChangeValue::DataDeletion {
425                                    account_id: account_id.clone(),
426                                    key: key.to_vec().into(),
427                                }
428                            },
429                        },
430                    ));
431                }
432                // The next variants considered as unnecessary as too low level
433                TrieKey::ReceivedData { .. } => {}
434                TrieKey::PostponedReceiptId { .. } => {}
435                TrieKey::PendingDataCount { .. } => {}
436                TrieKey::PostponedReceipt { .. } => {}
437                TrieKey::DelayedReceiptIndices => {}
438                TrieKey::DelayedReceipt { .. } => {}
439                TrieKey::PromiseYieldIndices => {}
440                TrieKey::PromiseYieldTimeout { .. } => {}
441                TrieKey::PromiseYieldReceipt { .. } => {}
442                TrieKey::BufferedReceiptIndices => {}
443                TrieKey::BufferedReceipt { .. } => {}
444                TrieKey::BandwidthSchedulerState => {}
445                TrieKey::BufferedReceiptGroupsQueueData { .. } => {}
446                TrieKey::BufferedReceiptGroupsQueueItem { .. } => {}
447                // Global contract code is not a part of account, so ignoring it as well.
448                TrieKey::GlobalContractCode { .. } => {}
449            }
450        }
451
452        Ok(state_changes)
453    }
454    pub fn from_account_changes(
455        raw_changes: impl Iterator<Item = Result<RawStateChangesWithTrieKey, std::io::Error>>,
456    ) -> Result<StateChanges, std::io::Error> {
457        let state_changes = Self::from_changes(raw_changes)?;
458
459        Ok(state_changes
460            .into_iter()
461            .filter(|state_change| {
462                matches!(
463                    state_change.value,
464                    StateChangeValue::AccountUpdate { .. }
465                        | StateChangeValue::AccountDeletion { .. }
466                )
467            })
468            .collect())
469    }
470
471    pub fn from_access_key_changes(
472        raw_changes: impl Iterator<Item = Result<RawStateChangesWithTrieKey, std::io::Error>>,
473    ) -> Result<StateChanges, std::io::Error> {
474        let state_changes = Self::from_changes(raw_changes)?;
475
476        Ok(state_changes
477            .into_iter()
478            .filter(|state_change| {
479                matches!(
480                    state_change.value,
481                    StateChangeValue::AccessKeyUpdate { .. }
482                        | StateChangeValue::AccessKeyDeletion { .. }
483                )
484            })
485            .collect())
486    }
487
488    pub fn from_gas_key_changes(
489        raw_changes: impl Iterator<Item = Result<RawStateChangesWithTrieKey, std::io::Error>>,
490    ) -> Result<StateChanges, std::io::Error> {
491        let state_changes = Self::from_changes(raw_changes)?;
492
493        Ok(state_changes
494            .into_iter()
495            .filter(|state_change| {
496                matches!(
497                    state_change.value,
498                    StateChangeValue::GasKeyUpdate { .. }
499                        | StateChangeValue::GasKeyNonceUpdate { .. }
500                        | StateChangeValue::GasKeyDeletion { .. }
501                )
502            })
503            .collect())
504    }
505
506    pub fn from_contract_code_changes(
507        raw_changes: impl Iterator<Item = Result<RawStateChangesWithTrieKey, std::io::Error>>,
508    ) -> Result<StateChanges, std::io::Error> {
509        let state_changes = Self::from_changes(raw_changes)?;
510
511        Ok(state_changes
512            .into_iter()
513            .filter(|state_change| {
514                matches!(
515                    state_change.value,
516                    StateChangeValue::ContractCodeUpdate { .. }
517                        | StateChangeValue::ContractCodeDeletion { .. }
518                )
519            })
520            .collect())
521    }
522
523    pub fn from_data_changes(
524        raw_changes: impl Iterator<Item = Result<RawStateChangesWithTrieKey, std::io::Error>>,
525    ) -> Result<StateChanges, std::io::Error> {
526        let state_changes = Self::from_changes(raw_changes)?;
527
528        Ok(state_changes
529            .into_iter()
530            .filter(|state_change| {
531                matches!(
532                    state_change.value,
533                    StateChangeValue::DataUpdate { .. } | StateChangeValue::DataDeletion { .. }
534                )
535            })
536            .collect())
537    }
538}
539
540#[derive(
541    PartialEq, Eq, Clone, Debug, BorshSerialize, BorshDeserialize, serde::Serialize, ProtocolSchema,
542)]
543pub struct StateRootNode {
544    /// In Nightshade, data is the serialized TrieNodeWithSize.
545    ///
546    /// Beware that hash of an empty state root (i.e. once who’s data is an
547    /// empty byte string) **does not** equal hash of an empty byte string.
548    /// Instead, an all-zero hash indicates an empty node.
549    pub data: Arc<[u8]>,
550
551    /// In Nightshade, memory_usage is a field of TrieNodeWithSize.
552    pub memory_usage: u64,
553}
554
555impl StateRootNode {
556    pub fn empty() -> Self {
557        static EMPTY: LazyLock<Arc<[u8]>> = LazyLock::new(|| Arc::new([]));
558        StateRootNode { data: EMPTY.clone(), memory_usage: 0 }
559    }
560}
561
562/// Epoch identifier -- wrapped hash, to make it easier to distinguish.
563/// EpochId of epoch T is the hash of last block in T-2
564/// EpochId of first two epochs is 0
565#[derive(
566    Debug,
567    Clone,
568    Copy,
569    Default,
570    Hash,
571    Eq,
572    PartialEq,
573    PartialOrd,
574    Ord,
575    derive_more::AsRef,
576    BorshSerialize,
577    BorshDeserialize,
578    serde::Serialize,
579    serde::Deserialize,
580    arbitrary::Arbitrary,
581    ProtocolSchema,
582)]
583#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
584#[as_ref(forward)]
585pub struct EpochId(pub CryptoHash);
586
587impl std::str::FromStr for EpochId {
588    type Err = Box<dyn std::error::Error + Send + Sync>;
589
590    /// Decodes base58-encoded string into a 32-byte crypto hash.
591    fn from_str(epoch_id_str: &str) -> Result<Self, Self::Err> {
592        Ok(EpochId(CryptoHash::from_str(epoch_id_str)?))
593    }
594}
595
596/// Stores validator and its stake for two consecutive epochs.
597/// It is necessary because the blocks on the epoch boundary need to contain approvals from both
598/// epochs.
599#[derive(serde::Serialize, Debug, Clone, PartialEq, Eq)]
600pub struct ApprovalStake {
601    /// Account that stakes money.
602    pub account_id: AccountId,
603    /// Public key of the proposed validator.
604    pub public_key: PublicKey,
605    /// Stake / weight of the validator.
606    pub stake_this_epoch: Balance,
607    pub stake_next_epoch: Balance,
608}
609
610pub mod validator_stake {
611    use crate::types::ApprovalStake;
612    use borsh::{BorshDeserialize, BorshSerialize};
613    use near_crypto::{KeyType, PublicKey};
614    use near_primitives_core::types::{AccountId, Balance};
615    use serde::Serialize;
616
617    pub use super::ValidatorStakeV1;
618
619    /// Stores validator and its stake.
620    #[derive(BorshSerialize, BorshDeserialize, Serialize, Debug, Clone, PartialEq, Eq)]
621    #[serde(tag = "validator_stake_struct_version")]
622    #[borsh(use_discriminant = true)]
623    #[repr(u8)]
624    pub enum ValidatorStake {
625        V1(ValidatorStakeV1) = 0,
626        // Warning: if you're adding a new version, make sure that the borsh encoding of
627        // any `ValidatorStake` cannot be equal to the borsh encoding of any `ValidatorStakeV1`.
628        // See `EpochSyncProofEpochData::use_versioned_bp_hash_format` for an explanation.
629        // The simplest way to ensure that is to make sure that any new `ValidatorStakeVx`
630        // begins with a field of type `AccountId`.
631    }
632
633    pub struct ValidatorStakeIter<'a> {
634        collection: ValidatorStakeIterSource<'a>,
635        curr_index: usize,
636        len: usize,
637    }
638
639    impl<'a> ValidatorStakeIter<'a> {
640        pub fn empty() -> Self {
641            Self { collection: ValidatorStakeIterSource::V2(&[]), curr_index: 0, len: 0 }
642        }
643
644        pub fn v1(collection: &'a [ValidatorStakeV1]) -> Self {
645            Self {
646                collection: ValidatorStakeIterSource::V1(collection),
647                curr_index: 0,
648                len: collection.len(),
649            }
650        }
651
652        pub fn new(collection: &'a [ValidatorStake]) -> Self {
653            Self {
654                collection: ValidatorStakeIterSource::V2(collection),
655                curr_index: 0,
656                len: collection.len(),
657            }
658        }
659
660        pub fn len(&self) -> usize {
661            self.len
662        }
663    }
664
665    impl<'a> Iterator for ValidatorStakeIter<'a> {
666        type Item = ValidatorStake;
667
668        fn next(&mut self) -> Option<Self::Item> {
669            if self.curr_index < self.len {
670                let item = match self.collection {
671                    ValidatorStakeIterSource::V1(collection) => {
672                        ValidatorStake::V1(collection[self.curr_index].clone())
673                    }
674                    ValidatorStakeIterSource::V2(collection) => collection[self.curr_index].clone(),
675                };
676                self.curr_index += 1;
677                Some(item)
678            } else {
679                None
680            }
681        }
682    }
683
684    enum ValidatorStakeIterSource<'a> {
685        V1(&'a [ValidatorStakeV1]),
686        V2(&'a [ValidatorStake]),
687    }
688
689    impl ValidatorStake {
690        pub fn new_v1(account_id: AccountId, public_key: PublicKey, stake: Balance) -> Self {
691            Self::V1(ValidatorStakeV1 { account_id, public_key, stake })
692        }
693
694        pub fn new(account_id: AccountId, public_key: PublicKey, stake: Balance) -> Self {
695            Self::new_v1(account_id, public_key, stake)
696        }
697
698        pub fn test(account_id: AccountId) -> Self {
699            Self::new_v1(account_id, PublicKey::empty(KeyType::ED25519), Balance::ZERO)
700        }
701
702        pub fn into_v1(self) -> ValidatorStakeV1 {
703            match self {
704                Self::V1(v1) => v1,
705            }
706        }
707
708        #[inline]
709        pub fn account_and_stake(self) -> (AccountId, Balance) {
710            match self {
711                Self::V1(v1) => (v1.account_id, v1.stake),
712            }
713        }
714
715        #[inline]
716        pub fn destructure(self) -> (AccountId, PublicKey, Balance) {
717            match self {
718                Self::V1(v1) => (v1.account_id, v1.public_key, v1.stake),
719            }
720        }
721
722        #[inline]
723        pub fn take_account_id(self) -> AccountId {
724            match self {
725                Self::V1(v1) => v1.account_id,
726            }
727        }
728
729        #[inline]
730        pub fn account_id(&self) -> &AccountId {
731            match self {
732                Self::V1(v1) => &v1.account_id,
733            }
734        }
735
736        #[inline]
737        pub fn take_public_key(self) -> PublicKey {
738            match self {
739                Self::V1(v1) => v1.public_key,
740            }
741        }
742
743        #[inline]
744        pub fn public_key(&self) -> &PublicKey {
745            match self {
746                Self::V1(v1) => &v1.public_key,
747            }
748        }
749
750        #[inline]
751        pub fn stake(&self) -> Balance {
752            match self {
753                Self::V1(v1) => v1.stake,
754            }
755        }
756
757        #[inline]
758        pub fn stake_mut(&mut self) -> &mut Balance {
759            match self {
760                Self::V1(v1) => &mut v1.stake,
761            }
762        }
763
764        pub fn get_approval_stake(&self, is_next_epoch: bool) -> ApprovalStake {
765            ApprovalStake {
766                account_id: self.account_id().clone(),
767                public_key: self.public_key().clone(),
768                stake_this_epoch: if is_next_epoch { Balance::ZERO } else { self.stake() },
769                stake_next_epoch: if is_next_epoch { self.stake() } else { Balance::ZERO },
770            }
771        }
772
773        /// Returns the validator's number of mandates (rounded down) at `stake_per_seat`.
774        ///
775        /// It returns `u16` since it allows infallible conversion to `usize` and with [`u16::MAX`]
776        /// equalling 65_535 it should be sufficient to hold the number of mandates per validator.
777        ///
778        /// # Why `u16` should be sufficient
779        ///
780        /// As of October 2023, a [recommended lower bound] for the stake required per mandate is
781        /// 25k $NEAR. At this price, the validator with highest stake would have 1_888 mandates,
782        /// which is well below `u16::MAX`.
783        ///
784        /// From another point of view, with more than `u16::MAX` mandates for validators, sampling
785        /// mandates might become computationally too expensive. This might trigger an increase in
786        /// the required stake per mandate, bringing down the number of mandates per validator.
787        ///
788        /// [recommended lower bound]: https://near.zulipchat.com/#narrow/stream/407237-pagoda.2Fcore.2Fstateless-validation/topic/validator.20seat.20assignment/near/393792901
789        ///
790        /// # Panics
791        ///
792        /// Panics if the number of mandates overflows `u16`.
793        pub fn num_mandates(&self, stake_per_mandate: Balance) -> u16 {
794            u16::try_from(self.stake().as_yoctonear() / stake_per_mandate.as_yoctonear())
795                .expect("number of mandates should fit u16")
796        }
797
798        /// Returns the weight attributed to the validator's partial mandate.
799        ///
800        /// A validator has a partial mandate if its stake cannot be divided evenly by
801        /// `stake_per_mandate`. The remainder of that division is the weight of the partial
802        /// mandate.
803        ///
804        /// Due to this definition a validator has exactly one partial mandate with `0 <= weight <
805        /// stake_per_mandate`.
806        ///
807        /// # Example
808        ///
809        /// Let `V` be a validator with stake of 12. If `stake_per_mandate` equals 5 then the weight
810        /// of `V`'s partial mandate is `12 % 5 = 2`.
811        pub fn partial_mandate_weight(&self, stake_per_mandate: Balance) -> Balance {
812            Balance::from_yoctonear(self.stake().as_yoctonear() % stake_per_mandate.as_yoctonear())
813        }
814    }
815}
816
817/// Stores validator and its stake.
818#[derive(
819    BorshSerialize, BorshDeserialize, serde::Serialize, Debug, Clone, PartialEq, Eq, ProtocolSchema,
820)]
821pub struct ValidatorStakeV1 {
822    /// Account that stakes money.
823    pub account_id: AccountId,
824    /// Public key of the proposed validator.
825    pub public_key: PublicKey,
826    /// Stake / weight of the validator.
827    pub stake: Balance,
828}
829
830pub mod chunk_extra {
831    use crate::bandwidth_scheduler::BandwidthRequests;
832    use crate::congestion_info::CongestionInfo;
833    use crate::types::StateRoot;
834    use crate::types::validator_stake::{ValidatorStake, ValidatorStakeIter};
835    use borsh::{BorshDeserialize, BorshSerialize};
836    use near_primitives_core::hash::CryptoHash;
837    use near_primitives_core::types::{Balance, Gas};
838
839    pub use super::ChunkExtraV1;
840
841    /// Information after chunk was processed, used to produce or check next chunk.
842    #[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq, serde::Serialize)]
843    #[borsh(use_discriminant = true)]
844    #[repr(u8)]
845    pub enum ChunkExtra {
846        V1(ChunkExtraV1) = 0,
847        V2(ChunkExtraV2) = 1,
848        V3(ChunkExtraV3) = 2,
849        V4(ChunkExtraV4) = 3,
850    }
851
852    #[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq, serde::Serialize)]
853    pub struct ChunkExtraV2 {
854        /// Post state root after applying give chunk.
855        pub state_root: StateRoot,
856        /// Root of merklizing results of receipts (transactions) execution.
857        pub outcome_root: CryptoHash,
858        /// Validator proposals produced by given chunk.
859        pub validator_proposals: Vec<ValidatorStake>,
860        /// Actually how much gas were used.
861        pub gas_used: Gas,
862        /// Gas limit, allows to increase or decrease limit based on expected time vs real time for computing the chunk.
863        pub gas_limit: Gas,
864        /// Total balance burnt after processing the current chunk.
865        pub balance_burnt: Balance,
866    }
867
868    /// V2 -> V3: add congestion info fields.
869    #[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq, serde::Serialize)]
870    pub struct ChunkExtraV3 {
871        /// Post state root after applying give chunk.
872        pub state_root: StateRoot,
873        /// Root of merklizing results of receipts (transactions) execution.
874        pub outcome_root: CryptoHash,
875        /// Validator proposals produced by given chunk.
876        pub validator_proposals: Vec<ValidatorStake>,
877        /// Actually how much gas were used.
878        pub gas_used: Gas,
879        /// Gas limit, allows to increase or decrease limit based on expected time vs real time for computing the chunk.
880        pub gas_limit: Gas,
881        /// Total balance burnt after processing the current chunk.
882        pub balance_burnt: Balance,
883        /// Congestion info about this shard after the chunk was applied.
884        congestion_info: CongestionInfo,
885    }
886
887    /// V3 -> V4: add bandwidth requests field.
888    #[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq, serde::Serialize)]
889    pub struct ChunkExtraV4 {
890        /// Post state root after applying give chunk.
891        pub state_root: StateRoot,
892        /// Root of merklizing results of receipts (transactions) execution.
893        pub outcome_root: CryptoHash,
894        /// Validator proposals produced by given chunk.
895        pub validator_proposals: Vec<ValidatorStake>,
896        /// Actually how much gas were used.
897        pub gas_used: Gas,
898        /// Gas limit, allows to increase or decrease limit based on expected time vs real time for computing the chunk.
899        pub gas_limit: Gas,
900        /// Total balance burnt after processing the current chunk.
901        pub balance_burnt: Balance,
902        /// Congestion info about this shard after the chunk was applied.
903        congestion_info: CongestionInfo,
904        /// Requests for bandwidth to send receipts to other shards.
905        pub bandwidth_requests: BandwidthRequests,
906    }
907
908    impl ChunkExtra {
909        /// This method creates a slimmed down and invalid ChunkExtra. It's used
910        /// for resharding where we only need the state root. This should not be
911        /// used as part of regular processing.
912        pub fn new_with_only_state_root(state_root: &StateRoot) -> Self {
913            // TODO(congestion_control) - integration with resharding
914            let congestion_control = Some(CongestionInfo::default());
915            Self::new(
916                state_root,
917                CryptoHash::default(),
918                vec![],
919                Gas::ZERO,
920                Gas::ZERO,
921                Balance::ZERO,
922                congestion_control,
923                BandwidthRequests::empty(),
924            )
925        }
926
927        pub fn new(
928            state_root: &StateRoot,
929            outcome_root: CryptoHash,
930            validator_proposals: Vec<ValidatorStake>,
931            gas_used: Gas,
932            gas_limit: Gas,
933            balance_burnt: Balance,
934            congestion_info: Option<CongestionInfo>,
935            bandwidth_requests: BandwidthRequests,
936        ) -> Self {
937            Self::V4(ChunkExtraV4 {
938                state_root: *state_root,
939                outcome_root,
940                validator_proposals,
941                gas_used,
942                gas_limit,
943                balance_burnt,
944                congestion_info: congestion_info.unwrap(),
945                bandwidth_requests,
946            })
947        }
948
949        #[inline]
950        pub fn outcome_root(&self) -> &StateRoot {
951            match self {
952                Self::V1(v1) => &v1.outcome_root,
953                Self::V2(v2) => &v2.outcome_root,
954                Self::V3(v3) => &v3.outcome_root,
955                Self::V4(v4) => &v4.outcome_root,
956            }
957        }
958
959        #[inline]
960        pub fn state_root(&self) -> &StateRoot {
961            match self {
962                Self::V1(v1) => &v1.state_root,
963                Self::V2(v2) => &v2.state_root,
964                Self::V3(v3) => &v3.state_root,
965                Self::V4(v4) => &v4.state_root,
966            }
967        }
968
969        #[inline]
970        pub fn state_root_mut(&mut self) -> &mut StateRoot {
971            match self {
972                Self::V1(v1) => &mut v1.state_root,
973                Self::V2(v2) => &mut v2.state_root,
974                Self::V3(v3) => &mut v3.state_root,
975                Self::V4(v4) => &mut v4.state_root,
976            }
977        }
978
979        #[inline]
980        pub fn validator_proposals(&self) -> ValidatorStakeIter {
981            match self {
982                Self::V1(v1) => ValidatorStakeIter::v1(&v1.validator_proposals),
983                Self::V2(v2) => ValidatorStakeIter::new(&v2.validator_proposals),
984                Self::V3(v3) => ValidatorStakeIter::new(&v3.validator_proposals),
985                Self::V4(v4) => ValidatorStakeIter::new(&v4.validator_proposals),
986            }
987        }
988
989        #[inline]
990        pub fn gas_limit(&self) -> Gas {
991            match self {
992                Self::V1(v1) => v1.gas_limit,
993                Self::V2(v2) => v2.gas_limit,
994                Self::V3(v3) => v3.gas_limit,
995                Self::V4(v4) => v4.gas_limit,
996            }
997        }
998
999        #[inline]
1000        pub fn gas_used(&self) -> Gas {
1001            match self {
1002                Self::V1(v1) => v1.gas_used,
1003                Self::V2(v2) => v2.gas_used,
1004                Self::V3(v3) => v3.gas_used,
1005                Self::V4(v4) => v4.gas_used,
1006            }
1007        }
1008
1009        #[inline]
1010        pub fn balance_burnt(&self) -> Balance {
1011            match self {
1012                Self::V1(v1) => v1.balance_burnt,
1013                Self::V2(v2) => v2.balance_burnt,
1014                Self::V3(v3) => v3.balance_burnt,
1015                Self::V4(v4) => v4.balance_burnt,
1016            }
1017        }
1018
1019        #[inline]
1020        pub fn congestion_info(&self) -> CongestionInfo {
1021            match self {
1022                Self::V1(_) | Self::V2(_) => {
1023                    debug_assert!(false, "Calling congestion_info on V1 or V2 header version");
1024                    Default::default()
1025                }
1026                Self::V3(v3) => v3.congestion_info,
1027                Self::V4(v4) => v4.congestion_info,
1028            }
1029        }
1030
1031        #[inline]
1032        pub fn congestion_info_mut(&mut self) -> &mut CongestionInfo {
1033            match self {
1034                Self::V1(_) | Self::V2(_) => panic!("Calling congestion_info_mut on V1 or V2"),
1035                Self::V3(v3) => &mut v3.congestion_info,
1036                Self::V4(v4) => &mut v4.congestion_info,
1037            }
1038        }
1039
1040        #[inline]
1041        pub fn bandwidth_requests(&self) -> Option<&BandwidthRequests> {
1042            match self {
1043                Self::V1(_) | Self::V2(_) | Self::V3(_) => None,
1044                Self::V4(extra) => Some(&extra.bandwidth_requests),
1045            }
1046        }
1047    }
1048}
1049
1050/// Information after chunk was processed, used to produce or check next chunk.
1051#[derive(
1052    Debug, PartialEq, BorshSerialize, BorshDeserialize, Clone, Eq, ProtocolSchema, serde::Serialize,
1053)]
1054pub struct ChunkExtraV1 {
1055    /// Post state root after applying give chunk.
1056    pub state_root: StateRoot,
1057    /// Root of merklizing results of receipts (transactions) execution.
1058    pub outcome_root: CryptoHash,
1059    /// Validator proposals produced by given chunk.
1060    pub validator_proposals: Vec<ValidatorStakeV1>,
1061    /// Actually how much gas were used.
1062    pub gas_used: Gas,
1063    /// Gas limit, allows to increase or decrease limit based on expected time vs real time for computing the chunk.
1064    pub gas_limit: Gas,
1065    /// Total balance burnt after processing the current chunk.
1066    pub balance_burnt: Balance,
1067}
1068
1069#[derive(
1070    Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, arbitrary::Arbitrary,
1071)]
1072#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1073#[serde(untagged)]
1074pub enum BlockId {
1075    #[cfg_attr(feature = "schemars", schemars(title = "block_height"))]
1076    Height(BlockHeight),
1077    Hash(CryptoHash),
1078}
1079
1080pub type MaybeBlockId = Option<BlockId>;
1081
1082#[derive(
1083    Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, arbitrary::Arbitrary,
1084)]
1085#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1086#[serde(rename_all = "snake_case")]
1087pub enum SyncCheckpoint {
1088    Genesis,
1089    EarliestAvailable,
1090}
1091
1092#[derive(
1093    Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize, arbitrary::Arbitrary,
1094)]
1095#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1096#[serde(rename_all = "snake_case")]
1097pub enum BlockReference {
1098    BlockId(BlockId),
1099    Finality(Finality),
1100    SyncCheckpoint(SyncCheckpoint),
1101}
1102
1103impl BlockReference {
1104    pub fn latest() -> Self {
1105        Self::Finality(Finality::None)
1106    }
1107}
1108
1109impl From<BlockId> for BlockReference {
1110    fn from(block_id: BlockId) -> Self {
1111        Self::BlockId(block_id)
1112    }
1113}
1114
1115impl From<Finality> for BlockReference {
1116    fn from(finality: Finality) -> Self {
1117        Self::Finality(finality)
1118    }
1119}
1120
1121#[derive(
1122    Default,
1123    BorshSerialize,
1124    BorshDeserialize,
1125    Clone,
1126    Debug,
1127    PartialEq,
1128    Eq,
1129    ProtocolSchema,
1130    serde::Serialize,
1131)]
1132pub struct ValidatorStats {
1133    pub produced: NumBlocks,
1134    pub expected: NumBlocks,
1135}
1136
1137impl ValidatorStats {
1138    /// Compare stats with threshold which is an expected percentage from 0 to
1139    /// 100.
1140    pub fn less_than(&self, threshold: u8) -> bool {
1141        self.produced * 100 < u64::from(threshold) * self.expected
1142    }
1143}
1144
1145#[derive(Debug, BorshSerialize, BorshDeserialize, PartialEq, Eq, ProtocolSchema)]
1146pub struct BlockChunkValidatorStats {
1147    pub block_stats: ValidatorStats,
1148    pub chunk_stats: ChunkStats,
1149}
1150
1151#[derive(serde::Deserialize, Debug, arbitrary::Arbitrary, PartialEq, Eq)]
1152#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1153#[serde(rename_all = "snake_case")]
1154pub enum EpochReference {
1155    EpochId(EpochId),
1156    BlockId(BlockId),
1157    Latest,
1158}
1159
1160impl serde::Serialize for EpochReference {
1161    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
1162    where
1163        S: serde::Serializer,
1164    {
1165        // cspell:words newtype
1166        match self {
1167            EpochReference::EpochId(epoch_id) => {
1168                s.serialize_newtype_variant("EpochReference", 0, "epoch_id", epoch_id)
1169            }
1170            EpochReference::BlockId(block_id) => {
1171                s.serialize_newtype_variant("EpochReference", 1, "block_id", block_id)
1172            }
1173            EpochReference::Latest => {
1174                s.serialize_newtype_variant("EpochReference", 2, "latest", &())
1175            }
1176        }
1177    }
1178}
1179
1180/// Either an epoch id or latest block hash.  When `EpochId` variant is used it
1181/// must be an identifier of a past epoch.  When `BlockHeight` is used it must
1182/// be hash of the latest block in the current epoch.  Using current epoch id
1183/// with `EpochId` or arbitrary block hash in past or present epochs will result
1184/// in errors.
1185#[derive(Clone, Debug)]
1186pub enum ValidatorInfoIdentifier {
1187    EpochId(EpochId),
1188    BlockHash(CryptoHash),
1189}
1190
1191/// Reasons for removing a validator from the validator set.
1192#[derive(
1193    BorshSerialize,
1194    BorshDeserialize,
1195    serde::Serialize,
1196    serde::Deserialize,
1197    Clone,
1198    Debug,
1199    PartialEq,
1200    Eq,
1201    ProtocolSchema,
1202)]
1203#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1204#[borsh(use_discriminant = true)]
1205#[repr(u8)]
1206pub enum ValidatorKickoutReason {
1207    /// Deprecated
1208    _UnusedSlashed = 0,
1209    /// Validator didn't produce enough blocks.
1210    NotEnoughBlocks { produced: NumBlocks, expected: NumBlocks } = 1,
1211    /// Validator didn't produce enough chunks.
1212    NotEnoughChunks { produced: NumBlocks, expected: NumBlocks } = 2,
1213    /// Validator unstaked themselves.
1214    Unstaked = 3,
1215    /// Validator stake is now below threshold
1216    NotEnoughStake {
1217        #[serde(rename = "stake_u128")]
1218        stake: Balance,
1219        #[serde(rename = "threshold_u128")]
1220        threshold: Balance,
1221    } = 4,
1222    /// Enough stake but is not chosen because of seat limits.
1223    DidNotGetASeat = 5,
1224    /// Validator didn't produce enough chunk endorsements.
1225    NotEnoughChunkEndorsements { produced: NumBlocks, expected: NumBlocks } = 6,
1226    /// Validator's last block proposal was for a protocol version older than
1227    /// the network's voted protocol version.
1228    ProtocolVersionTooOld { version: ProtocolVersion, network_version: ProtocolVersion } = 7,
1229}
1230
1231#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
1232#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1233#[serde(tag = "type", rename_all = "snake_case")]
1234pub enum TransactionOrReceiptId {
1235    Transaction { transaction_hash: CryptoHash, sender_id: AccountId },
1236    Receipt { receipt_id: CryptoHash, receiver_id: AccountId },
1237}
1238
1239/// Provides information about current epoch validators.
1240/// Used to break dependency between epoch manager and runtime.
1241pub trait EpochInfoProvider: Send + Sync {
1242    /// Get current stake of a validator in the given epoch.
1243    /// If the account is not a validator, returns `None`.
1244    fn validator_stake(
1245        &self,
1246        epoch_id: &EpochId,
1247        account_id: &AccountId,
1248    ) -> Result<Option<Balance>, EpochError>;
1249
1250    /// Get the total stake of the given epoch.
1251    fn validator_total_stake(&self, epoch_id: &EpochId) -> Result<Balance, EpochError>;
1252
1253    fn minimum_stake(&self, prev_block_hash: &CryptoHash) -> Result<Balance, EpochError>;
1254
1255    /// Get the chain_id of the chain this epoch belongs to
1256    fn chain_id(&self) -> String;
1257
1258    fn shard_layout(&self, epoch_id: &EpochId) -> Result<ShardLayout, EpochError>;
1259}
1260
1261/// State changes for a range of blocks.
1262/// Expects that a block is present at most once in the list.
1263#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)]
1264pub struct StateChangesForBlockRange {
1265    pub blocks: Vec<StateChangesForBlock>,
1266}
1267
1268/// State changes for a single block.
1269/// Expects that a shard is present at most once in the list of state changes.
1270#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)]
1271pub struct StateChangesForBlock {
1272    pub block_hash: CryptoHash,
1273    pub state_changes: Vec<StateChangesForShard>,
1274}
1275
1276/// Key and value of a StateChanges column.
1277#[derive(borsh::BorshDeserialize, borsh::BorshSerialize)]
1278pub struct StateChangesForShard {
1279    pub shard_id: ShardId,
1280    pub state_changes: Vec<RawStateChangesWithTrieKey>,
1281}
1282
1283/// In spice missing chunks and equivalent to empty chunks so block hash and shard id always
1284/// uniquely identifies chunks.
1285#[derive(Debug, Clone, Hash, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
1286pub struct SpiceChunkId {
1287    pub block_hash: CryptoHash,
1288    pub shard_id: ShardId,
1289}
1290
1291/// Chunk application result.
1292#[derive(Debug, Clone, PartialEq, Eq, BorshSerialize, BorshDeserialize, ProtocolSchema)]
1293pub struct ChunkExecutionResult {
1294    pub chunk_extra: ChunkExtra,
1295    pub outgoing_receipts_root: CryptoHash,
1296}
1297
1298/// Execution results for all shards in the block.
1299pub struct BlockExecutionResults(pub HashMap<ShardId, Arc<ChunkExecutionResult>>);
1300
1301#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq, Hash)]
1302pub struct ChunkExecutionResultHash(pub CryptoHash);
1303
1304impl ChunkExecutionResult {
1305    pub fn compute_hash(&self) -> ChunkExecutionResultHash {
1306        let bytes = borsh::to_vec(self).expect("Failed to serialize");
1307        ChunkExecutionResultHash(hash(&bytes))
1308    }
1309}
1310
1311#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)]
1312pub struct SpiceUncertifiedChunkInfo {
1313    pub chunk_id: SpiceChunkId,
1314    pub missing_endorsements: Vec<AccountId>,
1315    pub present_endorsements: Vec<(AccountId, SpiceStoredVerifiedEndorsement)>,
1316}
1317
1318#[cfg(test)]
1319mod tests {
1320    use near_crypto::{KeyType, PublicKey};
1321    use near_primitives_core::types::Balance;
1322
1323    use super::validator_stake::ValidatorStake;
1324
1325    fn new_validator_stake(stake: Balance) -> ValidatorStake {
1326        ValidatorStake::new(
1327            "test_account".parse().unwrap(),
1328            PublicKey::empty(KeyType::ED25519),
1329            stake,
1330        )
1331    }
1332
1333    #[test]
1334    fn test_validator_stake_num_mandates() {
1335        assert_eq!(new_validator_stake(Balance::ZERO).num_mandates(Balance::from_yoctonear(5)), 0);
1336        assert_eq!(
1337            new_validator_stake(Balance::from_yoctonear(10))
1338                .num_mandates(Balance::from_yoctonear(5)),
1339            2
1340        );
1341        assert_eq!(
1342            new_validator_stake(Balance::from_yoctonear(12))
1343                .num_mandates(Balance::from_yoctonear(5)),
1344            2
1345        );
1346    }
1347
1348    #[test]
1349    fn test_validator_partial_mandate_weight() {
1350        assert_eq!(
1351            new_validator_stake(Balance::ZERO).partial_mandate_weight(Balance::from_yoctonear(5)),
1352            Balance::ZERO
1353        );
1354        assert_eq!(
1355            new_validator_stake(Balance::from_yoctonear(10))
1356                .partial_mandate_weight(Balance::from_yoctonear(5)),
1357            Balance::ZERO
1358        );
1359        assert_eq!(
1360            new_validator_stake(Balance::from_yoctonear(12))
1361                .partial_mandate_weight(Balance::from_yoctonear(5)),
1362            Balance::from_yoctonear(2)
1363        );
1364    }
1365}