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#[derive(Debug, PartialEq, BorshSerialize, BorshDeserialize)]
63pub struct StateSyncInfo {
64 pub epoch_tail_hash: CryptoHash,
66 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 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 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#[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 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 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 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)]
644pub struct ReceiptProof(pub Vec<Receipt>, pub ShardProof);
646
647impl 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 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
1250const RS_TTL: u64 = 2 * 1024;
1253
1254pub 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}