Skip to main content

near_primitives/
sharding.rs

1use crate::bandwidth_scheduler::BandwidthRequests;
2use crate::congestion_info::CongestionInfo;
3use crate::hash::{CryptoHash, hash};
4use crate::merkle::{MerklePath, combine_hash, merklize, verify_path};
5use crate::receipt::Receipt;
6use crate::transaction::SignedTransaction;
7#[cfg(feature = "solomon")]
8use crate::transaction::ValidatedTransaction;
9use crate::types::validator_stake::{ValidatorStake, ValidatorStakeIter, ValidatorStakeV1};
10use crate::types::{Balance, BlockHeight, Gas, MerkleHash, ShardId, StateRoot};
11use crate::validator_signer::{EmptyValidatorSigner, ValidatorSigner};
12use crate::version::ProtocolVersion;
13use borsh::{BorshDeserialize, BorshSerialize};
14use near_crypto::Signature;
15use near_fmt::AbbrBytes;
16use near_primitives_core::version::{PROTOCOL_VERSION, ProtocolFeature};
17use near_schema_checker_lib::ProtocolSchema;
18use shard_chunk_header_inner::ShardChunkHeaderInnerV4;
19use std::cmp::Ordering;
20use std::sync::Arc;
21use tracing::debug_span;
22
23#[derive(
24    BorshSerialize,
25    BorshDeserialize,
26    Hash,
27    Eq,
28    PartialEq,
29    Ord,
30    PartialOrd,
31    Clone,
32    Debug,
33    Default,
34    serde::Serialize,
35    serde::Deserialize,
36    ProtocolSchema,
37)]
38#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
39pub struct ChunkHash(pub CryptoHash);
40
41impl ChunkHash {
42    pub fn as_bytes(&self) -> &[u8; 32] {
43        self.0.as_bytes()
44    }
45}
46
47impl AsRef<[u8]> for ChunkHash {
48    fn as_ref(&self) -> &[u8] {
49        self.0.as_ref()
50    }
51}
52
53impl From<ChunkHash> for Vec<u8> {
54    fn from(chunk_hash: ChunkHash) -> Self {
55        chunk_hash.0.into()
56    }
57}
58
59impl From<CryptoHash> for ChunkHash {
60    fn from(crypto_hash: CryptoHash) -> Self {
61        Self(crypto_hash)
62    }
63}
64
65/// This version of the type is used in the old state sync, where we sync to the state right before the new epoch
66#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
67pub struct StateSyncInfoV0 {
68    /// The "sync_hash" block referred to in the state sync algorithm. This is the first block of the
69    /// epoch we want to state sync for. This field is not strictly required since this struct is keyed
70    /// by this hash in the database, but it's a small amount of data that makes the info in this type more complete.
71    pub sync_hash: CryptoHash,
72    /// Shards to fetch state
73    pub shards: Vec<ShardId>,
74}
75
76/// This version of the type is used when syncing to the current epoch's state, and `sync_hash` is an
77/// Option because it is not known at the beginning of the epoch, but only until a few more blocks are produced.
78#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
79pub struct StateSyncInfoV1 {
80    /// The first block of the epoch we want to state sync for. This field is not strictly required since
81    /// this struct is keyed by this hash in the database, but it's a small amount of data that makes
82    /// the info in this type more complete.
83    pub epoch_first_block: CryptoHash,
84    /// The block we'll use as the "sync_hash" when state syncing. Previously, state sync
85    /// used the first block of an epoch as the "sync_hash", and synced state to the epoch before.
86    /// Now that state sync downloads the state of the current epoch, we need to wait a few blocks
87    /// after applying the first block in an epoch to know what "sync_hash" we'll use, so this field
88    /// is first set to None until we find the right "sync_hash".
89    pub sync_hash: Option<CryptoHash>,
90    /// Shards to fetch state
91    pub shards: Vec<ShardId>,
92}
93
94/// Contains the information that is used to sync state for shards as epochs switch
95/// Currently there is only one version possible, but an improvement we might want to make in the future
96/// is that when syncing to the current epoch's state, we currently wait for two new chunks in each shard, but
97/// with some changes to the meaning of the "sync_hash", we should only need to wait for one. So this is included
98/// in order to allow for this change in the future without needing another database migration.
99#[derive(Clone, Debug, PartialEq, BorshSerialize, BorshDeserialize)]
100#[borsh(use_discriminant = true)]
101#[repr(u8)]
102pub enum StateSyncInfo {
103    /// Old state sync: sync to the state right before the new epoch
104    V0(StateSyncInfoV0) = 0,
105    /// New state sync: sync to the state right after the new epoch
106    V1(StateSyncInfoV1) = 1,
107}
108
109impl StateSyncInfo {
110    pub fn new(epoch_first_block: CryptoHash, shards: Vec<ShardId>) -> Self {
111        Self::V1(StateSyncInfoV1 { epoch_first_block, sync_hash: None, shards })
112    }
113
114    /// Block hash that identifies this state sync struct on disk
115    pub fn epoch_first_block(&self) -> &CryptoHash {
116        match self {
117            Self::V0(info) => &info.sync_hash,
118            Self::V1(info) => &info.epoch_first_block,
119        }
120    }
121
122    pub fn shards(&self) -> &[ShardId] {
123        match self {
124            Self::V0(info) => &info.shards,
125            Self::V1(info) => &info.shards,
126        }
127    }
128}
129
130pub mod shard_chunk_header_inner;
131use self::shard_chunk_header_inner::ShardChunkHeaderInnerV6SpiceTxOnly;
132use crate::sharding::shard_chunk_header_inner::ShardChunkHeaderInnerV5;
133use crate::trie_split::TrieSplit;
134pub use shard_chunk_header_inner::{
135    ShardChunkHeaderInner, ShardChunkHeaderInnerV1, ShardChunkHeaderInnerV2,
136    ShardChunkHeaderInnerV3,
137};
138
139#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug, ProtocolSchema)]
140#[borsh(init=init)]
141pub struct ShardChunkHeaderV1 {
142    pub inner: ShardChunkHeaderInnerV1,
143
144    pub height_included: BlockHeight,
145
146    /// Signature of the chunk producer.
147    pub signature: Signature,
148
149    #[borsh(skip)]
150    pub hash: ChunkHash,
151}
152
153#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug, ProtocolSchema)]
154#[borsh(init=init)]
155pub struct ShardChunkHeaderV2 {
156    pub inner: ShardChunkHeaderInnerV1,
157
158    pub height_included: BlockHeight,
159
160    /// Signature of the chunk producer.
161    pub signature: Signature,
162
163    #[borsh(skip)]
164    pub hash: ChunkHash,
165}
166
167impl ShardChunkHeaderV2 {
168    pub fn new_dummy(height: BlockHeight, shard_id: ShardId, prev_block_hash: CryptoHash) -> Self {
169        Self::new(
170            prev_block_hash,
171            Default::default(),
172            Default::default(),
173            Default::default(),
174            Default::default(),
175            height,
176            shard_id,
177            Default::default(),
178            Default::default(),
179            Default::default(),
180            Default::default(),
181            Default::default(),
182            Default::default(),
183            &EmptyValidatorSigner::default().into(),
184        )
185    }
186
187    pub fn init(&mut self) {
188        self.hash = Self::compute_hash(&self.inner);
189    }
190
191    pub fn compute_hash(inner: &ShardChunkHeaderInnerV1) -> ChunkHash {
192        let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize");
193        let inner_hash = hash(&inner_bytes);
194
195        ChunkHash(combine_hash(&inner_hash, &inner.encoded_merkle_root))
196    }
197
198    pub fn new(
199        prev_block_hash: CryptoHash,
200        prev_state_root: StateRoot,
201        prev_outcome_root: CryptoHash,
202        encoded_merkle_root: CryptoHash,
203        encoded_length: u64,
204        height: BlockHeight,
205        shard_id: ShardId,
206        prev_gas_used: Gas,
207        gas_limit: Gas,
208        prev_balance_burnt: Balance,
209        prev_outgoing_receipts_root: CryptoHash,
210        tx_root: CryptoHash,
211        prev_validator_proposals: Vec<ValidatorStakeV1>,
212        signer: &ValidatorSigner,
213    ) -> Self {
214        let inner = ShardChunkHeaderInnerV1 {
215            prev_block_hash,
216            prev_state_root,
217            prev_outcome_root,
218            encoded_merkle_root,
219            encoded_length,
220            height_created: height,
221            shard_id,
222            prev_gas_used,
223            gas_limit,
224            prev_balance_burnt,
225            prev_outgoing_receipts_root,
226            tx_root,
227            prev_validator_proposals,
228        };
229        let hash = Self::compute_hash(&inner);
230        let signature = signer.sign_bytes(hash.as_ref());
231        Self { inner, height_included: 0, signature, hash }
232    }
233}
234
235// V2 -> V3: Use versioned ShardChunkHeaderInner structure
236#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug, ProtocolSchema)]
237#[borsh(init=init)]
238pub struct ShardChunkHeaderV3 {
239    pub inner: ShardChunkHeaderInner,
240
241    pub height_included: BlockHeight,
242
243    /// Signature of the chunk producer.
244    pub signature: Signature,
245
246    #[borsh(skip)]
247    pub hash: ChunkHash,
248}
249
250impl ShardChunkHeaderV3 {
251    pub fn new_dummy(height: BlockHeight, shard_id: ShardId, prev_block_hash: CryptoHash) -> Self {
252        if ProtocolFeature::Spice.enabled(PROTOCOL_VERSION) {
253            Self::new_for_spice(
254                prev_block_hash,
255                Default::default(),
256                Default::default(),
257                height,
258                shard_id,
259                Default::default(),
260                Default::default(),
261                &EmptyValidatorSigner::default().into(),
262            )
263        } else {
264            Self::new(
265                prev_block_hash,
266                Default::default(),
267                Default::default(),
268                Default::default(),
269                Default::default(),
270                height,
271                shard_id,
272                Default::default(),
273                Default::default(),
274                Default::default(),
275                Default::default(),
276                Default::default(),
277                Default::default(),
278                CongestionInfo::default(),
279                BandwidthRequests::empty(),
280                None,
281                &EmptyValidatorSigner::default().into(),
282                PROTOCOL_VERSION,
283            )
284        }
285    }
286
287    pub fn init(&mut self) {
288        self.hash = Self::compute_hash(&self.inner);
289    }
290
291    pub fn compute_hash(inner: &ShardChunkHeaderInner) -> ChunkHash {
292        let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize");
293        let inner_hash = hash(&inner_bytes);
294
295        ChunkHash(combine_hash(&inner_hash, inner.encoded_merkle_root()))
296    }
297
298    pub fn new(
299        prev_block_hash: CryptoHash,
300        prev_state_root: StateRoot,
301        prev_outcome_root: CryptoHash,
302        encoded_merkle_root: CryptoHash,
303        encoded_length: u64,
304        height_created: BlockHeight,
305        shard_id: ShardId,
306        prev_gas_used: Gas,
307        gas_limit: Gas,
308        prev_balance_burnt: Balance,
309        prev_outgoing_receipts_root: CryptoHash,
310        tx_root: CryptoHash,
311        prev_validator_proposals: Vec<ValidatorStake>,
312        congestion_info: CongestionInfo,
313        bandwidth_requests: BandwidthRequests,
314        proposed_split: Option<TrieSplit>,
315        signer: &ValidatorSigner,
316        protocol_version: ProtocolVersion,
317    ) -> Self {
318        let inner = if ProtocolFeature::DynamicResharding.enabled(protocol_version) {
319            ShardChunkHeaderInner::V5(ShardChunkHeaderInnerV5 {
320                prev_block_hash,
321                prev_state_root,
322                prev_outcome_root,
323                encoded_merkle_root,
324                encoded_length,
325                height_created,
326                shard_id,
327                prev_gas_used,
328                gas_limit,
329                prev_balance_burnt,
330                prev_outgoing_receipts_root,
331                tx_root,
332                prev_validator_proposals,
333                congestion_info,
334                bandwidth_requests,
335                proposed_split,
336            })
337        } else {
338            ShardChunkHeaderInner::V4(ShardChunkHeaderInnerV4 {
339                prev_block_hash,
340                prev_state_root,
341                prev_outcome_root,
342                encoded_merkle_root,
343                encoded_length,
344                height_created,
345                shard_id,
346                prev_gas_used,
347                gas_limit,
348                prev_balance_burnt,
349                prev_outgoing_receipts_root,
350                tx_root,
351                prev_validator_proposals,
352                congestion_info,
353                bandwidth_requests,
354            })
355        };
356        Self::from_inner(inner, signer)
357    }
358
359    pub fn new_for_spice(
360        prev_block_hash: CryptoHash,
361        encoded_merkle_root: CryptoHash,
362        encoded_length: u64,
363        height_created: BlockHeight,
364        shard_id: ShardId,
365        prev_outgoing_receipts_root: CryptoHash,
366        tx_root: CryptoHash,
367        signer: &ValidatorSigner,
368    ) -> Self {
369        let inner = ShardChunkHeaderInner::V6(ShardChunkHeaderInnerV6SpiceTxOnly {
370            prev_block_hash,
371            encoded_merkle_root,
372            encoded_length,
373            height_created,
374            shard_id,
375            tx_root,
376            prev_outgoing_receipts_root,
377        });
378        Self::from_inner(inner, signer)
379    }
380
381    pub fn from_inner(inner: ShardChunkHeaderInner, signer: &ValidatorSigner) -> Self {
382        let hash = Self::compute_hash(&inner);
383        let signature = signer.sign_bytes(hash.as_ref());
384        Self { inner, height_included: 0, signature, hash }
385    }
386}
387
388#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug, ProtocolSchema)]
389#[borsh(use_discriminant = true)]
390#[repr(u8)]
391pub enum ShardChunkHeader {
392    V1(ShardChunkHeaderV1) = 0,
393    V2(ShardChunkHeaderV2) = 1,
394    V3(ShardChunkHeaderV3) = 2,
395}
396
397impl ShardChunkHeader {
398    pub fn new_dummy(height: BlockHeight, shard_id: ShardId, prev_block_hash: CryptoHash) -> Self {
399        Self::V3(ShardChunkHeaderV3::new_dummy(height, shard_id, prev_block_hash))
400    }
401
402    #[inline]
403    pub fn take_inner(self) -> ShardChunkHeaderInner {
404        match self {
405            Self::V1(header) => ShardChunkHeaderInner::V1(header.inner),
406            Self::V2(header) => ShardChunkHeaderInner::V1(header.inner),
407            Self::V3(header) => header.inner,
408        }
409    }
410
411    pub fn inner_header_hash(&self) -> CryptoHash {
412        let inner_bytes = match self {
413            Self::V1(header) => borsh::to_vec(&header.inner),
414            Self::V2(header) => borsh::to_vec(&header.inner),
415            Self::V3(header) => borsh::to_vec(&header.inner),
416        };
417        hash(&inner_bytes.expect("Failed to serialize"))
418    }
419
420    /// Height at which the chunk was created.
421    /// TODO: this is always `height(prev_block_hash) + 1`. Consider using
422    /// `prev_block_height` instead as this is more explicit and
423    /// `height_created` also conflicts with `height_included`.
424    #[inline]
425    pub fn height_created(&self) -> BlockHeight {
426        match self {
427            Self::V1(header) => header.inner.height_created,
428            Self::V2(header) => header.inner.height_created,
429            Self::V3(header) => header.inner.height_created(),
430        }
431    }
432
433    #[inline]
434    pub fn signature(&self) -> &Signature {
435        match self {
436            Self::V1(header) => &header.signature,
437            Self::V2(header) => &header.signature,
438            Self::V3(header) => &header.signature,
439        }
440    }
441
442    #[inline]
443    pub fn height_included(&self) -> BlockHeight {
444        match self {
445            Self::V1(header) => header.height_included,
446            Self::V2(header) => header.height_included,
447            Self::V3(header) => header.height_included,
448        }
449    }
450
451    #[inline]
452    pub fn height_included_mut(&mut self) -> &mut BlockHeight {
453        match self {
454            Self::V1(header) => &mut header.height_included,
455            Self::V2(header) => &mut header.height_included,
456            Self::V3(header) => &mut header.height_included,
457        }
458    }
459
460    pub fn is_new_chunk(&self, block_height: BlockHeight) -> bool {
461        self.height_included() == block_height
462    }
463
464    #[inline]
465    pub fn prev_validator_proposals(&self) -> ValidatorStakeIter<'_> {
466        match self {
467            Self::V1(header) => ValidatorStakeIter::v1(&header.inner.prev_validator_proposals),
468            Self::V2(header) => ValidatorStakeIter::v1(&header.inner.prev_validator_proposals),
469            Self::V3(header) => header.inner.prev_validator_proposals(),
470        }
471    }
472
473    #[inline]
474    pub fn prev_state_root(&self) -> StateRoot {
475        match self {
476            Self::V1(header) => header.inner.prev_state_root,
477            Self::V2(header) => header.inner.prev_state_root,
478            Self::V3(header) => *header.inner.prev_state_root(),
479        }
480    }
481
482    #[inline]
483    pub fn prev_block_hash(&self) -> &CryptoHash {
484        match self {
485            Self::V1(header) => &header.inner.prev_block_hash,
486            Self::V2(header) => &header.inner.prev_block_hash,
487            Self::V3(header) => header.inner.prev_block_hash(),
488        }
489    }
490
491    #[inline]
492    pub fn is_genesis(&self) -> bool {
493        self.prev_block_hash() == &CryptoHash::default()
494    }
495
496    #[inline]
497    pub fn encoded_merkle_root(&self) -> &CryptoHash {
498        match self {
499            Self::V1(header) => &header.inner.encoded_merkle_root,
500            Self::V2(header) => &header.inner.encoded_merkle_root,
501            Self::V3(header) => header.inner.encoded_merkle_root(),
502        }
503    }
504
505    #[inline]
506    pub fn shard_id(&self) -> ShardId {
507        match self {
508            Self::V1(header) => header.inner.shard_id,
509            Self::V2(header) => header.inner.shard_id,
510            Self::V3(header) => header.inner.shard_id(),
511        }
512    }
513
514    #[inline]
515    pub fn encoded_length(&self) -> u64 {
516        match self {
517            Self::V1(header) => header.inner.encoded_length,
518            Self::V2(header) => header.inner.encoded_length,
519            Self::V3(header) => header.inner.encoded_length(),
520        }
521    }
522
523    #[inline]
524    pub fn prev_gas_used(&self) -> Gas {
525        match &self {
526            ShardChunkHeader::V1(header) => header.inner.prev_gas_used,
527            ShardChunkHeader::V2(header) => header.inner.prev_gas_used,
528            ShardChunkHeader::V3(header) => header.inner.prev_gas_used(),
529        }
530    }
531
532    #[inline]
533    pub fn gas_limit(&self) -> Gas {
534        match &self {
535            ShardChunkHeader::V1(header) => header.inner.gas_limit,
536            ShardChunkHeader::V2(header) => header.inner.gas_limit,
537            ShardChunkHeader::V3(header) => header.inner.gas_limit(),
538        }
539    }
540
541    #[inline]
542    pub fn prev_balance_burnt(&self) -> Balance {
543        match &self {
544            ShardChunkHeader::V1(header) => header.inner.prev_balance_burnt,
545            ShardChunkHeader::V2(header) => header.inner.prev_balance_burnt,
546            ShardChunkHeader::V3(header) => header.inner.prev_balance_burnt(),
547        }
548    }
549
550    #[inline]
551    pub fn prev_outgoing_receipts_root(&self) -> &CryptoHash {
552        match &self {
553            ShardChunkHeader::V1(header) => &header.inner.prev_outgoing_receipts_root,
554            ShardChunkHeader::V2(header) => &header.inner.prev_outgoing_receipts_root,
555            ShardChunkHeader::V3(header) => header.inner.prev_outgoing_receipts_root(),
556        }
557    }
558
559    #[inline]
560    pub fn prev_outcome_root(&self) -> &CryptoHash {
561        match &self {
562            ShardChunkHeader::V1(header) => &header.inner.prev_outcome_root,
563            ShardChunkHeader::V2(header) => &header.inner.prev_outcome_root,
564            ShardChunkHeader::V3(header) => header.inner.prev_outcome_root(),
565        }
566    }
567
568    #[inline]
569    pub fn tx_root(&self) -> &CryptoHash {
570        match &self {
571            ShardChunkHeader::V1(header) => &header.inner.tx_root,
572            ShardChunkHeader::V2(header) => &header.inner.tx_root,
573            ShardChunkHeader::V3(header) => header.inner.tx_root(),
574        }
575    }
576
577    #[inline]
578    pub fn chunk_hash(&self) -> &ChunkHash {
579        match &self {
580            ShardChunkHeader::V1(header) => &header.hash,
581            ShardChunkHeader::V2(header) => &header.hash,
582            ShardChunkHeader::V3(header) => &header.hash,
583        }
584    }
585
586    #[inline]
587    pub fn congestion_info(&self) -> CongestionInfo {
588        match self {
589            ShardChunkHeader::V1(_) | ShardChunkHeader::V2(_) => {
590                debug_assert!(false, "Calling congestion_info on V1 or V2 header version");
591                Default::default()
592            }
593            ShardChunkHeader::V3(header) => header.inner.congestion_info(),
594        }
595    }
596
597    #[inline]
598    pub fn bandwidth_requests(&self) -> Option<&BandwidthRequests> {
599        match self {
600            ShardChunkHeader::V1(_) | ShardChunkHeader::V2(_) => None,
601            ShardChunkHeader::V3(header) => header.inner.bandwidth_requests(),
602        }
603    }
604
605    /// Returns whether the header is valid for given `ProtocolVersion`.
606    pub fn validate_version(
607        &self,
608        version: ProtocolVersion,
609    ) -> Result<(), BadHeaderForProtocolVersionError> {
610        let is_valid = match &self {
611            ShardChunkHeader::V1(_) => false,
612            ShardChunkHeader::V2(_) => false,
613            ShardChunkHeader::V3(header) => match header.inner {
614                ShardChunkHeaderInner::V1(_) => false,
615                ShardChunkHeaderInner::V2(_) => false,
616                ShardChunkHeaderInner::V3(_) => false,
617                ShardChunkHeaderInner::V4(_) => true,
618                ShardChunkHeaderInner::V5(_) => ProtocolFeature::DynamicResharding.enabled(version),
619                ShardChunkHeaderInner::V6(_) => ProtocolFeature::Spice.enabled(version),
620            },
621        };
622
623        if is_valid {
624            Ok(())
625        } else {
626            Err(BadHeaderForProtocolVersionError {
627                protocol_version: version,
628                header_version: self.header_version_number(),
629                header_inner_version: self.inner_version_number(),
630            })
631        }
632    }
633
634    /// Used for error messages, use `match` for other code.
635    #[inline]
636    pub(crate) fn header_version_number(&self) -> u64 {
637        match self {
638            ShardChunkHeader::V1(_) => 1,
639            ShardChunkHeader::V2(_) => 2,
640            ShardChunkHeader::V3(_) => 3,
641        }
642    }
643
644    /// Used for error messages, use `match` for other code.
645    #[inline]
646    pub(crate) fn inner_version_number(&self) -> u64 {
647        match self {
648            ShardChunkHeader::V1(v1) => {
649                // Shows that Header V1 contains Inner V1
650                let _inner_v1: &ShardChunkHeaderInnerV1 = &v1.inner;
651                1
652            }
653            ShardChunkHeader::V2(v2) => {
654                // Shows that Header V2 also contains Inner V1, not Inner V2
655                let _inner_v1: &ShardChunkHeaderInnerV1 = &v2.inner;
656                1
657            }
658            ShardChunkHeader::V3(v3) => {
659                let inner_enum: &ShardChunkHeaderInner = &v3.inner;
660                inner_enum.version_number()
661            }
662        }
663    }
664
665    #[inline]
666    pub fn is_spice_chunk(&self) -> bool {
667        match self {
668            ShardChunkHeader::V1(_) | ShardChunkHeader::V2(_) => false,
669            ShardChunkHeader::V3(header) => header.inner.is_spice_chunk(),
670        }
671    }
672
673    pub fn compute_hash(&self) -> ChunkHash {
674        match self {
675            ShardChunkHeader::V1(header) => ShardChunkHeaderV1::compute_hash(&header.inner),
676            ShardChunkHeader::V2(header) => ShardChunkHeaderV2::compute_hash(&header.inner),
677            ShardChunkHeader::V3(header) => ShardChunkHeaderV3::compute_hash(&header.inner),
678        }
679    }
680
681    #[inline]
682    pub fn proposed_split(&self) -> Option<&TrieSplit> {
683        match self {
684            ShardChunkHeader::V1(_) | ShardChunkHeader::V2(_) => None,
685            ShardChunkHeader::V3(header) => header.inner.proposed_split(),
686        }
687    }
688}
689
690#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
691#[error(
692    "Invalid chunk header version for protocol version {protocol_version}. (header: {header_version}, inner: {header_inner_version})"
693)]
694pub struct BadHeaderForProtocolVersionError {
695    pub protocol_version: ProtocolVersion,
696    pub header_version: u64,
697    pub header_inner_version: u64,
698}
699
700#[derive(
701    BorshSerialize, BorshDeserialize, Hash, Eq, PartialEq, Clone, Debug, Default, ProtocolSchema,
702)]
703pub struct ChunkHashHeight(pub ChunkHash, pub BlockHeight);
704
705impl ShardChunkHeaderV1 {
706    pub fn new_dummy(height: BlockHeight, shard_id: ShardId, prev_block_hash: CryptoHash) -> Self {
707        Self::new(
708            prev_block_hash,
709            Default::default(),
710            Default::default(),
711            Default::default(),
712            Default::default(),
713            height,
714            shard_id,
715            Default::default(),
716            Default::default(),
717            Default::default(),
718            Default::default(),
719            Default::default(),
720            Default::default(),
721            &EmptyValidatorSigner::default().into(),
722        )
723    }
724
725    pub fn init(&mut self) {
726        self.hash = Self::compute_hash(&self.inner);
727    }
728
729    pub fn chunk_hash(&self) -> &ChunkHash {
730        &self.hash
731    }
732
733    pub fn compute_hash(inner: &ShardChunkHeaderInnerV1) -> ChunkHash {
734        let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize");
735        let inner_hash = hash(&inner_bytes);
736
737        ChunkHash(inner_hash)
738    }
739
740    pub fn new(
741        prev_block_hash: CryptoHash,
742        prev_state_root: StateRoot,
743        prev_outcome_root: CryptoHash,
744        encoded_merkle_root: CryptoHash,
745        encoded_length: u64,
746        height: BlockHeight,
747        shard_id: ShardId,
748        prev_gas_used: Gas,
749        gas_limit: Gas,
750        prev_balance_burnt: Balance,
751        prev_outgoing_receipts_root: CryptoHash,
752        tx_root: CryptoHash,
753        prev_validator_proposals: Vec<ValidatorStakeV1>,
754        signer: &ValidatorSigner,
755    ) -> Self {
756        let inner = ShardChunkHeaderInnerV1 {
757            prev_block_hash,
758            prev_state_root,
759            prev_outcome_root,
760            encoded_merkle_root,
761            encoded_length,
762            height_created: height,
763            shard_id,
764            prev_gas_used,
765            gas_limit,
766            prev_balance_burnt,
767            prev_outgoing_receipts_root,
768            tx_root,
769            prev_validator_proposals,
770        };
771        let hash = Self::compute_hash(&inner);
772        let signature = signer.sign_bytes(hash.as_ref());
773        Self { inner, height_included: 0, signature, hash }
774    }
775}
776
777#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, ProtocolSchema)]
778#[borsh(use_discriminant = true)]
779#[repr(u8)]
780pub enum PartialEncodedChunk {
781    V1(PartialEncodedChunkV1) = 0,
782    V2(PartialEncodedChunkV2) = 1,
783}
784
785impl PartialEncodedChunk {
786    pub fn new(
787        header: ShardChunkHeader,
788        parts: Vec<PartialEncodedChunkPart>,
789        prev_outgoing_receipts: Vec<ReceiptProof>,
790    ) -> Self {
791        match header {
792            ShardChunkHeader::V1(header) => {
793                Self::V1(PartialEncodedChunkV1 { header, parts, prev_outgoing_receipts })
794            }
795            header => Self::V2(PartialEncodedChunkV2 { header, parts, prev_outgoing_receipts }),
796        }
797    }
798
799    pub fn into_parts_and_receipt_proofs(
800        self,
801    ) -> (impl Iterator<Item = PartialEncodedChunkPart>, impl Iterator<Item = ReceiptProof>) {
802        match self {
803            Self::V1(PartialEncodedChunkV1 { header: _, parts, prev_outgoing_receipts }) => {
804                (parts.into_iter(), prev_outgoing_receipts.into_iter())
805            }
806            Self::V2(PartialEncodedChunkV2 { header: _, parts, prev_outgoing_receipts }) => {
807                (parts.into_iter(), prev_outgoing_receipts.into_iter())
808            }
809        }
810    }
811
812    pub fn cloned_header(&self) -> ShardChunkHeader {
813        match self {
814            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()),
815            Self::V2(chunk) => chunk.header.clone(),
816        }
817    }
818
819    pub fn chunk_hash(&self) -> &ChunkHash {
820        match self {
821            Self::V1(chunk) => &chunk.header.hash,
822            Self::V2(chunk) => chunk.header.chunk_hash(),
823        }
824    }
825
826    pub fn height_included(&self) -> BlockHeight {
827        match self {
828            Self::V1(chunk) => chunk.header.height_included,
829            Self::V2(chunk) => chunk.header.height_included(),
830        }
831    }
832
833    #[inline]
834    pub fn parts(&self) -> &[PartialEncodedChunkPart] {
835        match self {
836            Self::V1(chunk) => &chunk.parts,
837            Self::V2(chunk) => &chunk.parts,
838        }
839    }
840
841    #[inline]
842    pub fn prev_outgoing_receipts(&self) -> &[ReceiptProof] {
843        match self {
844            Self::V1(chunk) => &chunk.prev_outgoing_receipts,
845            Self::V2(chunk) => &chunk.prev_outgoing_receipts,
846        }
847    }
848
849    #[inline]
850    pub fn prev_block(&self) -> &CryptoHash {
851        match &self {
852            PartialEncodedChunk::V1(chunk) => &chunk.header.inner.prev_block_hash,
853            PartialEncodedChunk::V2(chunk) => chunk.header.prev_block_hash(),
854        }
855    }
856
857    pub fn height_created(&self) -> BlockHeight {
858        match self {
859            Self::V1(chunk) => chunk.header.inner.height_created,
860            Self::V2(chunk) => chunk.header.height_created(),
861        }
862    }
863    pub fn shard_id(&self) -> ShardId {
864        match self {
865            Self::V1(chunk) => chunk.header.inner.shard_id,
866            Self::V2(chunk) => chunk.header.shard_id(),
867        }
868    }
869
870    /// Creates a clone of this partial chunk without the parts, keeping only
871    /// the header and receipts.
872    pub fn clone_without_parts(&self) -> Self {
873        match self {
874            Self::V1(chunk) => Self::V1(PartialEncodedChunkV1 {
875                header: chunk.header.clone(),
876                parts: Vec::new(),
877                prev_outgoing_receipts: chunk.prev_outgoing_receipts.clone(),
878            }),
879            Self::V2(chunk) => Self::V2(PartialEncodedChunkV2 {
880                header: chunk.header.clone(),
881                parts: Vec::new(),
882                prev_outgoing_receipts: chunk.prev_outgoing_receipts.clone(),
883            }),
884        }
885    }
886}
887
888#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, ProtocolSchema)]
889pub struct PartialEncodedChunkV2 {
890    pub header: ShardChunkHeader,
891    pub parts: Vec<PartialEncodedChunkPart>,
892    pub prev_outgoing_receipts: Vec<ReceiptProof>,
893}
894
895impl From<PartialEncodedChunk> for PartialEncodedChunkV2 {
896    fn from(pec: PartialEncodedChunk) -> Self {
897        match pec {
898            PartialEncodedChunk::V1(chunk) => PartialEncodedChunkV2 {
899                header: ShardChunkHeader::V1(chunk.header),
900                parts: chunk.parts,
901                prev_outgoing_receipts: chunk.prev_outgoing_receipts,
902            },
903            PartialEncodedChunk::V2(chunk) => chunk,
904        }
905    }
906}
907
908#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, ProtocolSchema)]
909pub struct PartialEncodedChunkV1 {
910    pub header: ShardChunkHeaderV1,
911    pub parts: Vec<PartialEncodedChunkPart>,
912    pub prev_outgoing_receipts: Vec<ReceiptProof>,
913}
914
915#[derive(Debug, Clone, Eq, PartialEq)]
916pub struct PartialEncodedChunkWithArcReceipts {
917    pub header: ShardChunkHeader,
918    pub parts: Vec<PartialEncodedChunkPart>,
919    pub prev_outgoing_receipts: Vec<Arc<ReceiptProof>>,
920}
921
922impl From<PartialEncodedChunkWithArcReceipts> for PartialEncodedChunk {
923    fn from(pec: PartialEncodedChunkWithArcReceipts) -> Self {
924        Self::V2(PartialEncodedChunkV2 {
925            header: pec.header,
926            parts: pec.parts,
927            prev_outgoing_receipts: pec
928                .prev_outgoing_receipts
929                .into_iter()
930                .map(|r| ReceiptProof::clone(&r))
931                .collect(),
932        })
933    }
934}
935
936#[derive(
937    BorshSerialize,
938    BorshDeserialize,
939    Debug,
940    Clone,
941    Eq,
942    PartialEq,
943    serde::Deserialize,
944    ProtocolSchema,
945)]
946pub struct ShardProof {
947    pub from_shard_id: ShardId,
948    pub to_shard_id: ShardId,
949    pub proof: MerklePath,
950}
951
952#[derive(
953    BorshSerialize,
954    BorshDeserialize,
955    Debug,
956    Clone,
957    Eq,
958    PartialEq,
959    serde::Deserialize,
960    ProtocolSchema,
961)]
962/// For each Merkle proof there is a subset of receipts which may be proven.
963pub struct ReceiptProof(pub Vec<Receipt>, pub ShardProof);
964
965// Implement ordering to ensure `ReceiptProofs` are ordered consistently,
966// because we expect messages with ReceiptProofs to be deterministic.
967impl PartialOrd<Self> for ReceiptProof {
968    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
969        Some(self.cmp(other))
970    }
971}
972
973impl Ord for ReceiptProof {
974    fn cmp(&self, other: &Self) -> Ordering {
975        (self.1.from_shard_id, self.1.to_shard_id)
976            .cmp(&(other.1.from_shard_id, other.1.to_shard_id))
977    }
978}
979
980impl ReceiptProof {
981    pub fn verify_against_receipt_root(&self, receipt_root: CryptoHash) -> bool {
982        let ReceiptProof(shard_receipts, receipt_proof) = self;
983        let receipt_hash =
984            CryptoHash::hash_borsh(ReceiptList(receipt_proof.to_shard_id, shard_receipts));
985        verify_path(receipt_root, &receipt_proof.proof, &receipt_hash)
986    }
987}
988
989#[derive(BorshSerialize, BorshDeserialize, Clone, Eq, PartialEq, ProtocolSchema)]
990pub struct PartialEncodedChunkPart {
991    pub part_ord: u64,
992    pub part: Box<[u8]>,
993    pub merkle_proof: MerklePath,
994}
995
996impl std::fmt::Debug for PartialEncodedChunkPart {
997    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
998        f.debug_struct("PartialEncodedChunkPart")
999            .field("part_ord", &self.part_ord)
1000            .field("part", &format_args!("{}", AbbrBytes(self.part.as_ref())))
1001            .field("merkle_proof", &self.merkle_proof)
1002            .finish()
1003    }
1004}
1005
1006#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, ProtocolSchema)]
1007pub struct ShardChunkV1 {
1008    pub chunk_hash: ChunkHash,
1009    pub header: ShardChunkHeaderV1,
1010    pub transactions: Vec<SignedTransaction>,
1011    pub prev_outgoing_receipts: Vec<Receipt>,
1012}
1013
1014#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, ProtocolSchema)]
1015pub struct ShardChunkV2 {
1016    pub chunk_hash: ChunkHash,
1017    pub header: ShardChunkHeader,
1018    pub transactions: Vec<SignedTransaction>,
1019    pub prev_outgoing_receipts: Vec<Receipt>,
1020}
1021
1022#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, ProtocolSchema)]
1023#[borsh(use_discriminant = true)]
1024#[repr(u8)]
1025pub enum ShardChunk {
1026    V1(ShardChunkV1) = 0,
1027    V2(ShardChunkV2) = 1,
1028}
1029
1030impl ShardChunk {
1031    pub fn new(
1032        header: ShardChunkHeader,
1033        transactions: Vec<SignedTransaction>,
1034        prev_outgoing_receipts: Vec<Receipt>,
1035    ) -> Self {
1036        ShardChunk::V2(ShardChunkV2 {
1037            chunk_hash: header.chunk_hash().clone(),
1038            header,
1039            transactions,
1040            prev_outgoing_receipts,
1041        })
1042    }
1043
1044    pub fn with_header(chunk: ShardChunk, header: ShardChunkHeader) -> Option<ShardChunk> {
1045        match chunk {
1046            Self::V1(chunk) => match header {
1047                ShardChunkHeader::V1(header) => Some(ShardChunk::V1(ShardChunkV1 {
1048                    chunk_hash: header.chunk_hash().clone(),
1049                    header,
1050                    transactions: chunk.transactions,
1051                    prev_outgoing_receipts: chunk.prev_outgoing_receipts,
1052                })),
1053                ShardChunkHeader::V2(_) => None,
1054                ShardChunkHeader::V3(_) => None,
1055            },
1056            Self::V2(chunk) => Some(ShardChunk::V2(ShardChunkV2 {
1057                chunk_hash: header.chunk_hash().clone(),
1058                header,
1059                transactions: chunk.transactions,
1060                prev_outgoing_receipts: chunk.prev_outgoing_receipts,
1061            })),
1062        }
1063    }
1064
1065    pub fn set_height_included(&mut self, height: BlockHeight) {
1066        match self {
1067            Self::V1(chunk) => chunk.header.height_included = height,
1068            Self::V2(chunk) => *chunk.header.height_included_mut() = height,
1069        }
1070    }
1071
1072    #[inline]
1073    pub fn height_included(&self) -> BlockHeight {
1074        match self {
1075            Self::V1(chunk) => chunk.header.height_included,
1076            Self::V2(chunk) => chunk.header.height_included(),
1077        }
1078    }
1079
1080    #[inline]
1081    pub fn height_created(&self) -> BlockHeight {
1082        match self {
1083            Self::V1(chunk) => chunk.header.inner.height_created,
1084            Self::V2(chunk) => chunk.header.height_created(),
1085        }
1086    }
1087
1088    #[inline]
1089    pub fn prev_block(&self) -> &CryptoHash {
1090        match &self {
1091            ShardChunk::V1(chunk) => &chunk.header.inner.prev_block_hash,
1092            ShardChunk::V2(chunk) => chunk.header.prev_block_hash(),
1093        }
1094    }
1095
1096    #[inline]
1097    pub fn prev_state_root(&self) -> StateRoot {
1098        match self {
1099            Self::V1(chunk) => chunk.header.inner.prev_state_root,
1100            Self::V2(chunk) => chunk.header.prev_state_root(),
1101        }
1102    }
1103
1104    #[inline]
1105    pub fn tx_root(&self) -> &CryptoHash {
1106        match self {
1107            Self::V1(chunk) => &chunk.header.inner.tx_root,
1108            Self::V2(chunk) => chunk.header.tx_root(),
1109        }
1110    }
1111
1112    #[inline]
1113    pub fn prev_outgoing_receipts_root(&self) -> &CryptoHash {
1114        match self {
1115            Self::V1(chunk) => &chunk.header.inner.prev_outgoing_receipts_root,
1116            Self::V2(chunk) => chunk.header.prev_outgoing_receipts_root(),
1117        }
1118    }
1119
1120    #[inline]
1121    pub fn shard_id(&self) -> ShardId {
1122        match self {
1123            Self::V1(chunk) => chunk.header.inner.shard_id,
1124            Self::V2(chunk) => chunk.header.shard_id(),
1125        }
1126    }
1127
1128    #[inline]
1129    pub fn chunk_hash(&self) -> &ChunkHash {
1130        match self {
1131            Self::V1(chunk) => &chunk.chunk_hash,
1132            Self::V2(chunk) => &chunk.chunk_hash,
1133        }
1134    }
1135
1136    #[inline]
1137    pub fn prev_outgoing_receipts(&self) -> &[Receipt] {
1138        match self {
1139            Self::V1(chunk) => &chunk.prev_outgoing_receipts,
1140            Self::V2(chunk) => &chunk.prev_outgoing_receipts,
1141        }
1142    }
1143
1144    #[inline]
1145    pub fn to_transactions(&self) -> &[SignedTransaction] {
1146        match self {
1147            Self::V1(chunk) => &chunk.transactions,
1148            Self::V2(chunk) => &chunk.transactions,
1149        }
1150    }
1151
1152    pub fn into_transactions(self) -> Vec<SignedTransaction> {
1153        match self {
1154            Self::V1(chunk) => chunk.transactions,
1155            Self::V2(chunk) => chunk.transactions,
1156        }
1157    }
1158
1159    #[inline]
1160    pub fn header_hash(&self) -> &ChunkHash {
1161        match self {
1162            Self::V1(chunk) => chunk.header.chunk_hash(),
1163            Self::V2(chunk) => chunk.header.chunk_hash(),
1164        }
1165    }
1166
1167    #[inline]
1168    pub fn prev_block_hash(&self) -> &CryptoHash {
1169        match self {
1170            Self::V1(chunk) => &chunk.header.inner.prev_block_hash,
1171            Self::V2(chunk) => chunk.header.prev_block_hash(),
1172        }
1173    }
1174
1175    #[inline]
1176    pub fn take_header(self) -> ShardChunkHeader {
1177        match self {
1178            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header),
1179            Self::V2(chunk) => chunk.header,
1180        }
1181    }
1182
1183    pub fn cloned_header(&self) -> ShardChunkHeader {
1184        match self {
1185            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()),
1186            Self::V2(chunk) => chunk.header.clone(),
1187        }
1188    }
1189
1190    pub fn compute_header_hash(&self) -> ChunkHash {
1191        match self {
1192            Self::V1(chunk) => ShardChunkHeaderV1::compute_hash(&chunk.header.inner),
1193            Self::V2(chunk) => chunk.header.compute_hash(),
1194        }
1195    }
1196}
1197
1198#[derive(
1199    Default, BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq, ProtocolSchema,
1200)]
1201pub struct EncodedShardChunkBody {
1202    pub parts: Vec<Option<Box<[u8]>>>,
1203}
1204
1205impl EncodedShardChunkBody {
1206    pub fn num_fetched_parts(&self) -> usize {
1207        let mut fetched_parts: usize = 0;
1208
1209        for part in &self.parts {
1210            if part.is_some() {
1211                fetched_parts += 1;
1212            }
1213        }
1214
1215        fetched_parts
1216    }
1217
1218    pub fn get_merkle_hash_and_paths(&self) -> (MerkleHash, Vec<MerklePath>) {
1219        let parts: Vec<&[u8]> =
1220            self.parts.iter().map(|x| x.as_deref().unwrap()).collect::<Vec<_>>();
1221        merklize(&parts)
1222    }
1223}
1224
1225#[derive(BorshSerialize, Debug, Clone, ProtocolSchema)]
1226pub struct ReceiptList<'a>(pub ShardId, pub &'a [Receipt]);
1227
1228#[derive(BorshSerialize, BorshDeserialize, ProtocolSchema)]
1229pub struct TransactionReceipt(pub Vec<SignedTransaction>, pub Vec<Receipt>);
1230
1231#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq, ProtocolSchema)]
1232pub struct EncodedShardChunkV1 {
1233    pub header: ShardChunkHeaderV1,
1234    pub content: EncodedShardChunkBody,
1235}
1236
1237#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq, ProtocolSchema)]
1238pub struct EncodedShardChunkV2 {
1239    pub header: ShardChunkHeader,
1240    pub content: EncodedShardChunkBody,
1241}
1242
1243#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq, ProtocolSchema)]
1244#[borsh(use_discriminant = true)]
1245#[repr(u8)]
1246pub enum EncodedShardChunk {
1247    V1(EncodedShardChunkV1) = 0,
1248    V2(EncodedShardChunkV2) = 1,
1249}
1250
1251impl EncodedShardChunk {
1252    pub fn cloned_header(&self) -> ShardChunkHeader {
1253        match self {
1254            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()),
1255            Self::V2(chunk) => chunk.header.clone(),
1256        }
1257    }
1258
1259    #[inline]
1260    pub fn content(&self) -> &EncodedShardChunkBody {
1261        match self {
1262            Self::V1(chunk) => &chunk.content,
1263            Self::V2(chunk) => &chunk.content,
1264        }
1265    }
1266
1267    #[inline]
1268    pub fn content_mut(&mut self) -> &mut EncodedShardChunkBody {
1269        match self {
1270            Self::V1(chunk) => &mut chunk.content,
1271            Self::V2(chunk) => &mut chunk.content,
1272        }
1273    }
1274
1275    #[inline]
1276    pub fn shard_id(&self) -> ShardId {
1277        match self {
1278            Self::V1(chunk) => chunk.header.inner.shard_id,
1279            Self::V2(chunk) => chunk.header.shard_id(),
1280        }
1281    }
1282
1283    #[inline]
1284    pub fn encoded_merkle_root(&self) -> &CryptoHash {
1285        match self {
1286            Self::V1(chunk) => &chunk.header.inner.encoded_merkle_root,
1287            Self::V2(chunk) => chunk.header.encoded_merkle_root(),
1288        }
1289    }
1290
1291    #[inline]
1292    pub fn encoded_length(&self) -> u64 {
1293        match self {
1294            Self::V1(chunk) => chunk.header.inner.encoded_length,
1295            Self::V2(chunk) => chunk.header.encoded_length(),
1296        }
1297    }
1298
1299    pub fn from_header(header: ShardChunkHeader, total_parts: usize) -> Self {
1300        let chunk = EncodedShardChunkV2 {
1301            header,
1302            content: EncodedShardChunkBody { parts: vec![None; total_parts] },
1303        };
1304        Self::V2(chunk)
1305    }
1306
1307    fn decode_transaction_receipts(
1308        parts: &[Option<Box<[u8]>>],
1309        encoded_length: u64,
1310    ) -> Result<TransactionReceipt, std::io::Error> {
1311        let encoded_data = parts
1312            .iter()
1313            .flat_map(|option| option.as_ref().expect("Missing shard").iter())
1314            .cloned()
1315            .take(encoded_length as usize)
1316            .collect::<Vec<u8>>();
1317
1318        TransactionReceipt::try_from_slice(&encoded_data)
1319    }
1320
1321    pub fn chunk_hash(&self) -> &ChunkHash {
1322        match self {
1323            Self::V1(chunk) => chunk.header.chunk_hash(),
1324            Self::V2(chunk) => chunk.header.chunk_hash(),
1325        }
1326    }
1327
1328    fn part_ords_to_parts(
1329        &self,
1330        part_ords: Vec<u64>,
1331        merkle_paths: &[MerklePath],
1332    ) -> Vec<PartialEncodedChunkPart> {
1333        let parts = match self {
1334            Self::V1(chunk) => &chunk.content.parts,
1335            Self::V2(chunk) => &chunk.content.parts,
1336        };
1337        part_ords
1338            .into_iter()
1339            .map(|part_ord| PartialEncodedChunkPart {
1340                part_ord,
1341                part: parts[part_ord as usize].clone().unwrap(),
1342                merkle_proof: merkle_paths[part_ord as usize].clone(),
1343            })
1344            .collect()
1345    }
1346
1347    pub fn create_partial_encoded_chunk(
1348        &self,
1349        part_ords: Vec<u64>,
1350        prev_outgoing_receipts: Vec<ReceiptProof>,
1351        merkle_paths: &[MerklePath],
1352    ) -> PartialEncodedChunk {
1353        let parts = self.part_ords_to_parts(part_ords, merkle_paths);
1354        match self {
1355            Self::V1(chunk) => {
1356                let chunk = PartialEncodedChunkV1 {
1357                    header: chunk.header.clone(),
1358                    parts,
1359                    prev_outgoing_receipts,
1360                };
1361                PartialEncodedChunk::V1(chunk)
1362            }
1363            Self::V2(chunk) => {
1364                let chunk = PartialEncodedChunkV2 {
1365                    header: chunk.header.clone(),
1366                    parts,
1367                    prev_outgoing_receipts,
1368                };
1369                PartialEncodedChunk::V2(chunk)
1370            }
1371        }
1372    }
1373
1374    pub fn create_partial_encoded_chunk_with_arc_receipts(
1375        &self,
1376        part_ords: Vec<u64>,
1377        prev_outgoing_receipts: Vec<Arc<ReceiptProof>>,
1378        merkle_paths: &[MerklePath],
1379    ) -> PartialEncodedChunkWithArcReceipts {
1380        let parts = self.part_ords_to_parts(part_ords, merkle_paths);
1381        let header = match self {
1382            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()),
1383            Self::V2(chunk) => chunk.header.clone(),
1384        };
1385        PartialEncodedChunkWithArcReceipts { header, parts, prev_outgoing_receipts }
1386    }
1387
1388    pub fn decode_chunk(&self) -> Result<ShardChunk, std::io::Error> {
1389        let _span = debug_span!(
1390            target: "sharding",
1391            "decode_chunk",
1392            height_included = self.cloned_header().height_included(),
1393            shard_id = %self.cloned_header().shard_id(),
1394            chunk_hash = ?self.chunk_hash())
1395        .entered();
1396
1397        let transaction_receipts =
1398            Self::decode_transaction_receipts(&self.content().parts, self.encoded_length())?;
1399        match self {
1400            Self::V1(chunk) => Ok(ShardChunk::V1(ShardChunkV1 {
1401                chunk_hash: chunk.header.chunk_hash().clone(),
1402                header: chunk.header.clone(),
1403                transactions: transaction_receipts.0,
1404                prev_outgoing_receipts: transaction_receipts.1,
1405            })),
1406
1407            Self::V2(chunk) => Ok(ShardChunk::V2(ShardChunkV2 {
1408                chunk_hash: chunk.header.chunk_hash().clone(),
1409                header: chunk.header.clone(),
1410                transactions: transaction_receipts.0,
1411                prev_outgoing_receipts: transaction_receipts.1,
1412            })),
1413        }
1414    }
1415}
1416
1417/// Combine shard chunk with its encoding to skip expensive encoding / decoding
1418/// and provide guarantees that the chunk and its encoding match.
1419#[derive(Clone)]
1420pub struct ShardChunkWithEncoding {
1421    shard_chunk: ShardChunk,
1422    bytes: EncodedShardChunk,
1423}
1424
1425impl ShardChunkWithEncoding {
1426    #[cfg(feature = "solomon")]
1427    pub fn new(
1428        prev_block_hash: CryptoHash,
1429        prev_state_root: StateRoot,
1430        prev_outcome_root: CryptoHash,
1431        height: u64,
1432        shard_id: ShardId,
1433        prev_gas_used: Gas,
1434        gas_limit: Gas,
1435        prev_balance_burnt: Balance,
1436        prev_validator_proposals: Vec<ValidatorStake>,
1437        validated_txs: Vec<ValidatedTransaction>,
1438        prev_outgoing_receipts: Vec<Receipt>,
1439        prev_outgoing_receipts_root: CryptoHash,
1440        tx_root: CryptoHash,
1441        congestion_info: CongestionInfo,
1442        bandwidth_requests: BandwidthRequests,
1443        proposed_split: Option<TrieSplit>,
1444        signer: &ValidatorSigner,
1445        rs: &reed_solomon_erasure::galois_8::ReedSolomon,
1446        protocol_version: ProtocolVersion,
1447    ) -> (ShardChunkWithEncoding, Vec<MerklePath>) {
1448        let signed_txs =
1449            validated_txs.into_iter().map(|validated_tx| validated_tx.into_signed_tx()).collect();
1450        let transaction_receipt = TransactionReceipt(signed_txs, prev_outgoing_receipts);
1451        let (parts, encoded_length) =
1452            crate::reed_solomon::reed_solomon_encode(rs, &transaction_receipt);
1453        let TransactionReceipt(signed_txs, prev_outgoing_receipts) = transaction_receipt;
1454        let content = EncodedShardChunkBody { parts };
1455        let (encoded_merkle_root, merkle_paths) = content.get_merkle_hash_and_paths();
1456
1457        let header = ShardChunkHeader::V3(ShardChunkHeaderV3::new(
1458            prev_block_hash,
1459            prev_state_root,
1460            prev_outcome_root,
1461            encoded_merkle_root,
1462            encoded_length as u64,
1463            height,
1464            shard_id,
1465            prev_gas_used,
1466            gas_limit,
1467            prev_balance_burnt,
1468            prev_outgoing_receipts_root,
1469            tx_root,
1470            prev_validator_proposals,
1471            congestion_info,
1472            bandwidth_requests,
1473            proposed_split,
1474            signer,
1475            protocol_version,
1476        ));
1477        let encoded_shard_chunk = EncodedShardChunk::V2(EncodedShardChunkV2 { header, content });
1478        let shard_chunk = ShardChunk::new(
1479            encoded_shard_chunk.cloned_header(),
1480            signed_txs,
1481            prev_outgoing_receipts,
1482        );
1483        (Self { shard_chunk, bytes: encoded_shard_chunk }, merkle_paths)
1484    }
1485
1486    #[cfg(feature = "solomon")]
1487    pub fn new_for_spice(
1488        prev_block_hash: CryptoHash,
1489        height: u64,
1490        shard_id: ShardId,
1491        validated_txs: Vec<ValidatedTransaction>,
1492        prev_outgoing_receipts: Vec<Receipt>,
1493        prev_outgoing_receipts_root: CryptoHash,
1494        tx_root: CryptoHash,
1495        signer: &ValidatorSigner,
1496        rs: &reed_solomon_erasure::galois_8::ReedSolomon,
1497    ) -> (ShardChunkWithEncoding, Vec<MerklePath>) {
1498        let signed_txs =
1499            validated_txs.into_iter().map(|validated_tx| validated_tx.into_signed_tx()).collect();
1500        let transaction_receipt = TransactionReceipt(signed_txs, prev_outgoing_receipts);
1501        let (parts, encoded_length) =
1502            crate::reed_solomon::reed_solomon_encode(rs, &transaction_receipt);
1503        let TransactionReceipt(signed_txs, prev_outgoing_receipts) = transaction_receipt;
1504        let content = EncodedShardChunkBody { parts };
1505        let (encoded_merkle_root, merkle_paths) = content.get_merkle_hash_and_paths();
1506
1507        let header = ShardChunkHeader::V3(ShardChunkHeaderV3::new_for_spice(
1508            prev_block_hash,
1509            encoded_merkle_root,
1510            encoded_length as u64,
1511            height,
1512            shard_id,
1513            prev_outgoing_receipts_root,
1514            tx_root,
1515            signer,
1516        ));
1517        let encoded_shard_chunk = EncodedShardChunk::V2(EncodedShardChunkV2 { header, content });
1518        let shard_chunk = ShardChunk::new(
1519            encoded_shard_chunk.cloned_header(),
1520            signed_txs,
1521            prev_outgoing_receipts,
1522        );
1523        (Self { shard_chunk, bytes: encoded_shard_chunk }, merkle_paths)
1524    }
1525
1526    pub fn from_encoded_shard_chunk(
1527        bytes: EncodedShardChunk,
1528    ) -> Result<Self, (std::io::Error, Box<EncodedShardChunk>)> {
1529        match bytes.decode_chunk() {
1530            Ok(shard_chunk) => Ok(Self { shard_chunk, bytes }),
1531            Err(err) => Err((err, Box::new(bytes))),
1532        }
1533    }
1534
1535    pub fn to_shard_chunk(&self) -> &ShardChunk {
1536        &self.shard_chunk
1537    }
1538
1539    pub fn to_encoded_shard_chunk(&self) -> &EncodedShardChunk {
1540        &self.bytes
1541    }
1542
1543    pub fn into_parts(self) -> (ShardChunk, EncodedShardChunk) {
1544        (self.shard_chunk, self.bytes)
1545    }
1546}
1547
1548#[derive(BorshDeserialize, BorshSerialize, Clone)]
1549pub struct ArcedShardChunkV1 {
1550    pub chunk_hash: ChunkHash,
1551    pub header: ShardChunkHeaderV1,
1552    pub transactions: Vec<Arc<SignedTransaction>>,
1553    pub prev_outgoing_receipts: Vec<Arc<Receipt>>,
1554}
1555
1556#[derive(BorshDeserialize, BorshSerialize, Clone)]
1557pub struct ArcedShardChunkV2 {
1558    pub chunk_hash: ChunkHash,
1559    pub header: ShardChunkHeader,
1560    pub transactions: Vec<Arc<SignedTransaction>>,
1561    pub prev_outgoing_receipts: Vec<Arc<Receipt>>,
1562}
1563
1564/// This struct has the same borsh representation as `ShardChunk` but it stores
1565/// some fields inside `Arc` to avoid some cloning when the chunk is being
1566/// persisted to disk.
1567#[derive(BorshDeserialize, BorshSerialize, Clone)]
1568#[borsh(use_discriminant = true)]
1569#[repr(u8)]
1570pub enum ArcedShardChunk {
1571    V1(ArcedShardChunkV1) = 0,
1572    V2(ArcedShardChunkV2) = 1,
1573}
1574
1575impl ArcedShardChunk {
1576    pub fn to_transactions(&self) -> &[Arc<SignedTransaction>] {
1577        match self {
1578            Self::V1(chunk) => &chunk.transactions,
1579            Self::V2(chunk) => &chunk.transactions,
1580        }
1581    }
1582
1583    pub fn to_prev_outgoing_receipts(&self) -> &[Arc<Receipt>] {
1584        match self {
1585            Self::V1(chunk) => &chunk.prev_outgoing_receipts,
1586            Self::V2(chunk) => &chunk.prev_outgoing_receipts,
1587        }
1588    }
1589
1590    pub fn to_chunk_hash(&self) -> ChunkHash {
1591        match self {
1592            Self::V1(chunk) => chunk.chunk_hash.clone(),
1593            Self::V2(chunk) => chunk.chunk_hash.clone(),
1594        }
1595    }
1596    pub fn height_created(&self) -> BlockHeight {
1597        match self {
1598            Self::V1(chunk) => chunk.header.inner.height_created,
1599            Self::V2(chunk) => chunk.header.height_created(),
1600        }
1601    }
1602}
1603
1604impl From<ShardChunkV1> for ArcedShardChunkV1 {
1605    fn from(chunk: ShardChunkV1) -> Self {
1606        let ShardChunkV1 { chunk_hash, header, transactions, prev_outgoing_receipts } = chunk;
1607        let transactions = transactions.into_iter().map(Arc::new).collect();
1608        let prev_outgoing_receipts = prev_outgoing_receipts.into_iter().map(Arc::new).collect();
1609        Self { chunk_hash, header, prev_outgoing_receipts, transactions }
1610    }
1611}
1612
1613impl From<&ArcedShardChunkV1> for ShardChunkV1 {
1614    fn from(chunk: &ArcedShardChunkV1) -> Self {
1615        let ArcedShardChunkV1 { chunk_hash, header, transactions, prev_outgoing_receipts } = chunk;
1616        let transactions = transactions.into_iter().map(|tx| tx.as_ref().clone()).collect();
1617        let prev_outgoing_receipts =
1618            prev_outgoing_receipts.into_iter().map(|r| r.as_ref().clone()).collect();
1619
1620        Self {
1621            chunk_hash: chunk_hash.clone(),
1622            header: header.clone(),
1623            transactions,
1624            prev_outgoing_receipts,
1625        }
1626    }
1627}
1628
1629impl From<ShardChunkV2> for ArcedShardChunkV2 {
1630    fn from(chunk: ShardChunkV2) -> Self {
1631        let ShardChunkV2 { chunk_hash, header, transactions, prev_outgoing_receipts } = chunk;
1632        let transactions = transactions.into_iter().map(Arc::new).collect();
1633        let prev_outgoing_receipts = prev_outgoing_receipts.into_iter().map(Arc::new).collect();
1634        Self { chunk_hash, header, prev_outgoing_receipts, transactions }
1635    }
1636}
1637
1638impl From<&ArcedShardChunkV2> for ShardChunkV2 {
1639    fn from(chunk: &ArcedShardChunkV2) -> Self {
1640        let ArcedShardChunkV2 { chunk_hash, header, transactions, prev_outgoing_receipts } = chunk;
1641        let transactions = transactions.into_iter().map(|tx| tx.as_ref().clone()).collect();
1642        let prev_outgoing_receipts =
1643            prev_outgoing_receipts.into_iter().map(|r| r.as_ref().clone()).collect();
1644
1645        Self {
1646            chunk_hash: chunk_hash.clone(),
1647            header: header.clone(),
1648            transactions,
1649            prev_outgoing_receipts,
1650        }
1651    }
1652}
1653
1654impl From<ShardChunk> for ArcedShardChunk {
1655    fn from(chunk: ShardChunk) -> Self {
1656        match chunk {
1657            ShardChunk::V1(chunk) => ArcedShardChunk::V1(ArcedShardChunkV1::from(chunk)),
1658            ShardChunk::V2(chunk) => ArcedShardChunk::V2(ArcedShardChunkV2::from(chunk)),
1659        }
1660    }
1661}
1662
1663impl From<&ArcedShardChunk> for ShardChunk {
1664    fn from(chunk: &ArcedShardChunk) -> Self {
1665        match chunk {
1666            ArcedShardChunk::V1(chunk) => Self::V1(ShardChunkV1::from(chunk)),
1667            ArcedShardChunk::V2(chunk) => Self::V2(ShardChunkV2::from(chunk)),
1668        }
1669    }
1670}
1671
1672#[cfg(test)]
1673mod tests {
1674    use crate::action::{Action, TransferAction};
1675    use crate::receipt::{ActionReceipt, Receipt, ReceiptEnum, ReceiptV0};
1676    use crate::sharding::{
1677        ArcedShardChunk, ArcedShardChunkV1, ArcedShardChunkV2, ChunkHash, ShardChunk,
1678        ShardChunkHeader, ShardChunkHeaderV1, ShardChunkHeaderV2, ShardChunkHeaderV3, ShardChunkV1,
1679        ShardChunkV2,
1680    };
1681    use crate::transaction::SignedTransaction;
1682    use near_crypto::{KeyType, PublicKey};
1683    use near_primitives_core::hash::CryptoHash;
1684    use near_primitives_core::types::{Balance, ShardId};
1685
1686    fn get_receipt() -> Receipt {
1687        let receipt_v0 = Receipt::V0(ReceiptV0 {
1688            predecessor_id: "predecessor_id".parse().unwrap(),
1689            receiver_id: "receiver_id".parse().unwrap(),
1690            receipt_id: CryptoHash::default(),
1691            receipt: ReceiptEnum::Action(ActionReceipt {
1692                signer_id: "signer_id".parse().unwrap(),
1693                signer_public_key: PublicKey::empty(KeyType::ED25519),
1694                gas_price: Balance::ZERO,
1695                output_data_receivers: vec![],
1696                input_data_ids: vec![],
1697                actions: vec![Action::Transfer(TransferAction { deposit: Balance::ZERO })],
1698            }),
1699        });
1700        receipt_v0
1701    }
1702
1703    #[test]
1704    fn shard_chunk_v1_conversion_is_valid() {
1705        let hash = CryptoHash([1; 32]);
1706        let chunk_hash = ChunkHash(hash);
1707        let shard_id = ShardId::new(3);
1708        let header = ShardChunkHeaderV1::new_dummy(1, shard_id, hash);
1709        let chunk = ShardChunkV1 {
1710            chunk_hash,
1711            header,
1712            transactions: vec![SignedTransaction::empty(hash)],
1713            prev_outgoing_receipts: vec![get_receipt()],
1714        };
1715        let arced = ArcedShardChunkV1::from(chunk.clone());
1716        assert_eq!(borsh::to_vec(&chunk).unwrap(), borsh::to_vec(&arced).unwrap());
1717
1718        let chunk = ShardChunkV1::from(&arced);
1719        assert_eq!(borsh::to_vec(&chunk).unwrap(), borsh::to_vec(&arced).unwrap());
1720    }
1721
1722    #[test]
1723    fn shard_chunk_v2_conversion_is_valid() {
1724        let hash = CryptoHash([2; 32]);
1725        let chunk_hash = ChunkHash(hash);
1726        let shard_id = ShardId::new(3);
1727        let header = ShardChunkHeader::V2(ShardChunkHeaderV2::new_dummy(1, shard_id, hash));
1728        let chunk = ShardChunkV2 {
1729            chunk_hash: chunk_hash.clone(),
1730            header,
1731            transactions: vec![SignedTransaction::empty(hash)],
1732            prev_outgoing_receipts: vec![get_receipt()],
1733        };
1734        let arced = ArcedShardChunkV2::from(chunk.clone());
1735        assert_eq!(borsh::to_vec(&chunk).unwrap(), borsh::to_vec(&arced).unwrap());
1736
1737        let chunk = ShardChunkV2::from(&arced);
1738        assert_eq!(borsh::to_vec(&chunk).unwrap(), borsh::to_vec(&arced).unwrap());
1739
1740        let header = ShardChunkHeader::V3(ShardChunkHeaderV3::new_dummy(1, shard_id, hash));
1741        let chunk = ShardChunkV2 {
1742            chunk_hash,
1743            header,
1744            transactions: vec![SignedTransaction::empty(hash)],
1745            prev_outgoing_receipts: vec![get_receipt()],
1746        };
1747        let arced = ArcedShardChunkV2::from(chunk.clone());
1748        assert_eq!(borsh::to_vec(&chunk).unwrap(), borsh::to_vec(&arced).unwrap());
1749
1750        let chunk = ShardChunkV2::from(&arced);
1751        assert_eq!(borsh::to_vec(&chunk).unwrap(), borsh::to_vec(&arced).unwrap());
1752    }
1753
1754    #[test]
1755    fn arced_shard_chunk_is_valid() {
1756        let shard_id = ShardId::new(3);
1757        let hash = CryptoHash([1; 32]);
1758        let header = ShardChunkHeader::new_dummy(1, shard_id, hash);
1759        let chunk =
1760            ShardChunk::new(header, vec![SignedTransaction::empty(hash)], vec![get_receipt()]);
1761        let arced = ArcedShardChunk::from(chunk.clone());
1762        assert_eq!(borsh::to_vec(&chunk).unwrap(), borsh::to_vec(&arced).unwrap());
1763
1764        let chunk = ShardChunk::from(&arced);
1765        assert_eq!(borsh::to_vec(&chunk).unwrap(), borsh::to_vec(&arced).unwrap());
1766    }
1767}