unc_primitives/
receipt.rs

1use crate::hash::CryptoHash;
2use crate::serialize::dec_format;
3use crate::transaction::{Action, TransferAction};
4use crate::types::{AccountId, Balance, ShardId};
5use borsh::{BorshDeserialize, BorshSerialize};
6use serde_with::base64::Base64;
7use serde_with::serde_as;
8use std::borrow::Borrow;
9use std::collections::HashMap;
10use std::fmt;
11use unc_crypto::{KeyType, PublicKey};
12use unc_fmt::AbbrBytes;
13
14pub use unc_vm_runner::logic::DataReceiver;
15
16/// Receipts are used for a cross-shard communication.
17/// Receipts could be 2 types (determined by a `ReceiptEnum`): `ReceiptEnum::Action` of `ReceiptEnum::Data`.
18#[derive(
19    BorshSerialize,
20    BorshDeserialize,
21    Debug,
22    PartialEq,
23    Eq,
24    Clone,
25    serde::Serialize,
26    serde::Deserialize,
27)]
28pub struct Receipt {
29    /// An issuer account_id of a particular receipt.
30    /// `predecessor_id` could be either `Transaction` `signer_id` or intermediate contract's `account_id`.
31    pub predecessor_id: AccountId,
32    /// `receiver_id` is a receipt destination.
33    pub receiver_id: AccountId,
34    /// An unique id for the receipt
35    pub receipt_id: CryptoHash,
36    /// A receipt type
37    pub receipt: ReceiptEnum,
38}
39
40impl Borrow<CryptoHash> for Receipt {
41    fn borrow(&self) -> &CryptoHash {
42        &self.receipt_id
43    }
44}
45
46impl Receipt {
47    /// It's not a content hash, but receipt_id is unique.
48    pub fn get_hash(&self) -> CryptoHash {
49        self.receipt_id
50    }
51
52    /// Generates a receipt with a transfer from system for a given balance without a receipt_id.
53    /// This should be used for token refunds instead of gas refunds. It doesn't refund the
54    /// allowance of the access key. For gas refunds use `new_gas_refund`.
55    pub fn new_balance_refund(receiver_id: &AccountId, refund: Balance) -> Self {
56        Receipt {
57            predecessor_id: "system".parse().unwrap(),
58            receiver_id: receiver_id.clone(),
59            receipt_id: CryptoHash::default(),
60
61            receipt: ReceiptEnum::Action(ActionReceipt {
62                signer_id: "system".parse().unwrap(),
63                signer_public_key: PublicKey::empty(KeyType::ED25519),
64                gas_price: 0,
65                output_data_receivers: vec![],
66                input_data_ids: vec![],
67                actions: vec![Action::Transfer(TransferAction { deposit: refund })],
68            }),
69        }
70    }
71
72    /// Generates a receipt with a transfer action from system for a given balance without a
73    /// receipt_id. It contains `signer_id` and `signer_public_key` to indicate this is a gas
74    /// refund. The execution of this receipt will try to refund the allowance of the
75    /// access key with the given public key.
76    /// NOTE: The access key may be replaced by the owner, so the execution can't rely that the
77    /// access key is the same and it should use best effort for the refund.
78    pub fn new_gas_refund(
79        receiver_id: &AccountId,
80        refund: Balance,
81        signer_public_key: PublicKey,
82    ) -> Self {
83        Receipt {
84            predecessor_id: "system".parse().unwrap(),
85            receiver_id: receiver_id.clone(),
86            receipt_id: CryptoHash::default(),
87
88            receipt: ReceiptEnum::Action(ActionReceipt {
89                signer_id: receiver_id.clone(),
90                signer_public_key,
91                gas_price: 0,
92                output_data_receivers: vec![],
93                input_data_ids: vec![],
94                actions: vec![Action::Transfer(TransferAction { deposit: refund })],
95            }),
96        }
97    }
98}
99
100/// Receipt could be either ActionReceipt or DataReceipt
101#[derive(
102    BorshSerialize,
103    BorshDeserialize,
104    Clone,
105    Debug,
106    PartialEq,
107    Eq,
108    serde::Serialize,
109    serde::Deserialize,
110)]
111pub enum ReceiptEnum {
112    Action(ActionReceipt),
113    Data(DataReceipt),
114}
115
116/// ActionReceipt is derived from an Action from `Transaction or from Receipt`
117#[derive(
118    BorshSerialize,
119    BorshDeserialize,
120    Debug,
121    PartialEq,
122    Eq,
123    Clone,
124    serde::Serialize,
125    serde::Deserialize,
126)]
127pub struct ActionReceipt {
128    /// A signer of the original transaction
129    pub signer_id: AccountId,
130    /// An access key which was used to sign the original transaction
131    pub signer_public_key: PublicKey,
132    /// A gas_price which has been used to buy gas in the original transaction
133    #[serde(with = "dec_format")]
134    pub gas_price: Balance,
135    /// If present, where to route the output data
136    pub output_data_receivers: Vec<DataReceiver>,
137    /// A list of the input data dependencies for this Receipt to process.
138    /// If all `input_data_ids` for this receipt are delivered to the account
139    /// that means we have all the `ReceivedData` input which will be than converted to a
140    /// `PromiseResult::Successful(value)` or `PromiseResult::Failed`
141    /// depending on `ReceivedData` is `Some(_)` or `None`
142    pub input_data_ids: Vec<CryptoHash>,
143    /// A list of actions to process when all input_data_ids are filled
144    pub actions: Vec<Action>,
145}
146
147/// An incoming (ingress) `DataReceipt` which is going to a Receipt's `receiver` input_data_ids
148/// Which will be converted to `PromiseResult::Successful(value)` or `PromiseResult::Failed`
149#[serde_as]
150#[derive(
151    BorshSerialize,
152    BorshDeserialize,
153    Hash,
154    PartialEq,
155    Eq,
156    Clone,
157    serde::Serialize,
158    serde::Deserialize,
159)]
160pub struct DataReceipt {
161    pub data_id: CryptoHash,
162    #[serde_as(as = "Option<Base64>")]
163    pub data: Option<Vec<u8>>,
164}
165
166impl fmt::Debug for DataReceipt {
167    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
168        f.debug_struct("DataReceipt")
169            .field("data_id", &self.data_id)
170            .field("data", &format_args!("{}", AbbrBytes(self.data.as_deref())))
171            .finish()
172    }
173}
174
175/// A temporary data which is created by processing of DataReceipt
176/// stored in a state trie with a key = `account_id` + `data_id` until
177/// `input_data_ids` of all incoming Receipts are satisfied
178/// None means data retrieval was failed
179#[derive(BorshSerialize, BorshDeserialize, Hash, PartialEq, Eq, Clone)]
180pub struct ReceivedData {
181    pub data: Option<Vec<u8>>,
182}
183
184impl fmt::Debug for ReceivedData {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        f.debug_struct("ReceivedData")
187            .field("data", &format_args!("{}", AbbrBytes(self.data.as_deref())))
188            .finish()
189    }
190}
191
192/// Stores indices for a persistent queue for delayed receipts that didn't fit into a block.
193#[derive(Default, BorshSerialize, BorshDeserialize, Clone, PartialEq, Debug)]
194pub struct DelayedReceiptIndices {
195    // First inclusive index in the queue.
196    pub first_index: u64,
197    // Exclusive end index of the queue
198    pub next_available_index: u64,
199}
200
201impl DelayedReceiptIndices {
202    pub fn len(&self) -> u64 {
203        self.next_available_index - self.first_index
204    }
205}
206
207/// Map of shard to list of receipts to send to it.
208pub type ReceiptResult = HashMap<ShardId, Vec<Receipt>>;