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