1use crate::action::GlobalContractIdentifier;
2use crate::errors::EpochError;
3use crate::hash::CryptoHash;
4use crate::shard_layout::ShardLayout;
5use crate::transaction::{Action, TransferAction};
6use crate::types::{AccountId, Balance, BlockHeight, ShardId};
7use borsh::{BorshDeserialize, BorshSerialize};
8use itertools::Itertools;
9use near_crypto::{KeyType, PublicKey};
10use near_fmt::AbbrBytes;
11use near_primitives_core::types::Gas;
12use near_schema_checker_lib::ProtocolSchema;
13use serde_with::base64::Base64;
14use serde_with::serde_as;
15use std::borrow::Cow;
16use std::collections::{BTreeMap, HashMap};
17use std::fmt;
18use std::io::{self, Read};
19use std::io::{Error, ErrorKind};
20use std::sync::Arc;
21
22#[derive(
25 BorshSerialize,
26 BorshDeserialize,
27 Hash,
28 Clone,
29 Debug,
30 PartialEq,
31 Eq,
32 serde::Serialize,
33 serde::Deserialize,
34 ProtocolSchema,
35)]
36#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
37pub struct DataReceiver {
38 pub data_id: CryptoHash,
39 pub receiver_id: AccountId,
40}
41
42#[derive(
45 BorshSerialize,
46 BorshDeserialize,
47 Debug,
48 PartialEq,
49 Eq,
50 Clone,
51 serde::Serialize,
52 serde::Deserialize,
53 ProtocolSchema,
54)]
55#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
56pub struct ReceiptV0 {
57 pub predecessor_id: AccountId,
60 pub receiver_id: AccountId,
62 pub receipt_id: CryptoHash,
64 pub receipt: ReceiptEnum,
66}
67
68#[derive(
73 BorshSerialize,
74 BorshDeserialize,
75 Debug,
76 PartialEq,
77 Eq,
78 Clone,
79 serde::Serialize,
80 serde::Deserialize,
81 ProtocolSchema,
82)]
83#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
84pub struct ReceiptV1 {
85 pub predecessor_id: AccountId,
88 pub receiver_id: AccountId,
90 pub receipt_id: CryptoHash,
92 pub receipt: ReceiptEnum,
94 pub priority: u64,
96}
97
98#[derive(Debug, PartialEq, Eq, Clone, serde::Serialize, serde::Deserialize, ProtocolSchema)]
99#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
100#[serde(untagged)]
101pub enum Receipt {
102 V0(ReceiptV0),
103 V1(ReceiptV1),
104}
105
106#[derive(PartialEq, Eq, Debug, ProtocolSchema)]
116pub enum StateStoredReceipt<'a> {
117 V0(StateStoredReceiptV0<'a>),
118 V1(StateStoredReceiptV1<'a>),
119}
120
121#[derive(BorshDeserialize, BorshSerialize, PartialEq, Eq, Debug, ProtocolSchema)]
123pub struct StateStoredReceiptV0<'a> {
124 pub receipt: Cow<'a, Receipt>,
126 pub metadata: StateStoredReceiptMetadata,
127}
128
129#[derive(BorshDeserialize, BorshSerialize, PartialEq, Eq, Debug, ProtocolSchema)]
136pub struct StateStoredReceiptV1<'a> {
137 pub receipt: Cow<'a, Receipt>,
138 pub metadata: StateStoredReceiptMetadata,
139}
140
141#[derive(BorshDeserialize, BorshSerialize, PartialEq, Eq, Debug, ProtocolSchema)]
143pub struct StateStoredReceiptMetadata {
144 pub congestion_gas: Gas,
147 pub congestion_size: u64,
150}
151
152const STATE_STORED_RECEIPT_TAG: u8 = u8::MAX;
154
155#[derive(PartialEq, Eq, Debug, ProtocolSchema)]
169pub enum ReceiptOrStateStoredReceipt<'a> {
170 Receipt(Cow<'a, Receipt>),
171 StateStoredReceipt(StateStoredReceipt<'a>),
172}
173
174impl ReceiptOrStateStoredReceipt<'_> {
175 pub fn into_receipt(self) -> Receipt {
176 match self {
177 ReceiptOrStateStoredReceipt::Receipt(receipt) => receipt.into_owned(),
178 ReceiptOrStateStoredReceipt::StateStoredReceipt(receipt) => receipt.into_receipt(),
179 }
180 }
181
182 pub fn get_receipt(&self) -> &Receipt {
183 match self {
184 ReceiptOrStateStoredReceipt::Receipt(receipt) => receipt,
185 ReceiptOrStateStoredReceipt::StateStoredReceipt(receipt) => receipt.get_receipt(),
186 }
187 }
188
189 pub fn should_update_outgoing_metadatas(&self) -> bool {
190 match self {
191 ReceiptOrStateStoredReceipt::Receipt(_) => false,
192 ReceiptOrStateStoredReceipt::StateStoredReceipt(state_stored_receipt) => {
193 state_stored_receipt.should_update_outgoing_metadatas()
194 }
195 }
196 }
197}
198
199impl<'a> StateStoredReceipt<'a> {
200 pub fn new_owned(receipt: Receipt, metadata: StateStoredReceiptMetadata) -> Self {
201 let receipt = Cow::Owned(receipt);
202 Self::V1(StateStoredReceiptV1 { receipt, metadata })
203 }
204
205 pub fn new_borrowed(receipt: &'a Receipt, metadata: StateStoredReceiptMetadata) -> Self {
206 let receipt = Cow::Borrowed(receipt);
207
208 Self::V1(StateStoredReceiptV1 { receipt, metadata })
209 }
210
211 pub fn into_receipt(self) -> Receipt {
212 match self {
213 StateStoredReceipt::V0(v0) => v0.receipt.into_owned(),
214 StateStoredReceipt::V1(v1) => v1.receipt.into_owned(),
215 }
216 }
217
218 pub fn get_receipt(&self) -> &Receipt {
219 match self {
220 StateStoredReceipt::V0(v0) => &v0.receipt,
221 StateStoredReceipt::V1(v1) => &v1.receipt,
222 }
223 }
224
225 pub fn metadata(&self) -> &StateStoredReceiptMetadata {
226 match self {
227 StateStoredReceipt::V0(v0) => &v0.metadata,
228 StateStoredReceipt::V1(v1) => &v1.metadata,
229 }
230 }
231
232 pub fn should_update_outgoing_metadatas(&self) -> bool {
233 match self {
234 StateStoredReceipt::V0(_) => false,
235 StateStoredReceipt::V1(_) => true,
236 }
237 }
238}
239
240impl BorshSerialize for Receipt {
241 fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
242 match self {
243 Receipt::V0(receipt) => BorshSerialize::serialize(&receipt, writer),
244 Receipt::V1(receipt) => {
245 BorshSerialize::serialize(&1_u8, writer)?;
246 BorshSerialize::serialize(&receipt, writer)
247 }
248 }
249 }
250}
251
252impl BorshDeserialize for Receipt {
253 fn deserialize_reader<R: Read>(reader: &mut R) -> std::io::Result<Self> {
256 let u1 = u8::deserialize_reader(reader)?;
264 let u2 = u8::deserialize_reader(reader)?;
265 let is_v0 = u2 == 0;
266
267 let prefix = if is_v0 { vec![u1, u2] } else { vec![u2] };
268 let mut reader = prefix.chain(reader);
269
270 let receipt = if is_v0 {
271 Receipt::V0(ReceiptV0::deserialize_reader(&mut reader)?)
272 } else {
273 Receipt::V1(ReceiptV1::deserialize_reader(&mut reader)?)
274 };
275 Ok(receipt)
276 }
277}
278
279impl BorshSerialize for StateStoredReceipt<'_> {
280 fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
281 BorshSerialize::serialize(&STATE_STORED_RECEIPT_TAG, writer)?;
288 BorshSerialize::serialize(&STATE_STORED_RECEIPT_TAG, writer)?;
289 match self {
290 StateStoredReceipt::V0(v0) => {
291 BorshSerialize::serialize(&0_u8, writer)?;
292 BorshSerialize::serialize(&v0, writer)?;
293 }
294 StateStoredReceipt::V1(v1) => {
295 BorshSerialize::serialize(&1_u8, writer)?;
296 BorshSerialize::serialize(&v1, writer)?;
297 }
298 }
299 Ok(())
300 }
301}
302
303impl BorshDeserialize for StateStoredReceipt<'_> {
304 fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
305 let u1 = u8::deserialize_reader(reader)?;
306 let u2 = u8::deserialize_reader(reader)?;
307 let u3 = u8::deserialize_reader(reader)?;
308
309 if u1 != STATE_STORED_RECEIPT_TAG || u2 != STATE_STORED_RECEIPT_TAG {
310 let error = format!(
311 "Invalid tag found when deserializing StateStoredReceipt. Found: {}, {}. Expected: {}, {}",
312 u1, u2, STATE_STORED_RECEIPT_TAG, STATE_STORED_RECEIPT_TAG
313 );
314 let error = Error::new(ErrorKind::Other, error);
315 return Err(io::Error::new(ErrorKind::InvalidData, error));
316 }
317
318 match u3 {
319 0 => {
320 let v0 = StateStoredReceiptV0::deserialize_reader(reader)?;
321 Ok(StateStoredReceipt::V0(v0))
322 }
323 1 => {
324 let v1 = StateStoredReceiptV1::deserialize_reader(reader)?;
325 Ok(StateStoredReceipt::V1(v1))
326 }
327 v => {
328 let error = format!(
329 "Invalid version found when deserializing StateStoredReceipt. Found: {}. Expected: 0",
330 v
331 );
332 let error = Error::new(ErrorKind::Other, error);
333 Err(io::Error::new(ErrorKind::InvalidData, error))
334 }
335 }
336 }
337}
338
339impl BorshSerialize for ReceiptOrStateStoredReceipt<'_> {
340 fn serialize<W: io::Write>(&self, writer: &mut W) -> io::Result<()> {
341 match self {
346 ReceiptOrStateStoredReceipt::Receipt(receipt) => {
347 BorshSerialize::serialize(receipt, writer)
348 }
349 ReceiptOrStateStoredReceipt::StateStoredReceipt(receipt) => {
350 BorshSerialize::serialize(receipt, writer)
351 }
352 }
353 }
354}
355
356impl BorshDeserialize for ReceiptOrStateStoredReceipt<'_> {
357 fn deserialize_reader<R: Read>(reader: &mut R) -> io::Result<Self> {
358 let u1 = u8::deserialize_reader(reader)?;
370 let u2 = u8::deserialize_reader(reader)?;
371
372 let prefix = [u1, u2];
374 let mut reader = prefix.chain(reader);
375
376 if u1 == STATE_STORED_RECEIPT_TAG && u2 == STATE_STORED_RECEIPT_TAG {
377 let receipt = StateStoredReceipt::deserialize_reader(&mut reader)?;
378 Ok(ReceiptOrStateStoredReceipt::StateStoredReceipt(receipt))
379 } else {
380 let receipt = Receipt::deserialize_reader(&mut reader)?;
381 let receipt = Cow::Owned(receipt);
382 Ok(ReceiptOrStateStoredReceipt::Receipt(receipt))
383 }
384 }
385}
386
387pub enum ReceiptPriority {
388 Priority(u64),
390 NoPriority,
392}
393
394impl ReceiptPriority {
395 pub fn value(&self) -> u64 {
396 match self {
397 ReceiptPriority::Priority(value) => *value,
398 ReceiptPriority::NoPriority => 0,
399 }
400 }
401}
402
403impl Receipt {
404 pub fn from_tx(
405 receipt_id: CryptoHash,
406 tx_signer_id: AccountId,
407 tx_receiver_id: AccountId,
408 signer_public_key: PublicKey,
409 gas_price: Balance,
410 actions: Vec<Action>,
411 ) -> Self {
412 Receipt::V0(ReceiptV0 {
413 predecessor_id: tx_signer_id.clone(),
414 receiver_id: tx_receiver_id,
415 receipt_id,
416 receipt: ReceiptEnum::Action(ActionReceipt {
417 signer_id: tx_signer_id,
418 signer_public_key,
419 gas_price,
420 output_data_receivers: vec![],
421 input_data_ids: vec![],
422 actions,
423 }),
424 })
425 }
426
427 pub fn receiver_id(&self) -> &AccountId {
428 match self {
429 Receipt::V0(receipt) => &receipt.receiver_id,
430 Receipt::V1(receipt) => &receipt.receiver_id,
431 }
432 }
433
434 pub fn set_receiver_id(&mut self, receiver_id: AccountId) {
435 match self {
436 Receipt::V0(receipt) => receipt.receiver_id = receiver_id,
437 Receipt::V1(receipt) => receipt.receiver_id = receiver_id,
438 }
439 }
440
441 pub fn predecessor_id(&self) -> &AccountId {
442 match self {
443 Receipt::V0(receipt) => &receipt.predecessor_id,
444 Receipt::V1(receipt) => &receipt.predecessor_id,
445 }
446 }
447
448 pub fn set_predecessor_id(&mut self, predecessor_id: AccountId) {
449 match self {
450 Receipt::V0(receipt) => receipt.predecessor_id = predecessor_id,
451 Receipt::V1(receipt) => receipt.predecessor_id = predecessor_id,
452 }
453 }
454
455 pub fn receipt(&self) -> &ReceiptEnum {
456 match self {
457 Receipt::V0(receipt) => &receipt.receipt,
458 Receipt::V1(receipt) => &receipt.receipt,
459 }
460 }
461
462 pub fn versioned_receipt(&self) -> VersionedReceiptEnum {
463 match self {
464 Receipt::V0(receipt) => VersionedReceiptEnum::from(&receipt.receipt),
465 Receipt::V1(receipt) => VersionedReceiptEnum::from(&receipt.receipt),
466 }
467 }
468
469 pub fn receipt_mut(&mut self) -> &mut ReceiptEnum {
470 match self {
471 Receipt::V0(receipt) => &mut receipt.receipt,
472 Receipt::V1(receipt) => &mut receipt.receipt,
473 }
474 }
475
476 pub fn take_versioned_receipt<'a>(self) -> VersionedReceiptEnum<'a> {
477 match self {
478 Receipt::V0(receipt) => VersionedReceiptEnum::from(receipt.receipt),
479 Receipt::V1(receipt) => VersionedReceiptEnum::from(receipt.receipt),
480 }
481 }
482
483 pub fn receipt_id(&self) -> &CryptoHash {
484 match self {
485 Receipt::V0(receipt) => &receipt.receipt_id,
486 Receipt::V1(receipt) => &receipt.receipt_id,
487 }
488 }
489
490 pub fn set_receipt_id(&mut self, receipt_id: CryptoHash) {
491 match self {
492 Receipt::V0(receipt) => receipt.receipt_id = receipt_id,
493 Receipt::V1(receipt) => receipt.receipt_id = receipt_id,
494 }
495 }
496
497 pub fn priority(&self) -> ReceiptPriority {
498 match self {
499 Receipt::V0(_) => ReceiptPriority::NoPriority,
500 Receipt::V1(receipt) => ReceiptPriority::Priority(receipt.priority),
501 }
502 }
503
504 pub fn refund_to(&self) -> &Option<AccountId> {
505 match self.receipt() {
506 ReceiptEnum::Action(_)
507 | ReceiptEnum::Data(_)
508 | ReceiptEnum::PromiseYield(_)
509 | ReceiptEnum::PromiseResume(_)
510 | ReceiptEnum::GlobalContractDistribution(_) => &None,
511 ReceiptEnum::ActionV2(action_receipt_v2)
512 | ReceiptEnum::PromiseYieldV2(action_receipt_v2) => &action_receipt_v2.refund_to,
513 }
514 }
515
516 pub fn balance_refund_receiver(&self) -> &AccountId {
517 self.refund_to().as_ref().unwrap_or_else(|| self.predecessor_id())
518 }
519
520 pub fn get_hash(&self) -> CryptoHash {
522 *self.receipt_id()
523 }
524
525 pub fn receiver_shard_id(&self, shard_layout: &ShardLayout) -> Result<ShardId, EpochError> {
526 let shard_id = match self.receipt() {
527 ReceiptEnum::Action(_)
528 | ReceiptEnum::ActionV2(_)
529 | ReceiptEnum::Data(_)
530 | ReceiptEnum::PromiseYield(_)
531 | ReceiptEnum::PromiseYieldV2(_)
532 | ReceiptEnum::PromiseResume(_) => {
533 shard_layout.account_id_to_shard_id(self.receiver_id())
534 }
535 ReceiptEnum::GlobalContractDistribution(receipt) => {
536 let target_shard = receipt.target_shard();
537 if shard_layout.shard_ids().contains(&target_shard) {
538 target_shard
539 } else {
540 let Some(children_shards) = shard_layout.get_children_shards_ids(target_shard)
541 else {
542 return Err(EpochError::ShardingError(format!(
543 "Shard {target_shard} does not exist in the parent shard layout",
544 )));
545 };
546 children_shards[0]
549 }
550 }
551 };
552 Ok(shard_id)
553 }
554
555 pub fn new_balance_refund(
559 receiver_id: &AccountId,
560 refund: Balance,
561 priority: ReceiptPriority,
562 ) -> Self {
563 match priority {
564 ReceiptPriority::Priority(priority) => Receipt::V1(ReceiptV1 {
565 predecessor_id: "system".parse().unwrap(),
566 receiver_id: receiver_id.clone(),
567 receipt_id: CryptoHash::default(),
568
569 receipt: ReceiptEnum::Action(ActionReceipt {
570 signer_id: "system".parse().unwrap(),
571 signer_public_key: PublicKey::empty(KeyType::ED25519),
572 gas_price: Balance::ZERO,
573 output_data_receivers: vec![],
574 input_data_ids: vec![],
575 actions: vec![Action::Transfer(TransferAction { deposit: refund })],
576 }),
577 priority,
578 }),
579 ReceiptPriority::NoPriority => Receipt::V0(ReceiptV0 {
580 predecessor_id: "system".parse().unwrap(),
581 receiver_id: receiver_id.clone(),
582 receipt_id: CryptoHash::default(),
583
584 receipt: ReceiptEnum::Action(ActionReceipt {
585 signer_id: "system".parse().unwrap(),
586 signer_public_key: PublicKey::empty(KeyType::ED25519),
587 gas_price: Balance::ZERO,
588 output_data_receivers: vec![],
589 input_data_ids: vec![],
590 actions: vec![Action::Transfer(TransferAction { deposit: refund })],
591 }),
592 }),
593 }
594 }
595
596 pub fn new_gas_refund(
604 receiver_id: &AccountId,
605 refund: Balance,
606 signer_public_key: PublicKey,
607 priority: ReceiptPriority,
608 ) -> Self {
609 match priority {
610 ReceiptPriority::Priority(priority) => Receipt::V1(ReceiptV1 {
611 predecessor_id: "system".parse().unwrap(),
612 receiver_id: receiver_id.clone(),
613 receipt_id: CryptoHash::default(),
614
615 receipt: ReceiptEnum::Action(ActionReceipt {
616 signer_id: receiver_id.clone(),
617 signer_public_key,
618 gas_price: Balance::ZERO,
619 output_data_receivers: vec![],
620 input_data_ids: vec![],
621 actions: vec![Action::Transfer(TransferAction { deposit: refund })],
622 }),
623 priority,
624 }),
625 ReceiptPriority::NoPriority => Receipt::V0(ReceiptV0 {
626 predecessor_id: "system".parse().unwrap(),
627 receiver_id: receiver_id.clone(),
628 receipt_id: CryptoHash::default(),
629
630 receipt: ReceiptEnum::Action(ActionReceipt {
631 signer_id: receiver_id.clone(),
632 signer_public_key,
633 gas_price: Balance::ZERO,
634 output_data_receivers: vec![],
635 input_data_ids: vec![],
636 actions: vec![Action::Transfer(TransferAction { deposit: refund })],
637 }),
638 }),
639 }
640 }
641
642 pub fn new_global_contract_distribution(
643 predecessor_id: AccountId,
644 receipt: GlobalContractDistributionReceipt,
645 ) -> Self {
646 Self::V0(ReceiptV0 {
647 predecessor_id,
648 receiver_id: "system".parse().unwrap(),
649 receipt_id: CryptoHash::default(),
650 receipt: ReceiptEnum::GlobalContractDistribution(receipt),
651 })
652 }
653}
654
655#[derive(
657 BorshSerialize,
658 BorshDeserialize,
659 Clone,
660 Debug,
661 PartialEq,
662 Eq,
663 serde::Serialize,
664 serde::Deserialize,
665 ProtocolSchema,
666)]
667#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
668#[borsh(use_discriminant = true)]
669#[repr(u8)]
670pub enum ReceiptEnum {
671 Action(ActionReceipt) = 0,
672 Data(DataReceipt) = 1,
673 PromiseYield(ActionReceipt) = 2,
674 PromiseResume(DataReceipt) = 3,
675 GlobalContractDistribution(GlobalContractDistributionReceipt) = 4,
676 ActionV2(ActionReceiptV2) = 5,
677 PromiseYieldV2(ActionReceiptV2) = 6,
678}
679
680#[derive(
682 BorshSerialize,
683 BorshDeserialize,
684 Debug,
685 PartialEq,
686 Eq,
687 Clone,
688 serde::Serialize,
689 serde::Deserialize,
690 ProtocolSchema,
691)]
692#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
693pub struct ActionReceipt {
694 pub signer_id: AccountId,
696 pub signer_public_key: PublicKey,
698 pub gas_price: Balance,
700 pub output_data_receivers: Vec<DataReceiver>,
702 pub input_data_ids: Vec<CryptoHash>,
708 pub actions: Vec<Action>,
710}
711
712#[derive(
714 BorshSerialize,
715 BorshDeserialize,
716 Debug,
717 PartialEq,
718 Eq,
719 Clone,
720 serde::Serialize,
721 serde::Deserialize,
722 ProtocolSchema,
723)]
724#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
725pub struct ActionReceiptV2 {
726 pub signer_id: AccountId,
728 pub refund_to: Option<AccountId>,
730 pub signer_public_key: PublicKey,
732 pub gas_price: Balance,
734 pub output_data_receivers: Vec<DataReceiver>,
736 pub input_data_ids: Vec<CryptoHash>,
742 pub actions: Vec<Action>,
744}
745
746#[derive(Debug, PartialEq, Eq, Clone)]
749pub enum VersionedActionReceipt<'a> {
750 V1(Cow<'a, ActionReceipt>),
751 V2(Cow<'a, ActionReceiptV2>),
752}
753
754impl VersionedActionReceipt<'_> {
755 pub fn signer_id(&self) -> &AccountId {
757 match self {
758 VersionedActionReceipt::V1(action_receipt) => &action_receipt.signer_id,
759 VersionedActionReceipt::V2(action_receipt) => &action_receipt.signer_id,
760 }
761 }
762
763 pub fn refund_to(&self) -> &Option<AccountId> {
765 match self {
766 VersionedActionReceipt::V1(_) => &None,
767 VersionedActionReceipt::V2(action_receipt) => &action_receipt.refund_to,
768 }
769 }
770
771 pub fn signer_public_key(&self) -> &PublicKey {
773 match self {
774 VersionedActionReceipt::V1(action_receipt) => &action_receipt.signer_public_key,
775 VersionedActionReceipt::V2(action_receipt) => &action_receipt.signer_public_key,
776 }
777 }
778
779 pub fn gas_price(&self) -> Balance {
781 match self {
782 VersionedActionReceipt::V1(action_receipt) => action_receipt.gas_price,
783 VersionedActionReceipt::V2(action_receipt) => action_receipt.gas_price,
784 }
785 }
786
787 pub fn output_data_receivers(&self) -> &[DataReceiver] {
789 match self {
790 VersionedActionReceipt::V1(action_receipt) => &action_receipt.output_data_receivers,
791 VersionedActionReceipt::V2(action_receipt) => &action_receipt.output_data_receivers,
792 }
793 }
794
795 pub fn input_data_ids(&self) -> &[CryptoHash] {
801 match self {
802 VersionedActionReceipt::V1(action_receipt) => &action_receipt.input_data_ids,
803 VersionedActionReceipt::V2(action_receipt) => &action_receipt.input_data_ids,
804 }
805 }
806
807 pub fn actions(&self) -> &[Action] {
809 match self {
810 VersionedActionReceipt::V1(action_receipt) => &action_receipt.actions,
811 VersionedActionReceipt::V2(action_receipt) => &action_receipt.actions,
812 }
813 }
814}
815
816impl<'a> From<&'a ActionReceipt> for VersionedActionReceipt<'a> {
817 fn from(other: &'a ActionReceipt) -> Self {
818 VersionedActionReceipt::V1(Cow::Borrowed(other))
819 }
820}
821
822impl From<ActionReceipt> for VersionedActionReceipt<'_> {
823 fn from(other: ActionReceipt) -> Self {
824 VersionedActionReceipt::V1(Cow::Owned(other))
825 }
826}
827
828impl<'a> From<&'a ActionReceiptV2> for VersionedActionReceipt<'a> {
829 fn from(other: &'a ActionReceiptV2) -> Self {
830 VersionedActionReceipt::V2(Cow::Borrowed(other))
831 }
832}
833
834impl From<ActionReceiptV2> for VersionedActionReceipt<'_> {
835 fn from(other: ActionReceiptV2) -> Self {
836 VersionedActionReceipt::V2(Cow::Owned(other))
837 }
838}
839
840#[derive(Debug, PartialEq, Eq, Clone)]
849pub enum VersionedReceiptEnum<'a> {
850 Action(VersionedActionReceipt<'a>),
851 Data(Cow<'a, DataReceipt>),
852 PromiseYield(VersionedActionReceipt<'a>),
853 PromiseResume(Cow<'a, DataReceipt>),
854 GlobalContractDistribution(Cow<'a, GlobalContractDistributionReceipt>),
855}
856
857impl<'a> From<&'a ReceiptEnum> for VersionedReceiptEnum<'a> {
858 fn from(other: &'a ReceiptEnum) -> Self {
859 match other {
860 ReceiptEnum::Action(action_receipt) => {
861 VersionedReceiptEnum::Action(action_receipt.into())
862 }
863 ReceiptEnum::Data(data_receipt) => {
864 VersionedReceiptEnum::Data(Cow::Borrowed(data_receipt))
865 }
866 ReceiptEnum::PromiseYield(action_receipt) => {
867 VersionedReceiptEnum::PromiseYield(action_receipt.into())
868 }
869 ReceiptEnum::PromiseResume(data_receipt) => {
870 VersionedReceiptEnum::PromiseResume(Cow::Borrowed(data_receipt))
871 }
872 ReceiptEnum::GlobalContractDistribution(global_contract_distribution_receipt) => {
873 VersionedReceiptEnum::GlobalContractDistribution(Cow::Borrowed(
874 global_contract_distribution_receipt,
875 ))
876 }
877 ReceiptEnum::ActionV2(action_receipt) => {
878 VersionedReceiptEnum::Action(action_receipt.into())
879 }
880 ReceiptEnum::PromiseYieldV2(action_receipt) => {
881 VersionedReceiptEnum::PromiseYield(action_receipt.into())
882 }
883 }
884 }
885}
886
887impl<'a> From<ReceiptEnum> for VersionedReceiptEnum<'a> {
888 fn from(other: ReceiptEnum) -> Self {
889 match other {
890 ReceiptEnum::Action(action_receipt) => {
891 VersionedReceiptEnum::Action(action_receipt.into())
892 }
893 ReceiptEnum::Data(data_receipt) => VersionedReceiptEnum::Data(Cow::Owned(data_receipt)),
894 ReceiptEnum::PromiseYield(action_receipt) => {
895 VersionedReceiptEnum::PromiseYield(action_receipt.into())
896 }
897 ReceiptEnum::PromiseResume(data_receipt) => {
898 VersionedReceiptEnum::PromiseResume(Cow::Owned(data_receipt))
899 }
900 ReceiptEnum::GlobalContractDistribution(global_contract_distribution_receipt) => {
901 VersionedReceiptEnum::GlobalContractDistribution(Cow::Owned(
902 global_contract_distribution_receipt,
903 ))
904 }
905 ReceiptEnum::ActionV2(action_receipt) => {
906 VersionedReceiptEnum::Action(action_receipt.into())
907 }
908 ReceiptEnum::PromiseYieldV2(action_receipt) => {
909 VersionedReceiptEnum::PromiseYield(action_receipt.into())
910 }
911 }
912 }
913}
914
915#[serde_as]
918#[derive(
919 BorshSerialize,
920 BorshDeserialize,
921 Hash,
922 PartialEq,
923 Eq,
924 Clone,
925 serde::Serialize,
926 serde::Deserialize,
927 ProtocolSchema,
928)]
929#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
930pub struct DataReceipt {
931 pub data_id: CryptoHash,
932 #[serde_as(as = "Option<Base64>")]
933 #[cfg_attr(feature = "schemars", schemars(with = "Option<String>"))]
934 pub data: Option<Vec<u8>>,
935}
936
937impl fmt::Debug for DataReceipt {
938 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
939 f.debug_struct("DataReceipt")
940 .field("data_id", &self.data_id)
941 .field("data", &format_args!("{}", AbbrBytes(self.data.as_deref())))
942 .finish()
943 }
944}
945
946#[derive(BorshSerialize, BorshDeserialize, Hash, PartialEq, Eq, Clone, ProtocolSchema)]
951pub struct ReceivedData {
952 pub data: Option<Vec<u8>>,
953}
954
955impl fmt::Debug for ReceivedData {
956 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
957 f.debug_struct("ReceivedData")
958 .field("data", &format_args!("{}", AbbrBytes(self.data.as_deref())))
959 .finish()
960 }
961}
962
963#[derive(
964 BorshSerialize,
965 BorshDeserialize,
966 Hash,
967 PartialEq,
968 Eq,
969 Clone,
970 Debug,
971 serde::Deserialize,
972 serde::Serialize,
973 ProtocolSchema,
974)]
975#[borsh(use_discriminant = true)]
976#[repr(u8)]
977#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
978pub enum GlobalContractDistributionReceipt {
979 V1(GlobalContractDistributionReceiptV1) = 0,
980}
981
982impl GlobalContractDistributionReceipt {
983 pub fn new(
984 id: GlobalContractIdentifier,
985 target_shard: ShardId,
986 already_delivered_shards: Vec<ShardId>,
987 code: Arc<[u8]>,
988 ) -> Self {
989 Self::V1(GlobalContractDistributionReceiptV1 {
990 id,
991 target_shard,
992 already_delivered_shards,
993 code,
994 })
995 }
996
997 pub fn id(&self) -> &GlobalContractIdentifier {
998 match &self {
999 Self::V1(v1) => &v1.id,
1000 }
1001 }
1002
1003 pub fn target_shard(&self) -> ShardId {
1004 match &self {
1005 Self::V1(v1) => v1.target_shard,
1006 }
1007 }
1008
1009 pub fn already_delivered_shards(&self) -> &[ShardId] {
1010 match &self {
1011 Self::V1(v1) => &v1.already_delivered_shards,
1012 }
1013 }
1014
1015 pub fn code(&self) -> &Arc<[u8]> {
1016 match &self {
1017 Self::V1(v1) => &v1.code,
1018 }
1019 }
1020}
1021
1022#[serde_as]
1023#[derive(
1024 BorshSerialize,
1025 BorshDeserialize,
1026 Hash,
1027 PartialEq,
1028 Eq,
1029 Clone,
1030 serde::Deserialize,
1031 serde::Serialize,
1032 ProtocolSchema,
1033)]
1034#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
1035pub struct GlobalContractDistributionReceiptV1 {
1036 id: GlobalContractIdentifier,
1037 target_shard: ShardId,
1038 already_delivered_shards: Vec<ShardId>,
1039 #[serde_as(as = "Base64")]
1040 #[cfg_attr(feature = "schemars", schemars(with = "String"))]
1041 code: Arc<[u8]>,
1042}
1043
1044impl fmt::Debug for GlobalContractDistributionReceiptV1 {
1045 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1046 f.debug_struct("GlobalContractDistributionReceipt")
1047 .field("id", &self.id)
1048 .field("target_shard", &self.target_shard)
1049 .field("already_delivered_shards", &self.already_delivered_shards)
1050 .field("code", &..)
1051 .finish()
1052 }
1053}
1054
1055#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1057pub struct DelayedReceiptIndices {
1058 pub first_index: u64,
1060 pub next_available_index: u64,
1062}
1063
1064impl DelayedReceiptIndices {
1065 pub fn len(&self) -> u64 {
1066 self.next_available_index - self.first_index
1067 }
1068}
1069
1070#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1072pub struct PromiseYieldIndices {
1073 pub first_index: u64,
1075 pub next_available_index: u64,
1077}
1078
1079impl PromiseYieldIndices {
1080 pub fn len(&self) -> u64 {
1081 self.next_available_index - self.first_index
1082 }
1083}
1084
1085#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1087pub struct PromiseYieldTimeout {
1088 pub account_id: AccountId,
1090 pub data_id: CryptoHash,
1092 pub expires_at: BlockHeight,
1094}
1095
1096#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1098pub struct TrieQueueIndices {
1099 pub first_index: u64,
1101 pub next_available_index: u64,
1103}
1104
1105impl TrieQueueIndices {
1106 pub fn len(&self) -> u64 {
1107 self.next_available_index - self.first_index
1108 }
1109
1110 pub fn is_default(&self) -> bool {
1111 self.next_available_index == 0
1112 }
1113}
1114
1115impl From<DelayedReceiptIndices> for TrieQueueIndices {
1116 fn from(other: DelayedReceiptIndices) -> Self {
1117 Self { first_index: other.first_index, next_available_index: other.next_available_index }
1118 }
1119}
1120
1121impl From<TrieQueueIndices> for DelayedReceiptIndices {
1122 fn from(other: TrieQueueIndices) -> Self {
1123 Self { first_index: other.first_index, next_available_index: other.next_available_index }
1124 }
1125}
1126
1127#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1133pub struct BufferedReceiptIndices {
1134 pub shard_buffers: BTreeMap<ShardId, TrieQueueIndices>,
1135}
1136
1137pub type ReceiptResult = HashMap<ShardId, Vec<Receipt>>;
1139
1140#[cfg(test)]
1141mod tests {
1142 use super::*;
1143
1144 fn get_receipt_v0() -> Receipt {
1145 let receipt_v0 = Receipt::V0(ReceiptV0 {
1146 predecessor_id: "predecessor_id".parse().unwrap(),
1147 receiver_id: "receiver_id".parse().unwrap(),
1148 receipt_id: CryptoHash::default(),
1149 receipt: ReceiptEnum::Action(ActionReceipt {
1150 signer_id: "signer_id".parse().unwrap(),
1151 signer_public_key: PublicKey::empty(KeyType::ED25519),
1152 gas_price: Balance::ZERO,
1153 output_data_receivers: vec![],
1154 input_data_ids: vec![],
1155 actions: vec![Action::Transfer(TransferAction { deposit: Balance::ZERO })],
1156 }),
1157 });
1158 receipt_v0
1159 }
1160
1161 fn get_receipt_v1() -> Receipt {
1162 let receipt_v1 = Receipt::V1(ReceiptV1 {
1163 predecessor_id: "predecessor_id".parse().unwrap(),
1164 receiver_id: "receiver_id".parse().unwrap(),
1165 receipt_id: CryptoHash::default(),
1166 receipt: ReceiptEnum::Action(ActionReceipt {
1167 signer_id: "signer_id".parse().unwrap(),
1168 signer_public_key: PublicKey::empty(KeyType::ED25519),
1169 gas_price: Balance::ZERO,
1170 output_data_receivers: vec![],
1171 input_data_ids: vec![],
1172 actions: vec![Action::Transfer(TransferAction { deposit: Balance::ZERO })],
1173 }),
1174 priority: 1,
1175 });
1176 receipt_v1
1177 }
1178
1179 #[test]
1180 fn test_receipt_v0_serialization() {
1181 let receipt_v0 = get_receipt_v0();
1182 let serialized_receipt = borsh::to_vec(&receipt_v0).unwrap();
1183 let receipt2 = Receipt::try_from_slice(&serialized_receipt).unwrap();
1184 assert_eq!(receipt_v0, receipt2);
1185 }
1186
1187 #[test]
1188 fn test_receipt_v1_serialization() {
1189 let receipt_v1 = get_receipt_v1();
1190 let serialized_receipt = borsh::to_vec(&receipt_v1).unwrap();
1191 let receipt2 = Receipt::try_from_slice(&serialized_receipt).unwrap();
1192 assert_eq!(receipt_v1, receipt2);
1193 }
1194
1195 fn test_state_stored_receipt_serialization_impl(receipt: Receipt) {
1196 let metadata =
1197 StateStoredReceiptMetadata { congestion_gas: Gas::from_gas(42), congestion_size: 43 };
1198 let receipt = StateStoredReceipt::new_owned(receipt, metadata);
1199
1200 let serialized_receipt = borsh::to_vec(&receipt).unwrap();
1201 let deserialized_receipt = StateStoredReceipt::try_from_slice(&serialized_receipt).unwrap();
1202
1203 assert_eq!(receipt, deserialized_receipt);
1204 }
1205
1206 #[test]
1207 fn test_state_stored_receipt_serialization_v0() {
1208 let receipt = get_receipt_v0();
1209 test_state_stored_receipt_serialization_impl(receipt);
1210 }
1211
1212 #[test]
1213 fn test_state_stored_receipt_serialization_v1() {
1214 let receipt = get_receipt_v1();
1215 test_state_stored_receipt_serialization_impl(receipt);
1216 }
1217
1218 #[test]
1219 fn test_receipt_or_state_stored_receipt_serialization() {
1220 {
1223 let receipt = get_receipt_v0();
1224 let receipt = Cow::Owned(receipt);
1225
1226 let serialized_receipt = borsh::to_vec(&receipt).unwrap();
1227 let deserialized_receipt =
1228 ReceiptOrStateStoredReceipt::try_from_slice(&serialized_receipt).unwrap();
1229
1230 assert_eq!(ReceiptOrStateStoredReceipt::Receipt(receipt), deserialized_receipt);
1231 }
1232
1233 {
1236 let receipt = get_receipt_v1();
1237 let receipt = Cow::Owned(receipt);
1238
1239 let serialized_receipt = borsh::to_vec(&receipt).unwrap();
1240 let deserialized_receipt =
1241 ReceiptOrStateStoredReceipt::try_from_slice(&serialized_receipt).unwrap();
1242
1243 assert_eq!(ReceiptOrStateStoredReceipt::Receipt(receipt), deserialized_receipt);
1244 }
1245
1246 {
1249 let receipt = get_receipt_v0();
1250 let metadata = StateStoredReceiptMetadata {
1251 congestion_gas: Gas::from_gas(42),
1252 congestion_size: 43,
1253 };
1254 let state_stored_receipt = StateStoredReceipt::new_owned(receipt, metadata);
1255
1256 let serialized_receipt = borsh::to_vec(&state_stored_receipt).unwrap();
1257 let deserialized_receipt =
1258 ReceiptOrStateStoredReceipt::try_from_slice(&serialized_receipt).unwrap();
1259
1260 assert_eq!(
1261 ReceiptOrStateStoredReceipt::StateStoredReceipt(state_stored_receipt),
1262 deserialized_receipt
1263 );
1264 }
1265
1266 {
1269 let receipt = get_receipt_v0();
1270 let receipt = Cow::Owned(receipt);
1271
1272 let receipt_or_state_stored_receipt = ReceiptOrStateStoredReceipt::Receipt(receipt);
1273
1274 let serialized_receipt = borsh::to_vec(&receipt_or_state_stored_receipt).unwrap();
1275 let deserialized_receipt =
1276 ReceiptOrStateStoredReceipt::try_from_slice(&serialized_receipt).unwrap();
1277
1278 assert_eq!(receipt_or_state_stored_receipt, deserialized_receipt);
1279 }
1280
1281 {
1284 let receipt = get_receipt_v0();
1285 let metadata = StateStoredReceiptMetadata {
1286 congestion_gas: Gas::from_gas(42),
1287 congestion_size: 43,
1288 };
1289 let state_stored_receipt = StateStoredReceipt::new_owned(receipt, metadata);
1290 let receipt_or_state_stored_receipt =
1291 ReceiptOrStateStoredReceipt::StateStoredReceipt(state_stored_receipt);
1292
1293 let serialized_receipt = borsh::to_vec(&receipt_or_state_stored_receipt).unwrap();
1294 let deserialized_receipt =
1295 ReceiptOrStateStoredReceipt::try_from_slice(&serialized_receipt).unwrap();
1296
1297 assert_eq!(receipt_or_state_stored_receipt, deserialized_receipt);
1298 }
1299 }
1300}