tycho_types/models/transaction/
phases.rs

1use crate::cell::*;
2use crate::error::Error;
3use crate::models::account::StorageUsedShort;
4use crate::models::currency::CurrencyCollection;
5use crate::num::*;
6
7/// Storage phase info.
8///
9/// At this phase account pays for storing its state.
10#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
11#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
12pub struct StoragePhase {
13    /// Amount of tokens collected for storing this contract for some time.
14    pub storage_fees_collected: Tokens,
15    /// Amount of tokens which this account owes to the network
16    /// (if there was not enough balance to pay storage fee).
17    pub storage_fees_due: Option<Tokens>,
18    /// Account status change during execution of this phase.
19    pub status_change: AccountStatusChange,
20}
21
22/// Credit phase info.
23///
24/// At this phase message balance is added to the account balance.
25#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
26#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
27pub struct CreditPhase {
28    /// Amount of tokens paid for the debt.
29    pub due_fees_collected: Option<Tokens>,
30    /// Amount of tokens added to the account balance from the remaining
31    /// message balance.
32    pub credit: CurrencyCollection,
33}
34
35/// Compute phase info.
36///
37/// At this phase the VM is executed to produce a list of actions.
38#[derive(Debug, Clone, Eq, PartialEq)]
39#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
40#[cfg_attr(feature = "serde", serde(tag = "ty"))]
41pub enum ComputePhase {
42    /// Compute phase was skipped.
43    Skipped(SkippedComputePhase),
44    /// Compute phase was executed.
45    Executed(ExecutedComputePhase),
46}
47
48impl Store for ComputePhase {
49    fn store_into(
50        &self,
51        builder: &mut CellBuilder,
52        context: &dyn CellContext,
53    ) -> Result<(), Error> {
54        match self {
55            Self::Skipped(phase) => {
56                ok!(builder.store_bit_zero());
57                phase.store_into(builder, context)
58            }
59            Self::Executed(phase) => {
60                let cell = {
61                    let mut builder = CellBuilder::new();
62                    ok!(phase.gas_used.store_into(&mut builder, context));
63                    ok!(phase.gas_limit.store_into(&mut builder, context));
64                    ok!(phase.gas_credit.store_into(&mut builder, context));
65                    ok!(builder.store_u8(phase.mode as u8));
66                    ok!(builder.store_u32(phase.exit_code as u32));
67                    ok!(phase.exit_arg.store_into(&mut builder, context));
68                    ok!(builder.store_u32(phase.vm_steps));
69                    ok!(builder.store_u256(&phase.vm_init_state_hash));
70                    ok!(builder.store_u256(&phase.vm_final_state_hash));
71                    ok!(builder.build_ext(context))
72                };
73
74                let flags = 0b1000u8
75                    | ((phase.success as u8) << 2)
76                    | ((phase.msg_state_used as u8) << 1)
77                    | (phase.account_activated as u8);
78                ok!(builder.store_small_uint(flags, 4));
79                ok!(phase.gas_fees.store_into(builder, context));
80                builder.store_reference(cell)
81            }
82        }
83    }
84}
85
86impl<'a> Load<'a> for ComputePhase {
87    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
88        if !ok!(slice.load_bit()) {
89            return Ok(Self::Skipped(ok!(SkippedComputePhase::load_from(slice))));
90        }
91
92        let flags = ok!(slice.load_small_uint(3));
93        let gas_fees = ok!(Tokens::load_from(slice));
94
95        let slice = &mut ok!(slice.load_reference_as_slice());
96        Ok(Self::Executed(ExecutedComputePhase {
97            success: flags & 0b100 != 0,
98            msg_state_used: flags & 0b010 != 0,
99            account_activated: flags & 0b001 != 0,
100            gas_fees,
101            gas_used: ok!(VarUint56::load_from(slice)),
102            gas_limit: ok!(VarUint56::load_from(slice)),
103            gas_credit: ok!(Option::<VarUint24>::load_from(slice)),
104            mode: ok!(slice.load_u8()) as i8,
105            exit_code: ok!(slice.load_u32()) as i32,
106            exit_arg: ok!(Option::<i32>::load_from(slice)),
107            vm_steps: ok!(slice.load_u32()),
108            vm_init_state_hash: ok!(slice.load_u256()),
109            vm_final_state_hash: ok!(slice.load_u256()),
110        }))
111    }
112}
113
114/// Executed compute phase info.
115#[derive(Debug, Clone, Eq, PartialEq)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub struct ExecutedComputePhase {
118    /// Whether the execution was successful.
119    pub success: bool,
120    /// Whether the `init` from the incoming message was used.
121    pub msg_state_used: bool,
122    /// Whether the account state changed to `Active` during this phase.
123    pub account_activated: bool,
124    /// Total amount of tokens spent to execute this phase.
125    pub gas_fees: Tokens,
126    /// Amount of gas used by the VM to execute this phase.
127    pub gas_used: VarUint56,
128    /// Max gas amount which could be used.
129    pub gas_limit: VarUint56,
130    /// Max gas amount which could be used before accepting this transaction.
131    pub gas_credit: Option<VarUint24>,
132    /// Execution mode.
133    pub mode: i8,
134    /// VM exit code.
135    pub exit_code: i32,
136    /// Additional VM exit argument.
137    pub exit_arg: Option<i32>,
138    /// The number of VM steps it took to complete this phase.
139    pub vm_steps: u32,
140    /// Hash of the initial state of the VM.
141    pub vm_init_state_hash: HashBytes,
142    /// Hash of the VM state after executing this phase.
143    pub vm_final_state_hash: HashBytes,
144}
145
146/// Skipped compute phase info.
147#[derive(Debug, Copy, Clone, Eq, PartialEq, Store, Load)]
148#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
149pub struct SkippedComputePhase {
150    /// The reason this step was skipped.
151    pub reason: ComputePhaseSkipReason,
152}
153
154/// Enum with reasons for skipping compute phase.
155#[derive(Debug, Copy, Clone, Eq, PartialEq)]
156#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
157pub enum ComputePhaseSkipReason {
158    /// Contract doesn't have state to execute.
159    NoState,
160    /// Contract state is invalid.
161    BadState,
162    /// Not enough gas to execute compute phase.
163    NoGas,
164    /// Account was suspended by the config.
165    Suspended,
166}
167
168impl Store for ComputePhaseSkipReason {
169    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
170        let (tag, bits) = match self {
171            Self::NoState => (0b00, 2),
172            Self::BadState => (0b01, 2),
173            Self::NoGas => (0b10, 2),
174            Self::Suspended => (0b110, 3),
175        };
176        builder.store_small_uint(tag, bits)
177    }
178}
179
180impl<'a> Load<'a> for ComputePhaseSkipReason {
181    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
182        match slice.load_small_uint(2) {
183            Ok(0b00) => Ok(Self::NoState),
184            Ok(0b01) => Ok(Self::BadState),
185            Ok(0b10) => Ok(Self::NoGas),
186            Ok(_) => {
187                if ok!(slice.load_bit()) {
188                    // 0b11 -> 1
189                    Err(Error::InvalidTag)
190                } else {
191                    // 0b11 -> 0
192                    Ok(Self::Suspended)
193                }
194            }
195            Err(e) => Err(e),
196        }
197    }
198}
199
200/// Action phase info.
201///
202/// At this phase the list of actions from the compute phase
203/// is converted into updates and outgoing messages.
204#[derive(Debug, Clone, Eq, PartialEq)]
205#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
206pub struct ActionPhase {
207    /// Whether the execution was successful.
208    pub success: bool,
209    /// Whether the action list was valid.
210    pub valid: bool,
211    /// There were no funds to create an outgoing message.
212    pub no_funds: bool,
213    /// Account status change during execution of this phase.
214    pub status_change: AccountStatusChange,
215    /// Total forwarding fee for outgoing messages.
216    pub total_fwd_fees: Option<Tokens>,
217    /// Total fees for processing all actions.
218    pub total_action_fees: Option<Tokens>,
219    /// Result code of the phase.
220    pub result_code: i32,
221    /// Optional result argument of the phase.
222    pub result_arg: Option<i32>,
223    /// The total number of processed actions.
224    pub total_actions: u16,
225    /// The number of special actions (`ReserveCurrency`, `SetCode`, `ChangeLibrary`).
226    pub special_actions: u16,
227    /// The number of skipped actions.
228    pub skipped_actions: u16,
229    /// The number of outgoing messages created by the compute phase.
230    pub messages_created: u16,
231    /// The hash of the actions list.
232    pub action_list_hash: HashBytes,
233    /// The total number of unique cells (bits / refs) of produced messages.
234    pub total_message_size: StorageUsedShort,
235}
236
237impl Store for ActionPhase {
238    fn store_into(
239        &self,
240        builder: &mut CellBuilder,
241        context: &dyn CellContext,
242    ) -> Result<(), Error> {
243        let flags = ((self.success as u8) << 2) | ((self.valid as u8) << 1) | self.no_funds as u8;
244        let counts = ((self.total_actions as u64) << 48)
245            | ((self.special_actions as u64) << 32)
246            | ((self.skipped_actions as u64) << 16)
247            | self.messages_created as u64;
248
249        ok!(builder.store_small_uint(flags, 3));
250        ok!(self.status_change.store_into(builder, context));
251        ok!(self.total_fwd_fees.store_into(builder, context));
252        ok!(self.total_action_fees.store_into(builder, context));
253        ok!(builder.store_u32(self.result_code as u32));
254        ok!(self.result_arg.store_into(builder, context));
255        ok!(builder.store_u64(counts));
256        ok!(builder.store_u256(&self.action_list_hash));
257        self.total_message_size.store_into(builder, context)
258    }
259}
260
261impl<'a> Load<'a> for ActionPhase {
262    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
263        let flags = ok!(slice.load_small_uint(3));
264
265        let status_change = ok!(AccountStatusChange::load_from(slice));
266        let total_fwd_fees = ok!(Option::<Tokens>::load_from(slice));
267        let total_action_fees = ok!(Option::<Tokens>::load_from(slice));
268        let result_code = ok!(slice.load_u32()) as i32;
269        let result_arg = ok!(Option::<i32>::load_from(slice));
270
271        let counts = ok!(slice.load_u64());
272
273        Ok(Self {
274            success: flags & 0b100 != 0,
275            valid: flags & 0b010 != 0,
276            no_funds: flags & 0b001 != 0,
277            status_change,
278            total_fwd_fees,
279            total_action_fees,
280            result_code,
281            result_arg,
282            total_actions: (counts >> 48) as u16,
283            special_actions: (counts >> 32) as u16,
284            skipped_actions: (counts >> 16) as u16,
285            messages_created: counts as u16,
286            action_list_hash: ok!(slice.load_u256()),
287            total_message_size: ok!(StorageUsedShort::load_from(slice)),
288        })
289    }
290}
291
292/// Bounce phase info.
293///
294/// At this stage some funds are returned back to the sender.
295#[derive(Debug, Clone, Eq, PartialEq)]
296#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
297#[cfg_attr(feature = "serde", serde(tag = "ty"))]
298pub enum BouncePhase {
299    /// Default phase state.
300    ///
301    /// Probably unused.
302    NegativeFunds,
303    /// There were not enough funds to execute this phase.
304    NoFunds(NoFundsBouncePhase),
305    /// Bounce phase was executed.
306    Executed(ExecutedBouncePhase),
307}
308
309impl Store for BouncePhase {
310    fn store_into(
311        &self,
312        builder: &mut CellBuilder,
313        context: &dyn CellContext,
314    ) -> Result<(), Error> {
315        match self {
316            Self::NegativeFunds => builder.store_small_uint(0b00, 2),
317            Self::NoFunds(phase) => {
318                ok!(builder.store_small_uint(0b01, 2));
319                phase.store_into(builder, context)
320            }
321            Self::Executed(phase) => {
322                ok!(builder.store_bit_one());
323                phase.store_into(builder, context)
324            }
325        }
326    }
327}
328
329impl<'a> Load<'a> for BouncePhase {
330    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
331        Ok(if ok!(slice.load_bit()) {
332            match ExecutedBouncePhase::load_from(slice) {
333                Ok(phase) => Self::Executed(phase),
334                Err(e) => return Err(e),
335            }
336        } else if ok!(slice.load_bit()) {
337            match NoFundsBouncePhase::load_from(slice) {
338                Ok(phase) => Self::NoFunds(phase),
339                Err(e) => return Err(e),
340            }
341        } else {
342            Self::NegativeFunds
343        })
344    }
345}
346
347/// Skipped bounce phase info.
348#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
349#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
350pub struct NoFundsBouncePhase {
351    /// The total number of unique cells (bits / refs) of the bounced message.
352    pub msg_size: StorageUsedShort,
353    /// Required amount of tokens to send the bounced message.
354    pub req_fwd_fees: Tokens,
355}
356
357/// Executed bounce phase info.
358#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
359#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
360pub struct ExecutedBouncePhase {
361    /// The total number of unique cells (bits / refs) of the bounced message.
362    pub msg_size: StorageUsedShort,
363    /// The part of fees which fo to the validators.
364    pub msg_fees: Tokens,
365    /// Message forwarding fee.
366    pub fwd_fees: Tokens,
367}
368
369/// Account status change during transaction execution.
370#[derive(Debug, Clone, Copy, Eq, PartialEq)]
371#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
372pub enum AccountStatusChange {
373    /// Account status has not changed.
374    Unchanged = 0b0,
375    /// Account has been frozen.
376    Frozen = 0b10,
377    /// Account deleted.
378    Deleted = 0b11,
379}
380
381impl Store for AccountStatusChange {
382    fn store_into(&self, builder: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
383        if *self == Self::Unchanged {
384            builder.store_bit_zero()
385        } else {
386            builder.store_small_uint(*self as u8, 2)
387        }
388    }
389}
390
391impl<'a> Load<'a> for AccountStatusChange {
392    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
393        Ok(if !ok!(slice.load_bit()) {
394            Self::Unchanged
395        } else if ok!(slice.load_bit()) {
396            Self::Deleted
397        } else {
398            Self::Frozen
399        })
400    }
401}