Skip to main content

tycho_types/models/message/
mod.rs

1//! Message models.
2
3use std::borrow::Borrow;
4
5use bitflags::bitflags;
6
7pub use self::address::*;
8pub use self::envelope::*;
9pub use self::in_message::*;
10pub use self::out_message::*;
11use crate::cell::*;
12use crate::error::Error;
13use crate::models::account::StateInit;
14use crate::models::currency::CurrencyCollection;
15use crate::num::*;
16
17mod address;
18mod envelope;
19mod in_message;
20mod out_message;
21#[cfg(test)]
22mod tests;
23
24/// Blockchain message (with body as slice).
25pub type Message<'a> = BaseMessage<MsgInfo, CellSlice<'a>>;
26
27impl EquivalentRepr<OwnedMessage> for Message<'_> {}
28impl EquivalentRepr<RelaxedMessage<'_>> for Message<'_> {}
29impl EquivalentRepr<OwnedRelaxedMessage> for Message<'_> {}
30
31/// Blockchain message (with body as slice parts).
32pub type OwnedMessage = BaseMessage<MsgInfo, CellSliceParts>;
33
34impl EquivalentRepr<Message<'_>> for OwnedMessage {}
35impl EquivalentRepr<RelaxedMessage<'_>> for OwnedMessage {}
36impl EquivalentRepr<OwnedRelaxedMessage> for OwnedMessage {}
37
38/// Unfinished blockchain message (with body as slice).
39pub type RelaxedMessage<'a> = BaseMessage<RelaxedMsgInfo, CellSlice<'a>>;
40
41impl EquivalentRepr<Message<'_>> for RelaxedMessage<'_> {}
42impl EquivalentRepr<OwnedMessage> for RelaxedMessage<'_> {}
43impl EquivalentRepr<OwnedRelaxedMessage> for RelaxedMessage<'_> {}
44
45/// Unfinished blockchain message (with body as slice parts).
46pub type OwnedRelaxedMessage = BaseMessage<RelaxedMsgInfo, CellSliceParts>;
47
48impl EquivalentRepr<Message<'_>> for OwnedRelaxedMessage {}
49impl EquivalentRepr<OwnedMessage> for OwnedRelaxedMessage {}
50impl EquivalentRepr<RelaxedMessage<'_>> for OwnedRelaxedMessage {}
51
52/// Blockchain message.
53#[derive(Debug, Clone)]
54pub struct BaseMessage<I, B> {
55    /// Message info.
56    pub info: I,
57    /// Optional state init.
58    pub init: Option<StateInit>,
59    /// Optional payload.
60    pub body: B,
61    /// Optional message layout.
62    pub layout: Option<MessageLayout>,
63}
64
65#[cfg(feature = "serde")]
66impl<I, B> serde::Serialize for BaseMessage<I, B>
67where
68    I: ExactSize + Store + serde::Serialize,
69    B: ExactSize + StoreBody,
70{
71    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
72        use serde::ser::{Error, SerializeStruct};
73
74        struct BodySerializer<'a, B: StoreBody>(&'a B);
75
76        impl<B: StoreBody> serde::Serialize for BodySerializer<'_, B> {
77            fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
78                let mut builder = CellBuilder::new();
79                ok!(self
80                    .0
81                    .store_body(false, &mut builder, Cell::empty_context())
82                    .map_err(Error::custom));
83                let cell = ok!(builder.build().map_err(Error::custom));
84                crate::boc::Boc::serialize(&cell, serializer)
85            }
86        }
87
88        if serializer.is_human_readable() {
89            let mut ser = ok!(serializer.serialize_struct("Message", 4));
90            ok!(ser.serialize_field("info", &self.info));
91            ok!(ser.serialize_field("init", &self.init));
92            ok!(ser.serialize_field("body", &BodySerializer(&self.body)));
93            ok!(ser.serialize_field("layout", &self.layout));
94            ser.end()
95        } else {
96            crate::boc::BocRepr::serialize(self, serializer)
97        }
98    }
99}
100
101#[cfg(feature = "serde")]
102impl<'de, I> serde::Deserialize<'de> for BaseMessage<I, CellSliceParts>
103where
104    for<'a> I: ExactSize + Load<'a> + serde::Deserialize<'de>,
105{
106    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
107    where
108        D: serde::Deserializer<'de>,
109    {
110        #[derive(serde::Deserialize)]
111        struct Message<I> {
112            info: I,
113            #[serde(default)]
114            init: Option<StateInit>,
115            #[serde(with = "crate::boc::Boc", default)]
116            body: Cell,
117            #[serde(default)]
118            layout: Option<MessageLayout>,
119        }
120
121        if deserializer.is_human_readable() {
122            let msg = Message::deserialize(deserializer)?;
123            Ok(Self {
124                info: msg.info,
125                init: msg.init,
126                body: msg.body.into(),
127                layout: msg.layout,
128            })
129        } else {
130            crate::boc::BocRepr::deserialize(deserializer)
131        }
132    }
133}
134
135#[cfg(feature = "arbitrary")]
136impl<'a, I: arbitrary::Arbitrary<'a>> arbitrary::Arbitrary<'a> for BaseMessage<I, CellSliceParts> {
137    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
138        Ok(Self {
139            info: u.arbitrary()?,
140            init: u.arbitrary()?,
141            body: u.arbitrary::<Cell>()?.into(),
142            layout: u.arbitrary()?,
143        })
144    }
145
146    fn size_hint(depth: usize) -> (usize, Option<usize>) {
147        Self::try_size_hint(depth).unwrap_or_default()
148    }
149
150    fn try_size_hint(
151        depth: usize,
152    ) -> arbitrary::Result<(usize, Option<usize>), arbitrary::MaxRecursionReached> {
153        Ok(arbitrary::size_hint::and_all(&[
154            <I as arbitrary::Arbitrary>::try_size_hint(depth)?,
155            <Option<StateInit> as arbitrary::Arbitrary>::try_size_hint(depth)?,
156            <Cell as arbitrary::Arbitrary>::try_size_hint(depth)?,
157            <Option<MessageLayout> as arbitrary::Arbitrary>::try_size_hint(depth)?,
158        ]))
159    }
160}
161
162impl<I: Borrow<MsgInfo>, B> BaseMessage<I, B> {
163    /// Returns the type of this message.
164    pub fn ty(&self) -> MsgType {
165        self.info.borrow().ty()
166    }
167}
168
169impl<I: ExactSize, B: ExactSize> BaseMessage<I, B> {
170    /// Computes the most optimal layout of the message parts.
171    pub fn compute_layout(info: &I, init: Option<&StateInit>, body: &B) -> MessageLayout {
172        let (layout, ..) = MessageLayout::compute(info.exact_size(), init, body.exact_size());
173        layout
174    }
175}
176
177impl<I, B> Store for BaseMessage<I, B>
178where
179    I: Store + ExactSize,
180    B: StoreBody + ExactSize,
181{
182    fn store_into(
183        &self,
184        builder: &mut CellBuilder,
185        context: &dyn CellContext,
186    ) -> Result<(), Error> {
187        let info_size = self.info.exact_size();
188        let body_size = self.body.exact_size();
189        let (layout, size) = match self.layout {
190            Some(layout) => {
191                let size = layout.compute_full_size(info_size, self.init.as_ref(), body_size);
192                (layout, size)
193            }
194            None => MessageLayout::compute(info_size, self.init.as_ref(), body_size),
195        };
196        let Size { bits, refs } = size;
197
198        // Check capacity
199        if !builder.has_capacity(bits, refs) {
200            return Err(Error::CellOverflow);
201        }
202
203        // Try to store info
204        ok!(self.info.store_into(builder, context));
205
206        // Try to store init
207        ok!(match &self.init {
208            Some(value) => {
209                ok!(builder.store_bit_one()); // just$1
210                SliceOrCell {
211                    to_cell: layout.init_to_cell,
212                    value,
213                }
214                .store_into(builder, context)
215            }
216            None => builder.store_bit_zero(), // nothing$0
217        });
218
219        // Try to store body
220        ok!(builder.store_bit(layout.body_to_cell));
221        self.body.store_body(layout.body_to_cell, builder, context)
222    }
223}
224
225impl<'a, I, B> Load<'a> for BaseMessage<I, B>
226where
227    I: Load<'a>,
228    B: LoadBody<'a>,
229{
230    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
231        let info = ok!(I::load_from(slice));
232        let init = ok!(Option::<SliceOrCell<StateInit>>::load_from(slice));
233
234        let body_to_cell = ok!(slice.load_bit());
235        let body = ok!(B::load_body(body_to_cell, slice));
236
237        let (init, init_to_cell) = match init {
238            Some(SliceOrCell { to_cell, value }) => (Some(value), to_cell),
239            None => (None, false),
240        };
241
242        let layout = MessageLayout {
243            init_to_cell,
244            body_to_cell,
245        };
246
247        Ok(Self {
248            info,
249            init,
250            body,
251            layout: Some(layout),
252        })
253    }
254}
255
256trait StoreBody {
257    fn store_body(
258        &self,
259        to_cell: bool,
260        builder: &mut CellBuilder,
261        context: &dyn CellContext,
262    ) -> Result<(), Error>;
263}
264
265impl StoreBody for CellSlice<'_> {
266    fn store_body(
267        &self,
268        to_cell: bool,
269        builder: &mut CellBuilder,
270        context: &dyn CellContext,
271    ) -> Result<(), Error> {
272        SliceOrCell {
273            to_cell,
274            value: self,
275        }
276        .store_only_value_into(builder, context)
277    }
278}
279
280impl StoreBody for CellSliceParts {
281    fn store_body(
282        &self,
283        to_cell: bool,
284        builder: &mut CellBuilder,
285        context: &dyn CellContext,
286    ) -> Result<(), Error> {
287        let (range, cell) = self;
288        if to_cell && range.is_full(cell.as_ref()) {
289            builder.store_reference(cell.clone())
290        } else {
291            SliceOrCell {
292                to_cell,
293                value: ok!(range.apply(cell)),
294            }
295            .store_only_value_into(builder, context)
296        }
297    }
298}
299
300trait LoadBody<'a>: Sized {
301    fn load_body(from_cell: bool, slice: &mut CellSlice<'a>) -> Result<Self, Error>;
302}
303
304impl<'a> LoadBody<'a> for CellSlice<'a> {
305    fn load_body(from_cell: bool, slice: &mut CellSlice<'a>) -> Result<Self, Error> {
306        if from_cell {
307            slice.load_reference_as_slice()
308        } else {
309            Ok(slice.load_remaining())
310        }
311    }
312}
313
314impl<'a> LoadBody<'a> for CellSliceParts {
315    fn load_body(from_cell: bool, slice: &mut CellSlice<'a>) -> Result<Self, Error> {
316        if from_cell {
317            slice.load_reference_cloned()
318        } else {
319            let slice = slice.load_remaining();
320            let mut builder = CellBuilder::new();
321            ok!(builder.store_slice(slice));
322            builder.build()
323        }
324        .map(From::from)
325    }
326}
327
328struct SliceOrCell<T> {
329    to_cell: bool,
330    value: T,
331}
332
333impl<T: Store> SliceOrCell<T> {
334    fn store_only_value_into(
335        &self,
336        builder: &mut CellBuilder,
337        context: &dyn CellContext,
338    ) -> Result<(), Error> {
339        if self.to_cell {
340            let cell = {
341                let mut builder = CellBuilder::new();
342                ok!(self.value.store_into(&mut builder, context));
343                ok!(builder.build_ext(context))
344            };
345            builder.store_reference(cell)
346        } else {
347            self.value.store_into(builder, context)
348        }
349    }
350}
351
352impl<T: Store> Store for SliceOrCell<T> {
353    fn store_into(
354        &self,
355        builder: &mut CellBuilder,
356        context: &dyn CellContext,
357    ) -> Result<(), Error> {
358        ok!(builder.store_bit(self.to_cell));
359        self.store_only_value_into(builder, context)
360    }
361}
362
363impl<'a, T: Load<'a>> Load<'a> for SliceOrCell<T> {
364    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
365        let to_cell = ok!(slice.load_bit());
366
367        let mut child_cell = if to_cell {
368            Some(ok!(slice.load_reference_as_slice()))
369        } else {
370            None
371        };
372
373        let slice = match &mut child_cell {
374            Some(slice) => slice,
375            None => slice,
376        };
377
378        Ok(Self {
379            to_cell,
380            value: ok!(T::load_from(slice)),
381        })
382    }
383}
384
385/// Message payload layout.
386#[derive(Debug, Copy, Clone, Eq, PartialEq)]
387#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
388#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
389pub struct MessageLayout {
390    /// Whether to store state init in a child cell.
391    pub init_to_cell: bool,
392    /// Whether to store payload as a child cell.
393    pub body_to_cell: bool,
394}
395
396impl MessageLayout {
397    /// Returns a plain message layout (init and body stored in the root cell).
398    #[inline]
399    pub const fn plain() -> Self {
400        Self {
401            init_to_cell: false,
402            body_to_cell: false,
403        }
404    }
405
406    /// Computes the number of bits and refs for this layout for the root cell.
407    pub const fn compute_full_size(
408        &self,
409        info_size: Size,
410        init: Option<&StateInit>,
411        body_size: Size,
412    ) -> Size {
413        let l = DetailedMessageLayout::compute(info_size, init, body_size);
414
415        let mut total = l.info;
416
417        // Append init bits and refs
418        if self.init_to_cell {
419            total.refs += 1;
420        } else {
421            total.bits += l.init.bits;
422            total.refs += l.init.refs;
423        }
424
425        // Append body bits and refs
426        if self.body_to_cell {
427            total.refs += 1;
428        } else {
429            total.bits += l.body.bits;
430            total.refs += l.body.refs;
431        }
432
433        total
434    }
435
436    /// Computes the most optimal layout of the message parts.
437    /// Also returns the number of bits and refs for the root cell.
438    pub const fn compute(
439        info_size: Size,
440        init: Option<&StateInit>,
441        body_size: Size,
442    ) -> (Self, Size) {
443        let l = DetailedMessageLayout::compute(info_size, init, body_size);
444
445        // Try plain layout
446        let total_bits = l.info.bits + l.init.bits + l.body.bits;
447        let total_refs = l.info.refs + l.init.refs + l.body.refs;
448        if total_bits <= MAX_BIT_LEN && total_refs <= MAX_REF_COUNT as u8 {
449            let layout = Self {
450                init_to_cell: false,
451                body_to_cell: false,
452            };
453            return (layout, Size {
454                bits: total_bits,
455                refs: total_refs,
456            });
457        }
458
459        // Try body to ref
460        let total_bits = l.info.bits + l.init.bits;
461        let total_refs = l.info.refs + l.init.refs;
462        if total_bits <= MAX_BIT_LEN && total_refs < MAX_REF_COUNT as u8 {
463            let layout = Self {
464                init_to_cell: false,
465                body_to_cell: true,
466            };
467            return (layout, Size {
468                bits: total_bits,
469                refs: total_refs + 1,
470            });
471        }
472
473        // Try init to ref
474        let total_bits = l.info.bits + l.body.bits;
475        let total_refs = l.info.refs + l.body.refs;
476        if total_bits <= MAX_BIT_LEN && total_refs < MAX_REF_COUNT as u8 {
477            let layout = Self {
478                init_to_cell: true,
479                body_to_cell: false,
480            };
481            return (layout, Size {
482                bits: total_bits,
483                refs: total_refs + 1,
484            });
485        }
486
487        // Fallback to init and body to ref
488        let layout = Self {
489            init_to_cell: true,
490            body_to_cell: true,
491        };
492        (layout, Size {
493            bits: l.info.bits,
494            refs: l.info.refs + 2,
495        })
496    }
497}
498
499struct DetailedMessageLayout {
500    info: Size,
501    init: Size,
502    body: Size,
503}
504
505impl DetailedMessageLayout {
506    const fn compute(mut info: Size, init: Option<&StateInit>, body: Size) -> Self {
507        info.bits += 2; // (Maybe X) (1bit) + (Either X) (1bit)
508
509        let init = match init {
510            Some(init) => {
511                info.bits += 1; // (Either X) (1bit)
512                init.exact_size_const()
513            }
514            None => Size::ZERO,
515        };
516
517        Self { info, init, body }
518    }
519}
520
521/// Unfinalized message info.
522#[derive(Debug, Clone, Eq, PartialEq)]
523#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
524#[cfg_attr(feature = "serde", serde(tag = "ty"))]
525#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
526pub enum RelaxedMsgInfo {
527    /// Internal message info,
528    Int(RelaxedIntMsgInfo),
529    /// External outgoing message info,
530    ExtOut(RelaxedExtOutMsgInfo),
531}
532
533impl RelaxedMsgInfo {
534    /// Exact size of this value when it is stored in slice.
535    pub const fn exact_size_const(&self) -> Size {
536        Size {
537            bits: self.bit_len(),
538            refs: self.has_references() as u8,
539        }
540    }
541
542    /// Returns the number of data bits that this struct occupies.
543    const fn bit_len(&self) -> u16 {
544        match self {
545            Self::Int(info) => info.bit_len(),
546            Self::ExtOut(info) => info.bit_len(),
547        }
548    }
549
550    const fn has_references(&self) -> bool {
551        match self {
552            Self::Int(info) => !info.value.other.is_empty(),
553            _ => false,
554        }
555    }
556}
557
558impl From<RelaxedIntMsgInfo> for RelaxedMsgInfo {
559    #[inline]
560    fn from(info: RelaxedIntMsgInfo) -> Self {
561        Self::Int(info)
562    }
563}
564
565impl From<RelaxedExtOutMsgInfo> for RelaxedMsgInfo {
566    #[inline]
567    fn from(info: RelaxedExtOutMsgInfo) -> Self {
568        Self::ExtOut(info)
569    }
570}
571
572impl From<IntMsgInfo> for RelaxedMsgInfo {
573    #[inline]
574    fn from(info: IntMsgInfo) -> Self {
575        Self::Int(RelaxedIntMsgInfo {
576            ihr_disabled: info.ihr_disabled,
577            bounce: info.bounce,
578            bounced: info.bounced,
579            src: Some(info.src),
580            dst: info.dst,
581            value: info.value,
582            extra_flags: info.extra_flags,
583            fwd_fee: info.fwd_fee,
584            created_lt: info.created_lt,
585            created_at: info.created_at,
586        })
587    }
588}
589
590impl From<ExtOutMsgInfo> for RelaxedMsgInfo {
591    #[inline]
592    fn from(info: ExtOutMsgInfo) -> Self {
593        Self::ExtOut(RelaxedExtOutMsgInfo {
594            src: Some(info.src),
595            dst: info.dst,
596            created_lt: info.created_lt,
597            created_at: info.created_at,
598        })
599    }
600}
601
602impl ExactSize for RelaxedMsgInfo {
603    #[inline]
604    fn exact_size(&self) -> Size {
605        self.exact_size_const()
606    }
607}
608
609impl Store for RelaxedMsgInfo {
610    fn store_into(
611        &self,
612        builder: &mut CellBuilder,
613        context: &dyn CellContext,
614    ) -> Result<(), Error> {
615        match self {
616            Self::Int(info) => {
617                ok!(builder.store_bit_zero());
618                info.store_into(builder, context)
619            }
620            Self::ExtOut(info) => {
621                ok!(builder.store_small_uint(0b11, 2));
622                info.store_into(builder, context)
623            }
624        }
625    }
626}
627
628impl<'a> Load<'a> for RelaxedMsgInfo {
629    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
630        Ok(if !ok!(slice.load_bit()) {
631            match RelaxedIntMsgInfo::load_from(slice) {
632                Ok(info) => Self::Int(info),
633                Err(e) => return Err(e),
634            }
635        } else if ok!(slice.load_bit()) {
636            match RelaxedExtOutMsgInfo::load_from(slice) {
637                Ok(info) => Self::ExtOut(info),
638                Err(e) => return Err(e),
639            }
640        } else {
641            return Err(Error::InvalidTag);
642        })
643    }
644}
645
646/// Message type.
647#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
648#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
649pub enum MsgType {
650    /// Internal message.
651    Int,
652    /// External incoming message.
653    ExtIn,
654    /// External outgoing message.
655    ExtOut,
656}
657
658impl MsgType {
659    /// Returns whether this message is internal.
660    pub const fn is_internal(&self) -> bool {
661        matches!(self, Self::Int)
662    }
663
664    /// Returns whether this message is external incoming.
665    pub const fn is_external_in(&self) -> bool {
666        matches!(self, Self::ExtIn)
667    }
668
669    /// Returns whether this message is external outgoing.
670    pub const fn is_external_out(&self) -> bool {
671        matches!(self, Self::ExtOut)
672    }
673}
674
675impl Store for MsgType {
676    fn store_into(&self, b: &mut CellBuilder, _: &dyn CellContext) -> Result<(), Error> {
677        match self {
678            Self::Int => b.store_bit_zero(),
679            Self::ExtIn => b.store_small_uint(0b10, 2),
680            Self::ExtOut => b.store_small_uint(0b11, 2),
681        }
682    }
683}
684
685impl<'a> Load<'a> for MsgType {
686    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
687        Ok(if !ok!(slice.load_bit()) {
688            Self::Int
689        } else if !ok!(slice.load_bit()) {
690            Self::ExtIn
691        } else {
692            Self::ExtOut
693        })
694    }
695}
696
697/// Message info.
698#[derive(Debug, Clone, Eq, PartialEq)]
699#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
700#[cfg_attr(feature = "serde", serde(tag = "ty"))]
701#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
702pub enum MsgInfo {
703    /// Internal message info,
704    Int(IntMsgInfo),
705    /// External incoming message info.
706    ExtIn(ExtInMsgInfo),
707    /// External outgoing message info,
708    ExtOut(ExtOutMsgInfo),
709}
710
711impl MsgInfo {
712    /// Returns the type of this message info.
713    pub const fn ty(&self) -> MsgType {
714        match self {
715            Self::Int(_) => MsgType::Int,
716            Self::ExtIn(_) => MsgType::ExtIn,
717            Self::ExtOut(_) => MsgType::ExtOut,
718        }
719    }
720
721    /// Returns whether this message is internal.
722    pub const fn is_internal(&self) -> bool {
723        matches!(self, Self::Int(_))
724    }
725
726    /// Returns whether this message is external incoming.
727    pub const fn is_external_in(&self) -> bool {
728        matches!(self, Self::ExtIn(_))
729    }
730
731    /// Returns whether this message is external outgoing.
732    pub const fn is_external_out(&self) -> bool {
733        matches!(self, Self::ExtOut(_))
734    }
735
736    /// Exact size of this value when it is stored in slice.
737    pub const fn exact_size_const(&self) -> Size {
738        Size {
739            bits: self.bit_len(),
740            refs: self.has_references() as u8,
741        }
742    }
743
744    /// Returns the number of data bits that this struct occupies.
745    const fn bit_len(&self) -> u16 {
746        match self {
747            Self::Int(info) => info.bit_len(),
748            Self::ExtIn(info) => info.bit_len(),
749            Self::ExtOut(info) => info.bit_len(),
750        }
751    }
752
753    const fn has_references(&self) -> bool {
754        match self {
755            Self::Int(info) => !info.value.other.is_empty(),
756            _ => false,
757        }
758    }
759}
760
761impl ExactSize for MsgInfo {
762    #[inline]
763    fn exact_size(&self) -> Size {
764        self.exact_size_const()
765    }
766}
767
768impl Store for MsgInfo {
769    fn store_into(
770        &self,
771        builder: &mut CellBuilder,
772        context: &dyn CellContext,
773    ) -> Result<(), Error> {
774        match self {
775            Self::Int(info) => {
776                ok!(builder.store_bit_zero());
777                info.store_into(builder, context)
778            }
779            Self::ExtIn(info) => {
780                ok!(builder.store_small_uint(0b10, 2));
781                info.store_into(builder, context)
782            }
783            Self::ExtOut(info) => {
784                ok!(builder.store_small_uint(0b11, 2));
785                info.store_into(builder, context)
786            }
787        }
788    }
789}
790
791impl<'a> Load<'a> for MsgInfo {
792    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
793        Ok(if !ok!(slice.load_bit()) {
794            match IntMsgInfo::load_from(slice) {
795                Ok(info) => Self::Int(info),
796                Err(e) => return Err(e),
797            }
798        } else if !ok!(slice.load_bit()) {
799            match ExtInMsgInfo::load_from(slice) {
800                Ok(info) => Self::ExtIn(info),
801                Err(e) => return Err(e),
802            }
803        } else {
804            match ExtOutMsgInfo::load_from(slice) {
805                Ok(info) => Self::ExtOut(info),
806                Err(e) => return Err(e),
807            }
808        })
809    }
810}
811
812impl From<IntMsgInfo> for MsgInfo {
813    #[inline]
814    fn from(value: IntMsgInfo) -> Self {
815        Self::Int(value)
816    }
817}
818
819impl From<ExtInMsgInfo> for MsgInfo {
820    #[inline]
821    fn from(value: ExtInMsgInfo) -> Self {
822        Self::ExtIn(value)
823    }
824}
825
826impl From<ExtOutMsgInfo> for MsgInfo {
827    #[inline]
828    fn from(value: ExtOutMsgInfo) -> Self {
829        Self::ExtOut(value)
830    }
831}
832
833/// Internal message info.
834#[derive(Debug, Clone, Eq, PartialEq)]
835#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
836#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
837pub struct IntMsgInfo {
838    /// Whether IHR is disabled for the message.
839    pub ihr_disabled: bool,
840    /// Whether to bounce this message back if the destination transaction fails.
841    pub bounce: bool,
842    /// Whether this message is a bounced message from some failed transaction.
843    pub bounced: bool,
844    /// Internal source address.
845    pub src: IntAddr,
846    /// Internal destination address.
847    pub dst: IntAddr,
848    /// Attached amounts.
849    pub value: CurrencyCollection,
850    /// Extra flags. Enables v12 bounce format for the message.
851    pub extra_flags: MessageExtraFlags,
852    /// Forwarding fee paid for using the routing.
853    pub fwd_fee: Tokens,
854    /// Logical time when the message was created.
855    pub created_lt: u64,
856    /// Unix timestamp when the message was created.
857    pub created_at: u32,
858}
859
860impl Default for IntMsgInfo {
861    fn default() -> Self {
862        Self {
863            ihr_disabled: true,
864            bounce: false,
865            bounced: false,
866            src: Default::default(),
867            dst: Default::default(),
868            value: CurrencyCollection::ZERO,
869            extra_flags: Default::default(),
870            fwd_fee: Default::default(),
871            created_lt: 0,
872            created_at: 0,
873        }
874    }
875}
876
877impl IntMsgInfo {
878    /// Returns the number of data bits that this struct occupies.
879    pub const fn bit_len(&self) -> u16 {
880        3 + self.src.bit_len()
881            + self.dst.bit_len()
882            + self.value.bit_len()
883            + self.extra_flags.bit_len()
884            + self.fwd_fee.unwrap_bit_len()
885            + 64
886            + 32
887    }
888}
889
890impl Store for IntMsgInfo {
891    fn store_into(
892        &self,
893        builder: &mut CellBuilder,
894        context: &dyn CellContext,
895    ) -> Result<(), Error> {
896        let flags =
897            ((self.ihr_disabled as u8) << 2) | ((self.bounce as u8) << 1) | self.bounced as u8;
898        ok!(builder.store_small_uint(flags, 3));
899        ok!(self.src.store_into(builder, context));
900        ok!(self.dst.store_into(builder, context));
901        ok!(self.value.store_into(builder, context));
902        ok!(self.extra_flags.store_into(builder, context));
903        ok!(self.fwd_fee.store_into(builder, context));
904        ok!(builder.store_u64(self.created_lt));
905        builder.store_u32(self.created_at)
906    }
907}
908
909impl<'a> Load<'a> for IntMsgInfo {
910    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
911        let flags = ok!(slice.load_small_uint(3));
912        Ok(Self {
913            ihr_disabled: flags & 0b100 != 0,
914            bounce: flags & 0b010 != 0,
915            bounced: flags & 0b001 != 0,
916            src: ok!(IntAddr::load_from(slice)),
917            dst: ok!(IntAddr::load_from(slice)),
918            value: ok!(CurrencyCollection::load_from(slice)),
919            extra_flags: ok!(MessageExtraFlags::load_from(slice)),
920            fwd_fee: ok!(Tokens::load_from(slice)),
921            created_lt: ok!(slice.load_u64()),
922            created_at: ok!(slice.load_u32()),
923        })
924    }
925}
926
927/// Unfinished internal message info.
928#[derive(Debug, Clone, Eq, PartialEq)]
929#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
930#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
931pub struct RelaxedIntMsgInfo {
932    /// Whether IHR is disabled for the message.
933    pub ihr_disabled: bool,
934    /// Whether to bounce this message back if the destination transaction fails.
935    pub bounce: bool,
936    /// Whether this message is a bounced message from some failed transaction.
937    pub bounced: bool,
938    /// Optional internal source address.
939    pub src: Option<IntAddr>,
940    /// Internal destination address.
941    pub dst: IntAddr,
942    /// Attached amounts.
943    pub value: CurrencyCollection,
944    /// Extra flags. Enables v12 bounce format for the message.
945    pub extra_flags: MessageExtraFlags,
946    /// Forwarding fee paid for using the routing.
947    pub fwd_fee: Tokens,
948    /// Logical time when the message was created.
949    pub created_lt: u64,
950    /// Unix timestamp when the message was created.
951    pub created_at: u32,
952}
953
954impl Default for RelaxedIntMsgInfo {
955    fn default() -> Self {
956        Self {
957            ihr_disabled: true,
958            bounce: false,
959            bounced: false,
960            src: Default::default(),
961            dst: Default::default(),
962            value: CurrencyCollection::ZERO,
963            extra_flags: Default::default(),
964            fwd_fee: Default::default(),
965            created_lt: 0,
966            created_at: 0,
967        }
968    }
969}
970
971impl RelaxedIntMsgInfo {
972    /// Returns the number of data bits that this struct occupies.
973    pub const fn bit_len(&self) -> u16 {
974        3 + compute_opt_int_addr_bit_len(&self.src)
975            + self.dst.bit_len()
976            + self.value.bit_len()
977            + self.extra_flags.bit_len()
978            + self.fwd_fee.unwrap_bit_len()
979            + 64
980            + 32
981    }
982}
983
984impl Store for RelaxedIntMsgInfo {
985    fn store_into(
986        &self,
987        builder: &mut CellBuilder,
988        context: &dyn CellContext,
989    ) -> Result<(), Error> {
990        let flags =
991            ((self.ihr_disabled as u8) << 2) | ((self.bounce as u8) << 1) | self.bounced as u8;
992        ok!(builder.store_small_uint(flags, 3));
993        ok!(store_opt_int_addr(builder, context, &self.src));
994        ok!(self.dst.store_into(builder, context));
995        ok!(self.value.store_into(builder, context));
996        ok!(self.extra_flags.store_into(builder, context));
997        ok!(self.fwd_fee.store_into(builder, context));
998        ok!(builder.store_u64(self.created_lt));
999        builder.store_u32(self.created_at)
1000    }
1001}
1002
1003impl<'a> Load<'a> for RelaxedIntMsgInfo {
1004    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1005        let flags = ok!(slice.load_small_uint(3));
1006        Ok(Self {
1007            ihr_disabled: flags & 0b100 != 0,
1008            bounce: flags & 0b010 != 0,
1009            bounced: flags & 0b001 != 0,
1010            src: ok!(load_opt_int_addr(slice)),
1011            dst: ok!(IntAddr::load_from(slice)),
1012            value: ok!(CurrencyCollection::load_from(slice)),
1013            extra_flags: ok!(MessageExtraFlags::load_from(slice)),
1014            fwd_fee: ok!(Tokens::load_from(slice)),
1015            created_lt: ok!(slice.load_u64()),
1016            created_at: ok!(slice.load_u32()),
1017        })
1018    }
1019}
1020
1021/// External incoming message info.
1022#[derive(Debug, Default, Clone, Eq, PartialEq)]
1023#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1024#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1025pub struct ExtInMsgInfo {
1026    /// Optional external source address.
1027    #[cfg_attr(
1028        feature = "serde",
1029        serde(default, skip_serializing_if = "Option::is_none")
1030    )]
1031    pub src: Option<ExtAddr>,
1032    /// Internal destination address.
1033    pub dst: IntAddr,
1034    /// External message import fee.
1035    ///
1036    /// NOTE: currently unused and reserved for future use.
1037    pub import_fee: Tokens,
1038}
1039
1040impl ExtInMsgInfo {
1041    /// Returns the number of data bits that this struct occupies.
1042    pub const fn bit_len(&self) -> u16 {
1043        2 + compute_ext_addr_bit_len(&self.src)
1044            + self.dst.bit_len()
1045            + self.import_fee.unwrap_bit_len()
1046    }
1047}
1048
1049impl Store for ExtInMsgInfo {
1050    fn store_into(
1051        &self,
1052        builder: &mut CellBuilder,
1053        context: &dyn CellContext,
1054    ) -> Result<(), Error> {
1055        if !self.import_fee.is_valid() {
1056            return Err(Error::InvalidData);
1057        }
1058        if !builder.has_capacity(self.bit_len(), 0) {
1059            return Err(Error::CellOverflow);
1060        }
1061        ok!(store_ext_addr(builder, context, &self.src));
1062        ok!(self.dst.store_into(builder, context));
1063        self.import_fee.store_into(builder, context)
1064    }
1065}
1066
1067impl<'a> Load<'a> for ExtInMsgInfo {
1068    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1069        Ok(Self {
1070            src: ok!(load_ext_addr(slice)),
1071            dst: ok!(IntAddr::load_from(slice)),
1072            import_fee: ok!(Tokens::load_from(slice)),
1073        })
1074    }
1075}
1076
1077/// External outgoing message info.
1078#[derive(Debug, Default, Clone, Eq, PartialEq)]
1079#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1080#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1081pub struct ExtOutMsgInfo {
1082    /// Internal source address.
1083    pub src: IntAddr,
1084    /// Optional external address.
1085    #[cfg_attr(
1086        feature = "serde",
1087        serde(default, skip_serializing_if = "Option::is_none")
1088    )]
1089    pub dst: Option<ExtAddr>,
1090    /// Logical time when the message was created.
1091    pub created_lt: u64,
1092    /// Unix timestamp when the message was created.
1093    pub created_at: u32,
1094}
1095
1096impl ExtOutMsgInfo {
1097    /// Returns the number of data bits that this struct occupies.
1098    pub const fn bit_len(&self) -> u16 {
1099        2 + self.src.bit_len() + compute_ext_addr_bit_len(&self.dst) + 64 + 32
1100    }
1101}
1102
1103impl Store for ExtOutMsgInfo {
1104    fn store_into(
1105        &self,
1106        builder: &mut CellBuilder,
1107        context: &dyn CellContext,
1108    ) -> Result<(), Error> {
1109        if !builder.has_capacity(self.bit_len(), 0) {
1110            return Err(Error::CellOverflow);
1111        }
1112        ok!(self.src.store_into(builder, context));
1113        ok!(store_ext_addr(builder, context, &self.dst));
1114        ok!(builder.store_u64(self.created_lt));
1115        builder.store_u32(self.created_at)
1116    }
1117}
1118
1119impl<'a> Load<'a> for ExtOutMsgInfo {
1120    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1121        Ok(Self {
1122            src: ok!(IntAddr::load_from(slice)),
1123            dst: ok!(load_ext_addr(slice)),
1124            created_lt: ok!(slice.load_u64()),
1125            created_at: ok!(slice.load_u32()),
1126        })
1127    }
1128}
1129
1130/// Unfinalized external outgoing message info.
1131#[derive(Debug, Default, Clone, Eq, PartialEq)]
1132#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1133#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
1134pub struct RelaxedExtOutMsgInfo {
1135    /// Optional internal source address.
1136    pub src: Option<IntAddr>,
1137    /// Optional external address.
1138    #[cfg_attr(
1139        feature = "serde",
1140        serde(default, skip_serializing_if = "Option::is_none")
1141    )]
1142    pub dst: Option<ExtAddr>,
1143    /// Logical time when the message was created.
1144    pub created_lt: u64,
1145    /// Unix timestamp when the message was created.
1146    pub created_at: u32,
1147}
1148
1149impl RelaxedExtOutMsgInfo {
1150    /// Returns the number of data bits that this struct occupies.
1151    pub const fn bit_len(&self) -> u16 {
1152        2 + compute_opt_int_addr_bit_len(&self.src) + compute_ext_addr_bit_len(&self.dst) + 64 + 32
1153    }
1154}
1155
1156impl Store for RelaxedExtOutMsgInfo {
1157    fn store_into(
1158        &self,
1159        builder: &mut CellBuilder,
1160        context: &dyn CellContext,
1161    ) -> Result<(), Error> {
1162        ok!(store_opt_int_addr(builder, context, &self.src));
1163        ok!(store_ext_addr(builder, context, &self.dst));
1164        ok!(builder.store_u64(self.created_lt));
1165        builder.store_u32(self.created_at)
1166    }
1167}
1168
1169impl<'a> Load<'a> for RelaxedExtOutMsgInfo {
1170    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1171        Ok(Self {
1172            src: ok!(load_opt_int_addr(slice)),
1173            dst: ok!(load_ext_addr(slice)),
1174            created_lt: ok!(slice.load_u64()),
1175            created_at: ok!(slice.load_u32()),
1176        })
1177    }
1178}
1179
1180const fn compute_ext_addr_bit_len(addr: &Option<ExtAddr>) -> u16 {
1181    match addr {
1182        Some(addr) => 2 + addr.bit_len(),
1183        None => 2,
1184    }
1185}
1186
1187fn store_ext_addr(
1188    builder: &mut CellBuilder,
1189    context: &dyn CellContext,
1190    addr: &Option<ExtAddr>,
1191) -> Result<(), Error> {
1192    match addr {
1193        None => builder.store_zeros(2),
1194        Some(ExtAddr { data_bit_len, data }) => {
1195            if !builder.has_capacity(2 + Uint9::BITS + data_bit_len.into_inner(), 0) {
1196                return Err(Error::CellOverflow);
1197            }
1198            ok!(builder.store_bit_zero());
1199            ok!(builder.store_bit_one());
1200            ok!(data_bit_len.store_into(builder, context));
1201            builder.store_raw(data, data_bit_len.into_inner())
1202        }
1203    }
1204}
1205
1206fn load_ext_addr(slice: &mut CellSlice<'_>) -> Result<Option<ExtAddr>, Error> {
1207    if ok!(slice.load_bit()) {
1208        return Err(Error::InvalidTag);
1209    }
1210
1211    if !ok!(slice.load_bit()) {
1212        return Ok(None);
1213    }
1214
1215    let data_bit_len = ok!(Uint9::load_from(slice));
1216    if !slice.has_remaining(data_bit_len.into_inner(), 0) {
1217        return Err(Error::CellUnderflow);
1218    }
1219
1220    let mut data = vec![0; data_bit_len.into_inner().div_ceil(8) as usize];
1221    ok!(slice.load_raw(&mut data, data_bit_len.into_inner()));
1222    Ok(Some(ExtAddr { data_bit_len, data }))
1223}
1224
1225const fn compute_opt_int_addr_bit_len(addr: &Option<IntAddr>) -> u16 {
1226    match addr {
1227        Some(src) => src.bit_len(),
1228        None => 2,
1229    }
1230}
1231
1232fn store_opt_int_addr(
1233    builder: &mut CellBuilder,
1234    context: &dyn CellContext,
1235    addr: &Option<IntAddr>,
1236) -> Result<(), Error> {
1237    match addr {
1238        Some(addr) => addr.store_into(builder, context),
1239        None => builder.store_zeros(2),
1240    }
1241}
1242
1243fn load_opt_int_addr(slice: &mut CellSlice<'_>) -> Result<Option<IntAddr>, Error> {
1244    if ok!(slice.get_bit(0)) {
1245        IntAddr::load_from(slice).map(Some)
1246    } else if ok!(slice.load_small_uint(2)) == 0b00 {
1247        Ok(None)
1248    } else {
1249        Err(Error::InvalidTag)
1250    }
1251}
1252
1253bitflags! {
1254    /// TVM v12 message bounce flags.
1255    #[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
1256    pub struct MessageExtraFlags: u8 {
1257        /// Whether new bounce format is expected.
1258        const NEW_BOUNCE_FORMAT = 0b01;
1259        /// Whether full body should be attached to the bounced message.
1260        const FULL_BODY_IN_BOUNCED = 0b10;
1261    }
1262}
1263
1264impl MessageExtraFlags {
1265    /// Converts physical representation into flags (if well-formed).
1266    #[inline]
1267    pub const fn from_stored(tokens: Tokens) -> Option<Self> {
1268        let value = tokens.into_inner();
1269        if value <= u8::MAX as u128 {
1270            Self::from_bits(value as u8)
1271        } else {
1272            None
1273        }
1274    }
1275
1276    /// Convert flags into its physical representation (varint).
1277    #[inline]
1278    pub const fn as_stored(&self) -> Tokens {
1279        Tokens::new(self.bits() as _)
1280    }
1281
1282    /// Returns `true` if new bounce format is enabled.
1283    #[inline]
1284    pub const fn is_new_bounce_format(&self) -> bool {
1285        self.contains(Self::NEW_BOUNCE_FORMAT)
1286    }
1287
1288    /// Returns `true` if extended info should be attached in case of bounce.
1289    #[inline]
1290    pub const fn is_full_body_in_bounced(&self) -> bool {
1291        self.contains(Self::FULL_BODY_IN_BOUNCED)
1292    }
1293
1294    /// Returns the number of data bits that this struct occupies.
1295    pub const fn bit_len(&self) -> u16 {
1296        Tokens::LEN_BITS + 8 * (self.bits() != 0) as u16
1297    }
1298}
1299
1300impl Store for MessageExtraFlags {
1301    #[inline]
1302    fn store_into(
1303        &self,
1304        builder: &mut CellBuilder,
1305        context: &dyn CellContext,
1306    ) -> Result<(), Error> {
1307        self.as_stored().store_into(builder, context)
1308    }
1309}
1310
1311impl<'a> Load<'a> for MessageExtraFlags {
1312    #[inline]
1313    fn load_from(slice: &mut CellSlice<'a>) -> Result<Self, Error> {
1314        Self::from_stored(Tokens::load_from(slice)?).ok_or(Error::InvalidData)
1315    }
1316}
1317
1318#[cfg(feature = "serde")]
1319impl serde::Serialize for MessageExtraFlags {
1320    #[inline]
1321    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
1322        SerdeMessageExtraFlags {
1323            new_bounce_format: self.contains(Self::NEW_BOUNCE_FORMAT),
1324            full_body_in_bounced: self.contains(Self::FULL_BODY_IN_BOUNCED),
1325        }
1326        .serialize(serializer)
1327    }
1328}
1329
1330#[cfg(feature = "serde")]
1331impl<'de> serde::Deserialize<'de> for MessageExtraFlags {
1332    #[inline]
1333    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
1334        let flags = SerdeMessageExtraFlags::deserialize(deserializer)?;
1335        let mut result = Self::empty();
1336        result.set(Self::NEW_BOUNCE_FORMAT, flags.new_bounce_format);
1337        result.set(Self::FULL_BODY_IN_BOUNCED, flags.full_body_in_bounced);
1338        Ok(result)
1339    }
1340}
1341
1342#[cfg(feature = "serde")]
1343#[derive(serde::Serialize, serde::Deserialize)]
1344struct SerdeMessageExtraFlags {
1345    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1346    new_bounce_format: bool,
1347    #[serde(default, skip_serializing_if = "std::ops::Not::not")]
1348    full_body_in_bounced: bool,
1349}
1350
1351#[cfg(feature = "arbitrary")]
1352impl<'a> arbitrary::Arbitrary<'a> for MessageExtraFlags {
1353    #[inline]
1354    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
1355        u.arbitrary::<u8>().map(Self::from_bits_truncate)
1356    }
1357}
1358
1359/// New bounce message body.
1360#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
1361#[tlb(tag = "#fffffffe")]
1362pub struct NewBounceBody {
1363    /// Cell that contains the body of the original message.
1364    /// If [`MessageExtraFlags::is_full_body_in_bounced`] returns true,
1365    /// then the whole body is returned, otherwise it is only the root without refs.
1366    pub original_body: Cell,
1367    /// Original message info
1368    pub original_info: Lazy<NewBounceOriginalInfo>,
1369    /// Id of a failed phase.
1370    ///  - `0` - compute phase was skipped. [`exit_code`] denotes the skip reason:
1371    ///     - `exit_code = -1` - no state (account is uninit or frozen, and no state init is present in the message).
1372    ///     - `exit_code = -2` - bad state (account is uninit or frozen, and state init in the message has the wrong hash).
1373    ///     - `exit_code = -3` - no gas.
1374    ///     - `exit_code = -4` - account is suspended.
1375    /// - `1` - compute phase failed. `exit_code` is the value from the compute phase.
1376    /// - `2` - action phase failed. `exit_code` is the value from the action phase.
1377    ///
1378    /// [`exit_code`]: NewBounceBody::exit_code
1379    pub bounced_by_phase: u8,
1380    /// Exit code (or a skip reason if compute phase was skipped).
1381    pub exit_code: i32,
1382    /// Compute phase info
1383    pub compute_phase: Option<NewBounceComputePhaseInfo>,
1384}
1385
1386impl NewBounceBody {
1387    /// State of [`NewBounceBody::bounced_by_phase`] when compute phase was skipped.
1388    pub const COMPUTE_PHASE_SKIPPED: u8 = 0;
1389    /// State of [`NewBounceBody::bounced_by_phase`] when compute phase failed.
1390    pub const COMPUTE_PHASE_FAILED: u8 = 1;
1391    /// State of [`NewBounceBody::bounced_by_phase`] when action phase failed.
1392    pub const ACTION_PHASE_FAILED: u8 = 2;
1393
1394    /// State of [`NewBounceBody::exit_code`] when there was no state.
1395    pub const EXIT_CODE_NO_STATE: i32 = -1;
1396    /// State of [`NewBounceBody::exit_code`] when there was an invalid state.
1397    pub const EXIT_CODE_BAD_STATE: i32 = -2;
1398    /// State of [`NewBounceBody::exit_code`] when there was not enough gas.
1399    pub const EXIT_CODE_NO_GAS: i32 = -3;
1400    /// State of [`NewBounceBody::exit_code`] when the account was suspended.
1401    pub const EXIT_CODE_SUSPENDED: i32 = -4;
1402
1403    /// Get a structured bounce reason info (if well-formed).
1404    pub const fn bounce_reason(&self) -> Option<BounceReason> {
1405        Some(match self.bounced_by_phase {
1406            0 => BounceReason::ComputePhaseSkipped(match self.exit_code {
1407                Self::EXIT_CODE_NO_STATE => {
1408                    crate::models::transaction::ComputePhaseSkipReason::NoState
1409                }
1410                Self::EXIT_CODE_BAD_STATE => {
1411                    crate::models::transaction::ComputePhaseSkipReason::BadState
1412                }
1413                Self::EXIT_CODE_NO_GAS => crate::models::transaction::ComputePhaseSkipReason::NoGas,
1414                Self::EXIT_CODE_SUSPENDED => {
1415                    crate::models::transaction::ComputePhaseSkipReason::Suspended
1416                }
1417                _ => return None,
1418            }),
1419            1 => BounceReason::ComputePhaseFailed {
1420                exit_code: self.exit_code,
1421            },
1422            2 => BounceReason::ActionPhaseFailed {
1423                result_code: self.exit_code,
1424            },
1425            _ => return None,
1426        })
1427    }
1428}
1429
1430/// Structured bounce reason derived from [`NewBounceBody`].
1431#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1432pub enum BounceReason {
1433    /// Compute phase was skipped.
1434    ComputePhaseSkipped(crate::models::transaction::ComputePhaseSkipReason),
1435    /// Compute phase failed.
1436    ComputePhaseFailed {
1437        /// Exit code of the compute phase.
1438        exit_code: i32,
1439    },
1440    /// Action phase failed.
1441    ActionPhaseFailed {
1442        /// Result code of the action phase.
1443        result_code: i32,
1444    },
1445}
1446
1447impl BounceReason {
1448    /// Flattens the struct into a tuple of [`NewBounceBody::bounced_by_phase`]
1449    /// and [`NewBounceBody::exit_code`].
1450    pub const fn flatten(&self) -> (u8, i32) {
1451        match self {
1452            Self::ComputePhaseSkipped(reason) => {
1453                let exit_code = match reason {
1454                    crate::models::ComputePhaseSkipReason::NoState => {
1455                        NewBounceBody::EXIT_CODE_NO_STATE
1456                    }
1457                    crate::models::ComputePhaseSkipReason::BadState => {
1458                        NewBounceBody::EXIT_CODE_BAD_STATE
1459                    }
1460                    crate::models::ComputePhaseSkipReason::NoGas => NewBounceBody::EXIT_CODE_NO_GAS,
1461                    crate::models::ComputePhaseSkipReason::Suspended => {
1462                        NewBounceBody::EXIT_CODE_SUSPENDED
1463                    }
1464                };
1465                (NewBounceBody::COMPUTE_PHASE_SKIPPED, exit_code)
1466            }
1467            Self::ComputePhaseFailed { exit_code } => {
1468                (NewBounceBody::COMPUTE_PHASE_FAILED, *exit_code)
1469            }
1470            Self::ActionPhaseFailed { result_code } => {
1471                (NewBounceBody::ACTION_PHASE_FAILED, *result_code)
1472            }
1473        }
1474    }
1475}
1476
1477/// Original message info
1478#[derive(Default, Debug, Clone, Eq, PartialEq, Store, Load)]
1479pub struct NewBounceOriginalInfo {
1480    /// Original message info.
1481    pub value: CurrencyCollection,
1482    /// Original created lt.
1483    pub created_lt: u64,
1484    /// Original created at.
1485    pub created_at: u32,
1486}
1487
1488/// Compute phase info.
1489#[derive(Clone, Debug, Default, Eq, PartialEq, Store, Load)]
1490pub struct NewBounceComputePhaseInfo {
1491    /// Gas used in compute phase
1492    pub gas_used: u32,
1493    /// Performed VM steps during compute phase.
1494    pub vm_steps: u32,
1495}