unc_primitives/
sharding.rs

1use crate::hash::{hash, CryptoHash};
2use crate::merkle::{combine_hash, merklize, verify_path, MerklePath};
3use crate::receipt::Receipt;
4use crate::transaction::SignedTransaction;
5use crate::types::validator_power::{ValidatorPower, ValidatorPowerIter, ValidatorPowerV1};
6use crate::types::{Balance, BlockHeight, Gas, MerkleHash, ShardId, StateRoot, ValidatorPledgeV1};
7use crate::validator_signer::ValidatorSigner;
8use crate::version::{ProtocolFeature, ProtocolVersion, SHARD_CHUNK_HEADER_UPGRADE_VERSION};
9use borsh::{BorshDeserialize, BorshSerialize};
10use reed_solomon_erasure::galois_8::{Field, ReedSolomon};
11use reed_solomon_erasure::ReconstructShard;
12use std::cmp::Ordering;
13use std::sync::Arc;
14use tracing::debug_span;
15use unc_crypto::Signature;
16use unc_fmt::AbbrBytes;
17
18#[derive(
19    BorshSerialize,
20    BorshDeserialize,
21    Hash,
22    Eq,
23    PartialEq,
24    Ord,
25    PartialOrd,
26    Clone,
27    Debug,
28    Default,
29    serde::Serialize,
30    serde::Deserialize,
31)]
32pub struct ChunkHash(pub CryptoHash);
33
34impl ChunkHash {
35    pub fn as_bytes(&self) -> &[u8; 32] {
36        self.0.as_bytes()
37    }
38}
39
40impl AsRef<[u8]> for ChunkHash {
41    fn as_ref(&self) -> &[u8] {
42        self.0.as_ref()
43    }
44}
45
46impl From<ChunkHash> for Vec<u8> {
47    fn from(chunk_hash: ChunkHash) -> Self {
48        chunk_hash.0.into()
49    }
50}
51
52impl From<CryptoHash> for ChunkHash {
53    fn from(crypto_hash: CryptoHash) -> Self {
54        Self(crypto_hash)
55    }
56}
57
58#[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize)]
59pub struct ShardInfo(pub ShardId, pub ChunkHash);
60
61/// Contains the information that is used to sync state for shards as epochs switch
62#[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize)]
63pub struct StateSyncInfo {
64    /// The first block of the epoch for which syncing is happening
65    pub epoch_tail_hash: CryptoHash,
66    /// Shards to fetch state
67    pub shards: Vec<ShardInfo>,
68}
69
70pub mod shard_chunk_header_inner;
71use crate::types::validator_stake::{ValidatorPledge, ValidatorPledgeIter};
72pub use shard_chunk_header_inner::{
73    ShardChunkHeaderInner, ShardChunkHeaderInnerV1, ShardChunkHeaderInnerV2,
74};
75
76#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)]
77#[borsh(init=init)]
78pub struct ShardChunkHeaderV1 {
79    pub inner: ShardChunkHeaderInnerV1,
80
81    pub height_included: BlockHeight,
82
83    /// Signature of the chunk producer.
84    pub signature: Signature,
85
86    #[borsh(skip)]
87    pub hash: ChunkHash,
88}
89
90#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)]
91#[borsh(init=init)]
92pub struct ShardChunkHeaderV2 {
93    pub inner: ShardChunkHeaderInnerV1,
94
95    pub height_included: BlockHeight,
96
97    /// Signature of the chunk producer.
98    pub signature: Signature,
99
100    #[borsh(skip)]
101    pub hash: ChunkHash,
102}
103
104impl ShardChunkHeaderV2 {
105    pub fn init(&mut self) {
106        self.hash = Self::compute_hash(&self.inner);
107    }
108
109    pub fn compute_hash(inner: &ShardChunkHeaderInnerV1) -> ChunkHash {
110        let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize");
111        let inner_hash = hash(&inner_bytes);
112
113        ChunkHash(combine_hash(&inner_hash, &inner.encoded_merkle_root))
114    }
115
116    pub fn new(
117        prev_block_hash: CryptoHash,
118        prev_state_root: StateRoot,
119        prev_outcome_root: CryptoHash,
120        encoded_merkle_root: CryptoHash,
121        encoded_length: u64,
122        height: BlockHeight,
123        shard_id: ShardId,
124        prev_gas_used: Gas,
125        gas_limit: Gas,
126        prev_balance_burnt: Balance,
127        prev_outgoing_receipts_root: CryptoHash,
128        tx_root: CryptoHash,
129        prev_validator_power_proposals: Vec<ValidatorPowerV1>,
130        prev_validator_pledge_proposals: Vec<ValidatorPledgeV1>,
131        signer: &dyn ValidatorSigner,
132    ) -> Self {
133        let inner = ShardChunkHeaderInnerV1 {
134            prev_block_hash,
135            prev_state_root,
136            prev_outcome_root,
137            encoded_merkle_root,
138            encoded_length,
139            height_created: height,
140            shard_id,
141            prev_gas_used,
142            gas_limit,
143            prev_balance_burnt,
144            prev_outgoing_receipts_root,
145            tx_root,
146            prev_validator_power_proposals,
147            prev_validator_pledge_proposals,
148        };
149        let hash = Self::compute_hash(&inner);
150        let signature = signer.sign_chunk_hash(&hash);
151        Self { inner, height_included: 0, signature, hash }
152    }
153}
154
155// V2 -> V3: Use versioned ShardChunkHeaderInner structure
156#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)]
157#[borsh(init=init)]
158pub struct ShardChunkHeaderV3 {
159    pub inner: ShardChunkHeaderInner,
160
161    pub height_included: BlockHeight,
162
163    /// Signature of the chunk producer.
164    pub signature: Signature,
165
166    #[borsh(skip)]
167    pub hash: ChunkHash,
168}
169
170impl ShardChunkHeaderV3 {
171    pub fn init(&mut self) {
172        self.hash = Self::compute_hash(&self.inner);
173    }
174
175    pub fn compute_hash(inner: &ShardChunkHeaderInner) -> ChunkHash {
176        let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize");
177        let inner_hash = hash(&inner_bytes);
178
179        ChunkHash(combine_hash(&inner_hash, inner.encoded_merkle_root()))
180    }
181
182    pub fn new(
183        prev_block_hash: CryptoHash,
184        prev_state_root: StateRoot,
185        prev_outcome_root: CryptoHash,
186        encoded_merkle_root: CryptoHash,
187        encoded_length: u64,
188        height: BlockHeight,
189        shard_id: ShardId,
190        prev_gas_used: Gas,
191        gas_limit: Gas,
192        prev_balance_burnt: Balance,
193        prev_outgoing_receipts_root: CryptoHash,
194        tx_root: CryptoHash,
195        prev_validator_power_proposals: Vec<ValidatorPower>,
196        prev_validator_pledge_proposals: Vec<ValidatorPledge>,
197        signer: &dyn ValidatorSigner,
198    ) -> Self {
199        let inner = ShardChunkHeaderInner::V2(ShardChunkHeaderInnerV2 {
200            prev_block_hash,
201            prev_state_root,
202            prev_outcome_root,
203            encoded_merkle_root,
204            encoded_length,
205            height_created: height,
206            shard_id,
207            prev_gas_used,
208            gas_limit,
209            prev_balance_burnt,
210            prev_outgoing_receipts_root,
211            tx_root,
212            prev_validator_power_proposals,
213            prev_validator_pledge_proposals,
214        });
215        Self::from_inner(inner, signer)
216    }
217
218    pub fn from_inner(inner: ShardChunkHeaderInner, signer: &dyn ValidatorSigner) -> Self {
219        let hash = Self::compute_hash(&inner);
220        let signature = signer.sign_chunk_hash(&hash);
221        Self { inner, height_included: 0, signature, hash }
222    }
223}
224
225#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Eq, Debug)]
226pub enum ShardChunkHeader {
227    V1(ShardChunkHeaderV1),
228    V2(ShardChunkHeaderV2),
229    V3(ShardChunkHeaderV3),
230}
231
232impl ShardChunkHeader {
233    #[inline]
234    pub fn take_inner(self) -> ShardChunkHeaderInner {
235        match self {
236            Self::V1(header) => ShardChunkHeaderInner::V1(header.inner),
237            Self::V2(header) => ShardChunkHeaderInner::V1(header.inner),
238            Self::V3(header) => header.inner,
239        }
240    }
241
242    pub fn inner_header_hash(&self) -> CryptoHash {
243        let inner_bytes = match self {
244            Self::V1(header) => borsh::to_vec(&header.inner),
245            Self::V2(header) => borsh::to_vec(&header.inner),
246            Self::V3(header) => borsh::to_vec(&header.inner),
247        };
248        hash(&inner_bytes.expect("Failed to serialize"))
249    }
250
251    #[inline]
252    pub fn height_created(&self) -> BlockHeight {
253        match self {
254            Self::V1(header) => header.inner.height_created,
255            Self::V2(header) => header.inner.height_created,
256            Self::V3(header) => header.inner.height_created(),
257        }
258    }
259
260    #[inline]
261    pub fn signature(&self) -> &Signature {
262        match self {
263            Self::V1(header) => &header.signature,
264            Self::V2(header) => &header.signature,
265            Self::V3(header) => &header.signature,
266        }
267    }
268
269    #[inline]
270    pub fn height_included(&self) -> BlockHeight {
271        match self {
272            Self::V1(header) => header.height_included,
273            Self::V2(header) => header.height_included,
274            Self::V3(header) => header.height_included,
275        }
276    }
277
278    #[inline]
279    pub fn height_included_mut(&mut self) -> &mut BlockHeight {
280        match self {
281            Self::V1(header) => &mut header.height_included,
282            Self::V2(header) => &mut header.height_included,
283            Self::V3(header) => &mut header.height_included,
284        }
285    }
286
287    pub fn is_new_chunk(&self) -> bool {
288        self.height_created() == self.height_included()
289    }
290
291    #[inline]
292    pub fn prev_validator_power_proposals(&self) -> ValidatorPowerIter {
293        match self {
294            Self::V1(header) => {
295                ValidatorPowerIter::v1(&header.inner.prev_validator_power_proposals)
296            }
297            Self::V2(header) => {
298                ValidatorPowerIter::v1(&header.inner.prev_validator_power_proposals)
299            }
300            Self::V3(header) => header.inner.prev_validator_power_proposals(),
301        }
302    }
303
304    #[inline]
305    pub fn prev_validator_pledge_proposals(&self) -> ValidatorPledgeIter {
306        match self {
307            Self::V1(header) => {
308                ValidatorPledgeIter::v1(&header.inner.prev_validator_pledge_proposals)
309            }
310            Self::V2(header) => {
311                ValidatorPledgeIter::v1(&header.inner.prev_validator_pledge_proposals)
312            }
313            Self::V3(header) => header.inner.prev_validator_pledge_proposals(),
314        }
315    }
316
317    #[inline]
318    pub fn prev_state_root(&self) -> StateRoot {
319        match self {
320            Self::V1(header) => header.inner.prev_state_root,
321            Self::V2(header) => header.inner.prev_state_root,
322            Self::V3(header) => *header.inner.prev_state_root(),
323        }
324    }
325
326    #[inline]
327    pub fn prev_block_hash(&self) -> &CryptoHash {
328        match self {
329            Self::V1(header) => &header.inner.prev_block_hash,
330            Self::V2(header) => &header.inner.prev_block_hash,
331            Self::V3(header) => header.inner.prev_block_hash(),
332        }
333    }
334
335    #[inline]
336    pub fn encoded_merkle_root(&self) -> CryptoHash {
337        match self {
338            Self::V1(header) => header.inner.encoded_merkle_root,
339            Self::V2(header) => header.inner.encoded_merkle_root,
340            Self::V3(header) => *header.inner.encoded_merkle_root(),
341        }
342    }
343
344    #[inline]
345    pub fn shard_id(&self) -> ShardId {
346        match self {
347            Self::V1(header) => header.inner.shard_id,
348            Self::V2(header) => header.inner.shard_id,
349            Self::V3(header) => header.inner.shard_id(),
350        }
351    }
352
353    #[inline]
354    pub fn encoded_length(&self) -> u64 {
355        match self {
356            Self::V1(header) => header.inner.encoded_length,
357            Self::V2(header) => header.inner.encoded_length,
358            Self::V3(header) => header.inner.encoded_length(),
359        }
360    }
361
362    #[inline]
363    pub fn prev_gas_used(&self) -> Gas {
364        match &self {
365            ShardChunkHeader::V1(header) => header.inner.prev_gas_used,
366            ShardChunkHeader::V2(header) => header.inner.prev_gas_used,
367            ShardChunkHeader::V3(header) => header.inner.prev_gas_used(),
368        }
369    }
370
371    #[inline]
372    pub fn gas_limit(&self) -> Gas {
373        match &self {
374            ShardChunkHeader::V1(header) => header.inner.gas_limit,
375            ShardChunkHeader::V2(header) => header.inner.gas_limit,
376            ShardChunkHeader::V3(header) => header.inner.gas_limit(),
377        }
378    }
379
380    #[inline]
381    pub fn prev_balance_burnt(&self) -> Balance {
382        match &self {
383            ShardChunkHeader::V1(header) => header.inner.prev_balance_burnt,
384            ShardChunkHeader::V2(header) => header.inner.prev_balance_burnt,
385            ShardChunkHeader::V3(header) => header.inner.prev_balance_burnt(),
386        }
387    }
388
389    #[inline]
390    pub fn prev_outgoing_receipts_root(&self) -> CryptoHash {
391        match &self {
392            ShardChunkHeader::V1(header) => header.inner.prev_outgoing_receipts_root,
393            ShardChunkHeader::V2(header) => header.inner.prev_outgoing_receipts_root,
394            ShardChunkHeader::V3(header) => *header.inner.prev_outgoing_receipts_root(),
395        }
396    }
397
398    #[inline]
399    pub fn prev_outcome_root(&self) -> CryptoHash {
400        match &self {
401            ShardChunkHeader::V1(header) => header.inner.prev_outcome_root,
402            ShardChunkHeader::V2(header) => header.inner.prev_outcome_root,
403            ShardChunkHeader::V3(header) => *header.inner.prev_outcome_root(),
404        }
405    }
406
407    #[inline]
408    pub fn tx_root(&self) -> CryptoHash {
409        match &self {
410            ShardChunkHeader::V1(header) => header.inner.tx_root,
411            ShardChunkHeader::V2(header) => header.inner.tx_root,
412            ShardChunkHeader::V3(header) => *header.inner.tx_root(),
413        }
414    }
415
416    #[inline]
417    pub fn chunk_hash(&self) -> ChunkHash {
418        match &self {
419            ShardChunkHeader::V1(header) => header.hash.clone(),
420            ShardChunkHeader::V2(header) => header.hash.clone(),
421            ShardChunkHeader::V3(header) => header.hash.clone(),
422        }
423    }
424
425    /// Returns whether the header is valid for given `ProtocolVersion`.
426    pub fn valid_for(&self, version: ProtocolVersion) -> bool {
427        const BLOCK_HEADER_V3_VERSION: ProtocolVersion =
428            ProtocolFeature::BlockHeaderV3.protocol_version();
429        match &self {
430            ShardChunkHeader::V1(_) => version < SHARD_CHUNK_HEADER_UPGRADE_VERSION,
431            ShardChunkHeader::V2(_) => {
432                SHARD_CHUNK_HEADER_UPGRADE_VERSION <= version && version < BLOCK_HEADER_V3_VERSION
433            }
434            ShardChunkHeader::V3(_) => BLOCK_HEADER_V3_VERSION <= version,
435        }
436    }
437
438    pub fn compute_hash(&self) -> ChunkHash {
439        match self {
440            ShardChunkHeader::V1(header) => ShardChunkHeaderV1::compute_hash(&header.inner),
441            ShardChunkHeader::V2(header) => ShardChunkHeaderV2::compute_hash(&header.inner),
442            ShardChunkHeader::V3(header) => ShardChunkHeaderV3::compute_hash(&header.inner),
443        }
444    }
445}
446
447#[derive(BorshSerialize, BorshDeserialize, Hash, Eq, PartialEq, Clone, Debug, Default)]
448pub struct ChunkHashHeight(pub ChunkHash, pub BlockHeight);
449
450impl ShardChunkHeaderV1 {
451    pub fn init(&mut self) {
452        self.hash = Self::compute_hash(&self.inner);
453    }
454
455    pub fn chunk_hash(&self) -> ChunkHash {
456        self.hash.clone()
457    }
458
459    pub fn compute_hash(inner: &ShardChunkHeaderInnerV1) -> ChunkHash {
460        let inner_bytes = borsh::to_vec(&inner).expect("Failed to serialize");
461        let inner_hash = hash(&inner_bytes);
462
463        ChunkHash(inner_hash)
464    }
465
466    pub fn new(
467        prev_block_hash: CryptoHash,
468        prev_state_root: StateRoot,
469        prev_outcome_root: CryptoHash,
470        encoded_merkle_root: CryptoHash,
471        encoded_length: u64,
472        height: BlockHeight,
473        shard_id: ShardId,
474        prev_gas_used: Gas,
475        gas_limit: Gas,
476        prev_balance_burnt: Balance,
477        prev_outgoing_receipts_root: CryptoHash,
478        tx_root: CryptoHash,
479        prev_validator_power_proposals: Vec<ValidatorPowerV1>,
480        prev_validator_pledge_proposals: Vec<ValidatorPledgeV1>,
481        signer: &dyn ValidatorSigner,
482    ) -> Self {
483        let inner = ShardChunkHeaderInnerV1 {
484            prev_block_hash,
485            prev_state_root,
486            prev_outcome_root,
487            encoded_merkle_root,
488            encoded_length,
489            height_created: height,
490            shard_id,
491            prev_gas_used,
492            gas_limit,
493            prev_balance_burnt,
494            prev_outgoing_receipts_root,
495            tx_root,
496            prev_validator_power_proposals,
497            prev_validator_pledge_proposals,
498        };
499        let hash = Self::compute_hash(&inner);
500        let signature = signer.sign_chunk_hash(&hash);
501        Self { inner, height_included: 0, signature, hash }
502    }
503}
504
505#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)]
506pub enum PartialEncodedChunk {
507    V1(PartialEncodedChunkV1),
508    V2(PartialEncodedChunkV2),
509}
510
511impl PartialEncodedChunk {
512    pub fn new(
513        header: ShardChunkHeader,
514        parts: Vec<PartialEncodedChunkPart>,
515        receipts: Vec<ReceiptProof>,
516    ) -> Self {
517        match header {
518            ShardChunkHeader::V1(header) => {
519                Self::V1(PartialEncodedChunkV1 { header, parts, receipts })
520            }
521            header => Self::V2(PartialEncodedChunkV2 { header, parts, receipts }),
522        }
523    }
524
525    pub fn cloned_header(&self) -> ShardChunkHeader {
526        match self {
527            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()),
528            Self::V2(chunk) => chunk.header.clone(),
529        }
530    }
531
532    pub fn chunk_hash(&self) -> ChunkHash {
533        match self {
534            Self::V1(chunk) => chunk.header.hash.clone(),
535            Self::V2(chunk) => chunk.header.chunk_hash(),
536        }
537    }
538
539    pub fn height_included(&self) -> BlockHeight {
540        match self {
541            Self::V1(chunk) => chunk.header.height_included,
542            Self::V2(chunk) => chunk.header.height_included(),
543        }
544    }
545
546    #[inline]
547    pub fn parts(&self) -> &[PartialEncodedChunkPart] {
548        match self {
549            Self::V1(chunk) => &chunk.parts,
550            Self::V2(chunk) => &chunk.parts,
551        }
552    }
553
554    #[inline]
555    pub fn receipts(&self) -> &[ReceiptProof] {
556        match self {
557            Self::V1(chunk) => &chunk.receipts,
558            Self::V2(chunk) => &chunk.receipts,
559        }
560    }
561
562    #[inline]
563    pub fn prev_block(&self) -> &CryptoHash {
564        match &self {
565            PartialEncodedChunk::V1(chunk) => &chunk.header.inner.prev_block_hash,
566            PartialEncodedChunk::V2(chunk) => chunk.header.prev_block_hash(),
567        }
568    }
569
570    /// Returns whether the chenk is valid for given `ProtocolVersion`.
571    pub fn valid_for(&self, version: ProtocolVersion) -> bool {
572        match &self {
573            PartialEncodedChunk::V1(_) => version < SHARD_CHUNK_HEADER_UPGRADE_VERSION,
574            PartialEncodedChunk::V2(_) => SHARD_CHUNK_HEADER_UPGRADE_VERSION <= version,
575        }
576    }
577
578    pub fn height_created(&self) -> BlockHeight {
579        match self {
580            Self::V1(chunk) => chunk.header.inner.height_created,
581            Self::V2(chunk) => chunk.header.height_created(),
582        }
583    }
584    pub fn shard_id(&self) -> ShardId {
585        match self {
586            Self::V1(chunk) => chunk.header.inner.shard_id,
587            Self::V2(chunk) => chunk.header.shard_id(),
588        }
589    }
590}
591
592#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)]
593pub struct PartialEncodedChunkV2 {
594    pub header: ShardChunkHeader,
595    pub parts: Vec<PartialEncodedChunkPart>,
596    pub receipts: Vec<ReceiptProof>,
597}
598
599impl From<PartialEncodedChunk> for PartialEncodedChunkV2 {
600    fn from(pec: PartialEncodedChunk) -> Self {
601        match pec {
602            PartialEncodedChunk::V1(chunk) => PartialEncodedChunkV2 {
603                header: ShardChunkHeader::V1(chunk.header),
604                parts: chunk.parts,
605                receipts: chunk.receipts,
606            },
607            PartialEncodedChunk::V2(chunk) => chunk,
608        }
609    }
610}
611
612#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)]
613pub struct PartialEncodedChunkV1 {
614    pub header: ShardChunkHeaderV1,
615    pub parts: Vec<PartialEncodedChunkPart>,
616    pub receipts: Vec<ReceiptProof>,
617}
618
619#[derive(Debug, Clone, Eq, PartialEq)]
620pub struct PartialEncodedChunkWithArcReceipts {
621    pub header: ShardChunkHeader,
622    pub parts: Vec<PartialEncodedChunkPart>,
623    pub receipts: Vec<Arc<ReceiptProof>>,
624}
625
626impl From<PartialEncodedChunkWithArcReceipts> for PartialEncodedChunk {
627    fn from(pec: PartialEncodedChunkWithArcReceipts) -> Self {
628        Self::V2(PartialEncodedChunkV2 {
629            header: pec.header,
630            parts: pec.parts,
631            receipts: pec.receipts.into_iter().map(|r| ReceiptProof::clone(&r)).collect(),
632        })
633    }
634}
635
636#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, serde::Deserialize)]
637pub struct ShardProof {
638    pub from_shard_id: ShardId,
639    pub to_shard_id: ShardId,
640    pub proof: MerklePath,
641}
642
643#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq, serde::Deserialize)]
644/// For each Merkle proof there is a subset of receipts which may be proven.
645pub struct ReceiptProof(pub Vec<Receipt>, pub ShardProof);
646
647// Implement ordering to ensure `ReceiptProofs` are ordered consistently,
648// because we expect messages with ReceiptProofs to be deterministic.
649impl PartialOrd<Self> for ReceiptProof {
650    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
651        Some(self.cmp(other))
652    }
653}
654
655impl Ord for ReceiptProof {
656    fn cmp(&self, other: &Self) -> Ordering {
657        (self.1.from_shard_id, self.1.to_shard_id)
658            .cmp(&(other.1.from_shard_id, other.1.to_shard_id))
659    }
660}
661
662impl ReceiptProof {
663    pub fn verify_against_receipt_root(&self, receipt_root: CryptoHash) -> bool {
664        let ReceiptProof(shard_receipts, receipt_proof) = self;
665        let receipt_hash =
666            CryptoHash::hash_borsh(ReceiptList(receipt_proof.to_shard_id, shard_receipts));
667        verify_path(receipt_root, &receipt_proof.proof, &receipt_hash)
668    }
669}
670
671#[derive(BorshSerialize, BorshDeserialize, Clone, Eq, PartialEq)]
672pub struct PartialEncodedChunkPart {
673    pub part_ord: u64,
674    pub part: Box<[u8]>,
675    pub merkle_proof: MerklePath,
676}
677
678impl std::fmt::Debug for PartialEncodedChunkPart {
679    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
680        f.debug_struct("PartialEncodedChunkPart")
681            .field("part_ord", &self.part_ord)
682            .field("part", &format_args!("{}", AbbrBytes(self.part.as_ref())))
683            .field("merkle_proof", &self.merkle_proof)
684            .finish()
685    }
686}
687
688#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)]
689pub struct ShardChunkV1 {
690    pub chunk_hash: ChunkHash,
691    pub header: ShardChunkHeaderV1,
692    pub transactions: Vec<SignedTransaction>,
693    pub prev_outgoing_receipts: Vec<Receipt>,
694}
695
696#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)]
697pub struct ShardChunkV2 {
698    pub chunk_hash: ChunkHash,
699    pub header: ShardChunkHeader,
700    pub transactions: Vec<SignedTransaction>,
701    pub prev_outgoing_receipts: Vec<Receipt>,
702}
703
704#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, Eq, PartialEq)]
705pub enum ShardChunk {
706    V1(ShardChunkV1),
707    V2(ShardChunkV2),
708}
709
710impl ShardChunk {
711    pub fn with_header(chunk: ShardChunk, header: ShardChunkHeader) -> Option<ShardChunk> {
712        match chunk {
713            Self::V1(chunk) => match header {
714                ShardChunkHeader::V1(header) => Some(ShardChunk::V1(ShardChunkV1 {
715                    chunk_hash: header.chunk_hash(),
716                    header,
717                    transactions: chunk.transactions,
718                    prev_outgoing_receipts: chunk.prev_outgoing_receipts,
719                })),
720                ShardChunkHeader::V2(_) => None,
721                ShardChunkHeader::V3(_) => None,
722            },
723            Self::V2(chunk) => Some(ShardChunk::V2(ShardChunkV2 {
724                chunk_hash: header.chunk_hash(),
725                header,
726                transactions: chunk.transactions,
727                prev_outgoing_receipts: chunk.prev_outgoing_receipts,
728            })),
729        }
730    }
731
732    pub fn set_height_included(&mut self, height: BlockHeight) {
733        match self {
734            Self::V1(chunk) => chunk.header.height_included = height,
735            Self::V2(chunk) => *chunk.header.height_included_mut() = height,
736        }
737    }
738
739    #[inline]
740    pub fn height_included(&self) -> BlockHeight {
741        match self {
742            Self::V1(chunk) => chunk.header.height_included,
743            Self::V2(chunk) => chunk.header.height_included(),
744        }
745    }
746
747    #[inline]
748    pub fn height_created(&self) -> BlockHeight {
749        match self {
750            Self::V1(chunk) => chunk.header.inner.height_created,
751            Self::V2(chunk) => chunk.header.height_created(),
752        }
753    }
754
755    #[inline]
756    pub fn prev_block(&self) -> &CryptoHash {
757        match &self {
758            ShardChunk::V1(chunk) => &chunk.header.inner.prev_block_hash,
759            ShardChunk::V2(chunk) => chunk.header.prev_block_hash(),
760        }
761    }
762
763    #[inline]
764    pub fn prev_state_root(&self) -> StateRoot {
765        match self {
766            Self::V1(chunk) => chunk.header.inner.prev_state_root,
767            Self::V2(chunk) => chunk.header.prev_state_root(),
768        }
769    }
770
771    #[inline]
772    pub fn tx_root(&self) -> CryptoHash {
773        match self {
774            Self::V1(chunk) => chunk.header.inner.tx_root,
775            Self::V2(chunk) => chunk.header.tx_root(),
776        }
777    }
778
779    #[inline]
780    pub fn prev_outgoing_receipts_root(&self) -> CryptoHash {
781        match self {
782            Self::V1(chunk) => chunk.header.inner.prev_outgoing_receipts_root,
783            Self::V2(chunk) => chunk.header.prev_outgoing_receipts_root(),
784        }
785    }
786
787    #[inline]
788    pub fn shard_id(&self) -> ShardId {
789        match self {
790            Self::V1(chunk) => chunk.header.inner.shard_id,
791            Self::V2(chunk) => chunk.header.shard_id(),
792        }
793    }
794
795    #[inline]
796    pub fn chunk_hash(&self) -> ChunkHash {
797        match self {
798            Self::V1(chunk) => chunk.chunk_hash.clone(),
799            Self::V2(chunk) => chunk.chunk_hash.clone(),
800        }
801    }
802
803    #[inline]
804    pub fn prev_outgoing_receipts(&self) -> &[Receipt] {
805        match self {
806            Self::V1(chunk) => &chunk.prev_outgoing_receipts,
807            Self::V2(chunk) => &chunk.prev_outgoing_receipts,
808        }
809    }
810
811    #[inline]
812    pub fn transactions(&self) -> &[SignedTransaction] {
813        match self {
814            Self::V1(chunk) => &chunk.transactions,
815            Self::V2(chunk) => &chunk.transactions,
816        }
817    }
818
819    #[inline]
820    pub fn header_hash(&self) -> ChunkHash {
821        match self {
822            Self::V1(chunk) => chunk.header.chunk_hash(),
823            Self::V2(chunk) => chunk.header.chunk_hash(),
824        }
825    }
826
827    #[inline]
828    pub fn prev_block_hash(&self) -> CryptoHash {
829        match self {
830            Self::V1(chunk) => chunk.header.inner.prev_block_hash,
831            Self::V2(chunk) => *chunk.header.prev_block_hash(),
832        }
833    }
834
835    #[inline]
836    pub fn take_header(self) -> ShardChunkHeader {
837        match self {
838            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header),
839            Self::V2(chunk) => chunk.header,
840        }
841    }
842
843    pub fn cloned_header(&self) -> ShardChunkHeader {
844        match self {
845            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()),
846            Self::V2(chunk) => chunk.header.clone(),
847        }
848    }
849
850    pub fn compute_header_hash(&self) -> ChunkHash {
851        match self {
852            Self::V1(chunk) => ShardChunkHeaderV1::compute_hash(&chunk.header.inner),
853            Self::V2(chunk) => chunk.header.compute_hash(),
854        }
855    }
856}
857
858#[derive(Default, BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)]
859pub struct EncodedShardChunkBody {
860    pub parts: Vec<Option<Box<[u8]>>>,
861}
862
863impl EncodedShardChunkBody {
864    pub fn num_fetched_parts(&self) -> usize {
865        let mut fetched_parts: usize = 0;
866
867        for part in self.parts.iter() {
868            if part.is_some() {
869                fetched_parts += 1;
870            }
871        }
872
873        fetched_parts
874    }
875
876    /// Returns true if reconstruction was successful
877    pub fn reconstruct(
878        &mut self,
879        rs: &mut ReedSolomonWrapper,
880    ) -> Result<(), reed_solomon_erasure::Error> {
881        rs.reconstruct(self.parts.as_mut_slice())
882    }
883
884    pub fn get_merkle_hash_and_paths(&self) -> (MerkleHash, Vec<MerklePath>) {
885        let parts: Vec<&[u8]> =
886            self.parts.iter().map(|x| x.as_deref().unwrap()).collect::<Vec<_>>();
887        merklize(&parts)
888    }
889}
890
891#[derive(BorshSerialize, Debug, Clone)]
892pub struct ReceiptList<'a>(pub ShardId, pub &'a [Receipt]);
893
894#[derive(BorshSerialize, BorshDeserialize)]
895struct TransactionReceipt(Vec<SignedTransaction>, Vec<Receipt>);
896
897#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)]
898pub struct EncodedShardChunkV1 {
899    pub header: ShardChunkHeaderV1,
900    pub content: EncodedShardChunkBody,
901}
902
903impl EncodedShardChunkV1 {
904    pub fn chunk_hash(&self) -> ChunkHash {
905        self.header.chunk_hash()
906    }
907
908    pub fn decode_chunk(&self, data_parts: usize) -> Result<ShardChunkV1, std::io::Error> {
909        let transaction_receipts = EncodedShardChunk::decode_transaction_receipts(
910            &self.content.parts[0..data_parts],
911            self.header.inner.encoded_length,
912        )?;
913
914        Ok(ShardChunkV1 {
915            chunk_hash: self.header.chunk_hash(),
916            header: self.header.clone(),
917            transactions: transaction_receipts.0,
918            prev_outgoing_receipts: transaction_receipts.1,
919        })
920    }
921}
922
923#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)]
924pub struct EncodedShardChunkV2 {
925    pub header: ShardChunkHeader,
926    pub content: EncodedShardChunkBody,
927}
928
929#[derive(BorshSerialize, BorshDeserialize, Debug, Clone, PartialEq, Eq)]
930pub enum EncodedShardChunk {
931    V1(EncodedShardChunkV1),
932    V2(EncodedShardChunkV2),
933}
934
935impl EncodedShardChunk {
936    pub fn cloned_header(&self) -> ShardChunkHeader {
937        match self {
938            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()),
939            Self::V2(chunk) => chunk.header.clone(),
940        }
941    }
942
943    #[inline]
944    pub fn content(&self) -> &EncodedShardChunkBody {
945        match self {
946            Self::V1(chunk) => &chunk.content,
947            Self::V2(chunk) => &chunk.content,
948        }
949    }
950
951    #[inline]
952    pub fn content_mut(&mut self) -> &mut EncodedShardChunkBody {
953        match self {
954            Self::V1(chunk) => &mut chunk.content,
955            Self::V2(chunk) => &mut chunk.content,
956        }
957    }
958
959    #[inline]
960    pub fn shard_id(&self) -> ShardId {
961        match self {
962            Self::V1(chunk) => chunk.header.inner.shard_id,
963            Self::V2(chunk) => chunk.header.shard_id(),
964        }
965    }
966
967    #[inline]
968    pub fn encoded_merkle_root(&self) -> CryptoHash {
969        match self {
970            Self::V1(chunk) => chunk.header.inner.encoded_merkle_root,
971            Self::V2(chunk) => chunk.header.encoded_merkle_root(),
972        }
973    }
974
975    #[inline]
976    pub fn encoded_length(&self) -> u64 {
977        match self {
978            Self::V1(chunk) => chunk.header.inner.encoded_length,
979            Self::V2(chunk) => chunk.header.encoded_length(),
980        }
981    }
982
983    pub fn from_header(
984        header: ShardChunkHeader,
985        total_parts: usize,
986        protocol_version: ProtocolVersion,
987    ) -> Self {
988        if protocol_version < SHARD_CHUNK_HEADER_UPGRADE_VERSION {
989            if let ShardChunkHeader::V1(header) = header {
990                let chunk = EncodedShardChunkV1 {
991                    header,
992                    content: EncodedShardChunkBody { parts: vec![None; total_parts] },
993                };
994                Self::V1(chunk)
995            } else {
996                panic!("Attempted to include ShardChunkHeader::V2 in old protocol version");
997            }
998        } else {
999            let chunk = EncodedShardChunkV2 {
1000                header,
1001                content: EncodedShardChunkBody { parts: vec![None; total_parts] },
1002            };
1003            Self::V2(chunk)
1004        }
1005    }
1006
1007    fn decode_transaction_receipts(
1008        parts: &[Option<Box<[u8]>>],
1009        encoded_length: u64,
1010    ) -> Result<TransactionReceipt, std::io::Error> {
1011        let encoded_data = parts
1012            .iter()
1013            .flat_map(|option| option.as_ref().expect("Missing shard").iter())
1014            .cloned()
1015            .take(encoded_length as usize)
1016            .collect::<Vec<u8>>();
1017
1018        TransactionReceipt::try_from_slice(&encoded_data)
1019    }
1020
1021    pub fn encode_transaction_receipts(
1022        rs: &ReedSolomonWrapper,
1023        transactions: Vec<SignedTransaction>,
1024        outgoing_receipts: &[Receipt],
1025    ) -> Result<(Vec<Option<Box<[u8]>>>, u64), std::io::Error> {
1026        let mut bytes =
1027            borsh::to_vec(&TransactionReceipt(transactions, outgoing_receipts.to_vec()))?;
1028
1029        let mut parts = Vec::with_capacity(rs.total_shard_count());
1030        let data_parts = rs.data_shard_count();
1031        let total_parts = rs.total_shard_count();
1032        let encoded_length = bytes.len();
1033
1034        if bytes.len() % data_parts != 0 {
1035            bytes.extend((bytes.len() % data_parts..data_parts).map(|_| 0));
1036        }
1037        let shard_length = (encoded_length + data_parts - 1) / data_parts;
1038        assert_eq!(bytes.len(), shard_length * data_parts);
1039
1040        for i in 0..data_parts {
1041            parts.push(Some(
1042                bytes[i * shard_length..(i + 1) * shard_length].to_vec().into_boxed_slice()
1043                    as Box<[u8]>,
1044            ));
1045        }
1046        for _ in data_parts..total_parts {
1047            parts.push(None);
1048        }
1049        Ok((parts, encoded_length as u64))
1050    }
1051
1052    pub fn new(
1053        prev_block_hash: CryptoHash,
1054        prev_state_root: StateRoot,
1055        prev_outcome_root: CryptoHash,
1056        height: BlockHeight,
1057        shard_id: ShardId,
1058        rs: &mut ReedSolomonWrapper,
1059        prev_gas_used: Gas,
1060        gas_limit: Gas,
1061        prev_balance_burnt: Balance,
1062        tx_root: CryptoHash,
1063        prev_validator_power_proposals: Vec<ValidatorPower>,
1064        prev_validator_pledge_proposals: Vec<ValidatorPledge>,
1065        transactions: Vec<SignedTransaction>,
1066        prev_outgoing_receipts: &[Receipt],
1067        prev_outgoing_receipts_root: CryptoHash,
1068        signer: &dyn ValidatorSigner,
1069        protocol_version: ProtocolVersion,
1070    ) -> Result<(Self, Vec<MerklePath>), std::io::Error> {
1071        let (transaction_receipts_parts, encoded_length) =
1072            Self::encode_transaction_receipts(rs, transactions, prev_outgoing_receipts)?;
1073
1074        let mut content = EncodedShardChunkBody { parts: transaction_receipts_parts };
1075        content.reconstruct(rs).unwrap();
1076        let (encoded_merkle_root, merkle_paths) = content.get_merkle_hash_and_paths();
1077
1078        let block_header_v3_version = Some(ProtocolFeature::BlockHeaderV3.protocol_version());
1079
1080        if protocol_version < SHARD_CHUNK_HEADER_UPGRADE_VERSION {
1081            let prev_validator_power_proposals =
1082                prev_validator_power_proposals.into_iter().map(|v| v.into_v1()).collect();
1083            let prev_validator_pledge_proposals =
1084                prev_validator_pledge_proposals.into_iter().map(|v| v.into_v1()).collect();
1085            let header = ShardChunkHeaderV1::new(
1086                prev_block_hash,
1087                prev_state_root,
1088                prev_outcome_root,
1089                encoded_merkle_root,
1090                encoded_length,
1091                height,
1092                shard_id,
1093                prev_gas_used,
1094                gas_limit,
1095                prev_balance_burnt,
1096                prev_outgoing_receipts_root,
1097                tx_root,
1098                prev_validator_power_proposals,
1099                prev_validator_pledge_proposals,
1100                signer,
1101            );
1102            let chunk = EncodedShardChunkV1 { header, content };
1103            Ok((Self::V1(chunk), merkle_paths))
1104        } else if block_header_v3_version.is_none()
1105            || protocol_version < block_header_v3_version.unwrap()
1106        {
1107            let validator_power_proposals =
1108                prev_validator_power_proposals.into_iter().map(|v| v.into_v1()).collect();
1109            let validator_pledge_proposals =
1110                prev_validator_pledge_proposals.into_iter().map(|v| v.into_v1()).collect();
1111            let header = ShardChunkHeaderV2::new(
1112                prev_block_hash,
1113                prev_state_root,
1114                prev_outcome_root,
1115                encoded_merkle_root,
1116                encoded_length,
1117                height,
1118                shard_id,
1119                prev_gas_used,
1120                gas_limit,
1121                prev_balance_burnt,
1122                prev_outgoing_receipts_root,
1123                tx_root,
1124                validator_power_proposals,
1125                validator_pledge_proposals,
1126                signer,
1127            );
1128            let chunk = EncodedShardChunkV2 { header: ShardChunkHeader::V2(header), content };
1129            Ok((Self::V2(chunk), merkle_paths))
1130        } else {
1131            let header = ShardChunkHeaderV3::new(
1132                prev_block_hash,
1133                prev_state_root,
1134                prev_outcome_root,
1135                encoded_merkle_root,
1136                encoded_length,
1137                height,
1138                shard_id,
1139                prev_gas_used,
1140                gas_limit,
1141                prev_balance_burnt,
1142                prev_outgoing_receipts_root,
1143                tx_root,
1144                prev_validator_power_proposals,
1145                prev_validator_pledge_proposals,
1146                signer,
1147            );
1148            let chunk = EncodedShardChunkV2 { header: ShardChunkHeader::V3(header), content };
1149            Ok((Self::V2(chunk), merkle_paths))
1150        }
1151    }
1152
1153    pub fn chunk_hash(&self) -> ChunkHash {
1154        match self {
1155            Self::V1(chunk) => chunk.header.chunk_hash(),
1156            Self::V2(chunk) => chunk.header.chunk_hash(),
1157        }
1158    }
1159
1160    fn part_ords_to_parts(
1161        &self,
1162        part_ords: Vec<u64>,
1163        merkle_paths: &[MerklePath],
1164    ) -> Vec<PartialEncodedChunkPart> {
1165        let parts = match self {
1166            Self::V1(chunk) => &chunk.content.parts,
1167            Self::V2(chunk) => &chunk.content.parts,
1168        };
1169        part_ords
1170            .into_iter()
1171            .map(|part_ord| PartialEncodedChunkPart {
1172                part_ord,
1173                part: parts[part_ord as usize].clone().unwrap(),
1174                merkle_proof: merkle_paths[part_ord as usize].clone(),
1175            })
1176            .collect()
1177    }
1178
1179    pub fn create_partial_encoded_chunk(
1180        &self,
1181        part_ords: Vec<u64>,
1182        receipts: Vec<ReceiptProof>,
1183        merkle_paths: &[MerklePath],
1184    ) -> PartialEncodedChunk {
1185        let parts = self.part_ords_to_parts(part_ords, merkle_paths);
1186        match self {
1187            Self::V1(chunk) => {
1188                let chunk = PartialEncodedChunkV1 { header: chunk.header.clone(), parts, receipts };
1189                PartialEncodedChunk::V1(chunk)
1190            }
1191            Self::V2(chunk) => {
1192                let chunk = PartialEncodedChunkV2 { header: chunk.header.clone(), parts, receipts };
1193                PartialEncodedChunk::V2(chunk)
1194            }
1195        }
1196    }
1197
1198    pub fn create_partial_encoded_chunk_with_arc_receipts(
1199        &self,
1200        part_ords: Vec<u64>,
1201        receipts: Vec<Arc<ReceiptProof>>,
1202        merkle_paths: &[MerklePath],
1203    ) -> PartialEncodedChunkWithArcReceipts {
1204        let parts = self.part_ords_to_parts(part_ords, merkle_paths);
1205        let header = match self {
1206            Self::V1(chunk) => ShardChunkHeader::V1(chunk.header.clone()),
1207            Self::V2(chunk) => chunk.header.clone(),
1208        };
1209        PartialEncodedChunkWithArcReceipts { header, parts, receipts }
1210    }
1211
1212    pub fn decode_chunk(&self, data_parts: usize) -> Result<ShardChunk, std::io::Error> {
1213        let _span = debug_span!(
1214            target: "sharding",
1215            "decode_chunk",
1216            data_parts,
1217            height_included = self.cloned_header().height_included(),
1218            shard_id = self.cloned_header().shard_id(),
1219            chunk_hash = ?self.chunk_hash())
1220        .entered();
1221        let parts = match self {
1222            Self::V1(chunk) => &chunk.content.parts[0..data_parts],
1223            Self::V2(chunk) => &chunk.content.parts[0..data_parts],
1224        };
1225        let encoded_length = match self {
1226            Self::V1(chunk) => chunk.header.inner.encoded_length,
1227            Self::V2(chunk) => chunk.header.encoded_length(),
1228        };
1229
1230        let transaction_receipts = Self::decode_transaction_receipts(parts, encoded_length)?;
1231
1232        match self {
1233            Self::V1(chunk) => Ok(ShardChunk::V1(ShardChunkV1 {
1234                chunk_hash: chunk.header.chunk_hash(),
1235                header: chunk.header.clone(),
1236                transactions: transaction_receipts.0,
1237                prev_outgoing_receipts: transaction_receipts.1,
1238            })),
1239
1240            Self::V2(chunk) => Ok(ShardChunk::V2(ShardChunkV2 {
1241                chunk_hash: chunk.header.chunk_hash(),
1242                header: chunk.header.clone(),
1243                transactions: transaction_receipts.0,
1244                prev_outgoing_receipts: transaction_receipts.1,
1245            })),
1246        }
1247    }
1248}
1249
1250/// The ttl for a reed solomon instance to control memory usage. This number below corresponds to
1251/// roughly 60MB of memory usage.
1252const RS_TTL: u64 = 2 * 1024;
1253
1254/// Wrapper around reed solomon which occasionally resets the underlying
1255/// reed solomon instead to work around the memory leak in reed solomon
1256/// implementation <https://github.com/darrenldl/reed-solomon-erasure/issues/74>
1257pub struct ReedSolomonWrapper {
1258    rs: ReedSolomon,
1259    ttl: u64,
1260}
1261
1262impl ReedSolomonWrapper {
1263    pub fn new(data_shards: usize, parity_shards: usize) -> Self {
1264        ReedSolomonWrapper {
1265            rs: ReedSolomon::new(data_shards, parity_shards).unwrap(),
1266            ttl: RS_TTL,
1267        }
1268    }
1269
1270    pub fn reconstruct<T: ReconstructShard<Field>>(
1271        &mut self,
1272        slices: &mut [T],
1273    ) -> Result<(), reed_solomon_erasure::Error> {
1274        let res = self.rs.reconstruct(slices);
1275        self.ttl -= 1;
1276        if self.ttl == 0 {
1277            *self =
1278                ReedSolomonWrapper::new(self.rs.data_shard_count(), self.rs.parity_shard_count());
1279        }
1280        res
1281    }
1282
1283    pub fn data_shard_count(&self) -> usize {
1284        self.rs.data_shard_count()
1285    }
1286
1287    pub fn total_shard_count(&self) -> usize {
1288        self.rs.total_shard_count()
1289    }
1290}