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