everscale_types/models/transaction/
phases.rs

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