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