near_primitives/
receipt.rs

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/// The outgoing (egress) data which will be transformed
23/// to a `DataReceipt` to be sent to a `receipt.receiver`
24#[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/// Receipts are used for a cross-shard communication.
43/// Receipts could be 2 types (determined by a `ReceiptEnum`): `ReceiptEnum::Action` of `ReceiptEnum::Data`.
44#[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    /// An issuer account_id of a particular receipt.
58    /// `predecessor_id` could be either `Transaction` `signer_id` or intermediate contract's `account_id`.
59    pub predecessor_id: AccountId,
60    /// `receiver_id` is a receipt destination.
61    pub receiver_id: AccountId,
62    /// An unique id for the receipt
63    pub receipt_id: CryptoHash,
64    /// A receipt type
65    pub receipt: ReceiptEnum,
66}
67
68/// DO NOT USE
69///
70/// `ReceiptV1` is not used, yet. It is only preparation for a possible future receipt priority.
71/// Therefore, most if not all code should keep using ReceiptV0, without the priority field.
72#[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    /// An issuer account_id of a particular receipt.
86    /// `predecessor_id` could be either `Transaction` `signer_id` or intermediate contract's `account_id`.
87    pub predecessor_id: AccountId,
88    /// `receiver_id` is a receipt destination.
89    pub receiver_id: AccountId,
90    /// An unique id for the receipt
91    pub receipt_id: CryptoHash,
92    /// A receipt type
93    pub receipt: ReceiptEnum,
94    /// Priority of a receipt
95    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/// A receipt that is stored in the state with added metadata. A receipt may be
107/// stored in the state as a delayed receipt, buffered receipt or a promise
108/// yield receipt. The metadata contains additional information about receipt
109///
110/// Please note that the StateStoredReceipt implements custom serialization and
111/// deserialization. Please see the comment on [ReceiptOrStateStoredReceipt]
112/// for more details.
113///
114/// This struct is versioned so that it can be enhanced in the future.
115#[derive(PartialEq, Eq, Debug, ProtocolSchema)]
116pub enum StateStoredReceipt<'a> {
117    V0(StateStoredReceiptV0<'a>),
118    V1(StateStoredReceiptV1<'a>),
119}
120
121/// The V0 of StateStoredReceipt. It contains the receipt and metadata.
122#[derive(BorshDeserialize, BorshSerialize, PartialEq, Eq, Debug, ProtocolSchema)]
123pub struct StateStoredReceiptV0<'a> {
124    /// The receipt.
125    pub receipt: Cow<'a, Receipt>,
126    pub metadata: StateStoredReceiptMetadata,
127}
128
129/// The V1 of StateStoredReceipt.
130/// The data is the same as in V0.
131/// Outgoing buffer metadata is updated only for versions V1 and higher.
132/// The receipts start being stored as V1 after the protocol change that introduced
133/// outgoing buffer metadata. Having a separate variant makes it clear whether the
134/// outgoing buffer metadata should be updated when a receipt is stored/removed.
135#[derive(BorshDeserialize, BorshSerialize, PartialEq, Eq, Debug, ProtocolSchema)]
136pub struct StateStoredReceiptV1<'a> {
137    pub receipt: Cow<'a, Receipt>,
138    pub metadata: StateStoredReceiptMetadata,
139}
140
141/// The metadata associated with the receipt stored in state.
142#[derive(BorshDeserialize, BorshSerialize, PartialEq, Eq, Debug, ProtocolSchema)]
143pub struct StateStoredReceiptMetadata {
144    /// The congestion gas of the receipt when it was stored in the state.
145    /// Please see [compute_receipt_congestion_gas] for more details.
146    pub congestion_gas: Gas,
147    /// The congestion size of the receipt when it was stored in the state.
148    /// Please see [compute_receipt_size] for more details.
149    pub congestion_size: u64,
150}
151
152/// The tag that is used to differentiate between the Receipt and StateStoredReceipt.
153const STATE_STORED_RECEIPT_TAG: u8 = u8::MAX;
154
155/// This is a convenience struct for handling the migration from [Receipt] to
156/// [StateStoredReceipt]. Both variants can be directly serialized and
157/// deserialized to this struct.
158///
159/// This structure is only meant as a migration vehicle and should not be used
160/// for other purposes. In order to make any changes to how receipts are stored
161/// in state the StateStoredReceipt should be used. It supports versioning.
162///
163/// The receipt in both variants is stored as a Cow to allow for both owned and
164/// borrowed ownership. The owned receipt should be used when pulling receipts
165/// from the state. The borrowed ownership can be used when pushing receipts
166/// into the state. In that case the receipt should never need to be cloned. The
167/// serialization only needs a reference.
168#[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    /// Deserialize based on the first and second bytes of the stream. For V0, we do backward compatible deserialization by deserializing
254    /// the entire stream into V0. For V1, we consume the first byte and then deserialize the rest.
255    fn deserialize_reader<R: Read>(reader: &mut R) -> std::io::Result<Self> {
256        // This is a ridiculous hackery: because the first field in `ReceiptV0` is an `AccountId`
257        // and an account id is at most 64 bytes, for all valid `ReceiptV0` the second byte must be 0
258        // because of the little endian encoding of the length of the account id.
259        // On the other hand, for `ReceiptV1`, since the first byte is 1 and an account id must have nonzero
260        // length, so the second byte must not be zero. Therefore, we can distinguish between the two versions
261        // by looking at the second byte.
262
263        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        // The serialization format for StateStored receipt is as follows:
282        // Byte 1: STATE_STORED_RECEIPT_TAG
283        // Byte 2: STATE_STORED_RECEIPT_TAG
284        // Byte 3: enum version (e.g. V0 => 0_u8)
285        // serialized variant value
286
287        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        // This is custom serialization in order to provide backwards
342        // compatibility for migration from Receipt to StateStoredReceipt.
343
344        // Please see the comment in deserialize_reader for more details.
345        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        // This is custom deserialization in order to provide backwards
359        // compatibility for migration from Receipt to StateStoredReceipt.
360
361        // Both variants (Receipt and StateStoredReceipt) need to be directly
362        // deserializable into the ReceiptOrStateStoredReceipt.
363
364        // Read the first two bytes in order to discriminate between the Receipt
365        // and StateStoredReceipt.
366        // The StateStored receipt has the tag as the first two bytes.
367        // The Receipt::V0 has 0 as the second byte.
368        // The Receipt::V1 has 1 as the first byte.
369        let u1 = u8::deserialize_reader(reader)?;
370        let u2 = u8::deserialize_reader(reader)?;
371
372        // Put the read bytes back into the reader by chaining.
373        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    /// Used in ReceiptV1
389    Priority(u64),
390    /// Used in ReceiptV0
391    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    /// It's not a content hash, but receipt_id is unique.
521    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                    // It is enough to send the receipt to the first child shard, it will be forwarded
547                    // to the rest of the children as part the receipt processing logic
548                    children_shards[0]
549                }
550            }
551        };
552        Ok(shard_id)
553    }
554
555    /// Generates a receipt with a transfer from system for a given balance without a receipt_id.
556    /// This should be used for token refunds instead of gas refunds. It inherits priority from the parent receipt.
557    /// It doesn't refund the allowance of the access key. For gas refunds use `new_gas_refund`.
558    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    /// Generates a receipt with a transfer action from system for a given balance without a
597    /// receipt_id. It contains `signer_id` and `signer_public_key` to indicate this is a gas
598    /// refund. The execution of this receipt will try to refund the allowance of the
599    /// access key with the given public key.
600    /// Gas refund does not inherit priority from its parent receipt and has no priority associated with it
601    /// NOTE: The access key may be replaced by the owner, so the execution can't rely that the
602    /// access key is the same and it should use best effort for the refund.
603    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/// Receipt could be either ActionReceipt or DataReceipt
656#[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/// ActionReceipt is derived from an Action from `Transaction or from Receipt`
681#[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    /// A signer of the original transaction
695    pub signer_id: AccountId,
696    /// An access key which was used to sign the original transaction
697    pub signer_public_key: PublicKey,
698    /// A gas_price which has been used to buy gas in the original transaction
699    pub gas_price: Balance,
700    /// If present, where to route the output data
701    pub output_data_receivers: Vec<DataReceiver>,
702    /// A list of the input data dependencies for this Receipt to process.
703    /// If all `input_data_ids` for this receipt are delivered to the account
704    /// that means we have all the `ReceivedData` input which will be than converted to a
705    /// `PromiseResult::Successful(value)` or `PromiseResult::Failed`
706    /// depending on `ReceivedData` is `Some(_)` or `None`
707    pub input_data_ids: Vec<CryptoHash>,
708    /// A list of actions to process when all input_data_ids are filled
709    pub actions: Vec<Action>,
710}
711
712/// ActionReceipt is derived from a set of Actions from `Transaction or from Receipt`
713#[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    /// A signer of the original transaction
727    pub signer_id: AccountId,
728    /// The receiver of any balance refunds form this receipt if it is different from receiver_id.
729    pub refund_to: Option<AccountId>,
730    /// An access key which was used to sign the original transaction
731    pub signer_public_key: PublicKey,
732    /// A gas_price which has been used to buy gas in the original transaction
733    pub gas_price: Balance,
734    /// If present, where to route the output data
735    pub output_data_receivers: Vec<DataReceiver>,
736    /// A list of the input data dependencies for this Receipt to process.
737    /// If all `input_data_ids` for this receipt are delivered to the account
738    /// that means we have all the `ReceivedData` input which will be than converted to a
739    /// `PromiseResult::Successful(value)` or `PromiseResult::Failed`
740    /// depending on `ReceivedData` is `Some(_)` or `None`
741    pub input_data_ids: Vec<CryptoHash>,
742    /// A list of actions to process when all input_data_ids are filled
743    pub actions: Vec<Action>,
744}
745
746/// Convenience wrapper for common logic accessing fields on action receipts of
747/// different versions.
748#[derive(Debug, PartialEq, Eq, Clone)]
749pub enum VersionedActionReceipt<'a> {
750    V1(Cow<'a, ActionReceipt>),
751    V2(Cow<'a, ActionReceiptV2>),
752}
753
754impl VersionedActionReceipt<'_> {
755    /// A signer of the original transaction
756    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    /// The receiver of any balance refunds form this receipt if it is different from receiver_id.
764    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    /// An access key which was used to sign the original transaction
772    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    /// A gas_price which has been used to buy gas in the original transaction
780    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    /// If present, where to route the output data
788    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    /// A list of the input data dependencies for this Receipt to process.
796    /// If all `input_data_ids` for this receipt are delivered to the account
797    /// that means we have all the `ReceivedData` input which will be than converted to a
798    /// `PromiseResult::Successful(value)` or `PromiseResult::Failed`
799    /// depending on `ReceivedData` is `Some(_)` or `None`
800    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    /// A list of actions to process when all input_data_ids are filled
808    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/// Convenience wrapper for common logic accessing fields on [`ReceiptEnum`] of
841/// different versions of its variants.
842///
843/// Note: So far, only action receipts are versioned. Other versioned variants
844/// can be added later if and when necessary. Then, the Cow pointer might be
845/// better pushed down, as done it [`VersionedActionReceipt`]. (Cow pointers are
846/// useful here to allow using the wrapper with owned and borrowed values
847/// without cloning.)
848#[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/// An incoming (ingress) `DataReceipt` which is going to a Receipt's `receiver` input_data_ids
916/// Which will be converted to `PromiseResult::Successful(value)` or `PromiseResult::Failed`
917#[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/// A temporary data which is created by processing of DataReceipt
947/// stored in a state trie with a key = `account_id` + `data_id` until
948/// `input_data_ids` of all incoming Receipts are satisfied
949/// None means data retrieval was failed
950#[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/// Stores indices for a persistent queue for delayed receipts that didn't fit into a block.
1056#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1057pub struct DelayedReceiptIndices {
1058    // First inclusive index in the queue.
1059    pub first_index: u64,
1060    // Exclusive end index of the queue
1061    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/// Stores indices for a persistent queue for PromiseYield timeouts.
1071#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1072pub struct PromiseYieldIndices {
1073    // First inclusive index in the queue.
1074    pub first_index: u64,
1075    // Exclusive end index of the queue
1076    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/// Entries in the queue of PromiseYield timeouts.
1086#[derive(BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1087pub struct PromiseYieldTimeout {
1088    /// The account on which the yielded promise was created
1089    pub account_id: AccountId,
1090    /// The `data_id` used to identify the awaited input data
1091    pub data_id: CryptoHash,
1092    /// The block height before which the data must be submitted
1093    pub expires_at: BlockHeight,
1094}
1095
1096/// Stores indices for a persistent queue in the state trie.
1097#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1098pub struct TrieQueueIndices {
1099    // First inclusive index in the queue.
1100    pub first_index: u64,
1101    // Exclusive end index of the queue
1102    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/// Stores indices for a persistent queue for buffered receipts that couldn't be
1128/// forwarded.
1129///
1130/// This is the singleton value stored in the `BUFFERED_RECEIPT_INDICES` trie
1131/// column.
1132#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug, ProtocolSchema)]
1133pub struct BufferedReceiptIndices {
1134    pub shard_buffers: BTreeMap<ShardId, TrieQueueIndices>,
1135}
1136
1137/// Map of shard to list of receipts to send to it.
1138pub 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        // Case 1:
1221        // Receipt V0 can be deserialized as ReceiptOrStateStoredReceipt
1222        {
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        // Case 2:
1234        // Receipt V1 can be deserialized as ReceiptOrStateStoredReceipt
1235        {
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        // Case 3:
1247        // StateStoredReceipt can be deserialized as ReceiptOrStateStoredReceipt
1248        {
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        // Case 4:
1267        // ReceiptOrStateStoredReceipt::Receipt
1268        {
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        // Case 5:
1282        // ReceiptOrStateStoredReceipt::StateStoredReceipt
1283        {
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}