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