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