1use num_bigint::BigUint;
4use tlb::{
5 Cell, Context, EitherInlineOrRef,
6 bits::{
7 NBits, NoArgs,
8 de::{BitReader, BitReaderExt, BitUnpack},
9 ser::{BitPack, BitWriter, BitWriterExt},
10 },
11 de::{CellDeserialize, CellParser, CellParserError},
12 hashmap::HashmapE,
13 ser::{CellBuilder, CellBuilderError, CellSerialize, CellSerializeExt},
14};
15
16use crate::{
17 MsgAddress,
18 currency::{CurrencyCollection, ExtraCurrencyCollection, Grams},
19 state_init::StateInit,
20};
21
22#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
29#[derive(Debug, Clone, PartialEq, Eq)]
30pub struct Message<T = Cell, IC = Cell, ID = Cell> {
31 pub info: CommonMsgInfo,
32 pub init: Option<StateInit<IC, ID>>,
33 pub body: T,
34}
35
36impl<T, IC, ID> Message<T, IC, ID>
37where
38 T: CellSerialize<Args: NoArgs>,
39 IC: CellSerialize<Args: NoArgs>,
40 ID: CellSerialize<Args: NoArgs>,
41{
42 #[inline]
43 pub fn with_state_init(mut self, state_init: impl Into<Option<StateInit<IC, ID>>>) -> Self {
44 self.init = state_init.into();
45 self
46 }
47
48 #[inline]
49 pub fn normalize(&self) -> Result<Message, CellBuilderError> {
50 Ok(Message {
51 info: self.info.clone(),
52 init: self.init.as_ref().map(StateInit::normalize).transpose()?,
53 body: self.body.to_cell(NoArgs::EMPTY)?,
54 })
55 }
56}
57
58impl Message<()> {
59 #[inline]
61 pub const fn transfer(dst: MsgAddress, grams: BigUint, bounce: bool) -> Self {
62 Self {
63 info: CommonMsgInfo::transfer(dst, grams, bounce),
64 init: None,
65 body: (),
66 }
67 }
68}
69
70impl<T, IC, ID> CellSerialize for Message<T, IC, ID>
71where
72 T: CellSerialize<Args: NoArgs>,
73 IC: CellSerialize<Args: NoArgs>,
74 ID: CellSerialize<Args: NoArgs>,
75{
76 type Args = ();
77
78 fn store(&self, builder: &mut CellBuilder, (): Self::Args) -> Result<(), CellBuilderError> {
79 builder
80 .store(&self.info, ())?
82 .store_as::<_, &Option<EitherInlineOrRef>>(&self.init, ())?
84 .store_as::<_, EitherInlineOrRef>(&self.body, NoArgs::EMPTY)?;
86 Ok(())
87 }
88}
89
90impl<'de, T, IC, ID> CellDeserialize<'de> for Message<T, IC, ID>
91where
92 T: CellDeserialize<'de, Args: NoArgs>,
93 IC: CellDeserialize<'de, Args: NoArgs>,
94 ID: CellDeserialize<'de, Args: NoArgs>,
95{
96 type Args = ();
97
98 fn parse(parser: &mut CellParser<'de>, (): Self::Args) -> Result<Self, CellParserError<'de>> {
99 Ok(Self {
100 info: parser.parse(()).context("info")?,
102 init: parser
104 .parse_as::<_, Option<EitherInlineOrRef>>(())
105 .context("init")?,
106 body: parser
108 .parse_as::<_, EitherInlineOrRef>(NoArgs::EMPTY)
109 .context("body")?,
110 })
111 }
112}
113
114#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
116#[derive(Debug, Clone, PartialEq, Eq)]
117pub enum CommonMsgInfo {
118 Internal(InternalMsgInfo),
122
123 ExternalIn(ExternalInMsgInfo),
127
128 ExternalOut(ExternalOutMsgInfo),
132}
133
134impl CommonMsgInfo {
135 #[inline]
136 pub const fn transfer(dst: MsgAddress, grams: BigUint, bounce: bool) -> Self {
137 Self::Internal(InternalMsgInfo::transfer(dst, grams, bounce))
138 }
139}
140
141impl CellSerialize for CommonMsgInfo {
142 type Args = ();
143
144 #[inline]
145 fn store(&self, builder: &mut CellBuilder, (): Self::Args) -> Result<(), CellBuilderError> {
146 match self {
147 Self::Internal(msg) => builder
148 .pack(false, ())?
150 .store(msg, ())?,
151 Self::ExternalIn(msg) => builder
152 .pack_as::<_, NBits<2>>(0b10, ())?
154 .pack(msg, ())?,
155 Self::ExternalOut(msg) => builder
156 .pack_as::<_, NBits<2>>(0b11, ())?
158 .pack(msg, ())?,
159 };
160 Ok(())
161 }
162}
163
164impl<'de> CellDeserialize<'de> for CommonMsgInfo {
165 type Args = ();
166
167 #[inline]
168 fn parse(parser: &mut CellParser<'de>, (): Self::Args) -> Result<Self, CellParserError<'de>> {
169 match parser.unpack(())? {
170 false => Ok(Self::Internal(parser.parse(()).context("int_msg_info")?)),
172 true => match parser.unpack(())? {
173 false => Ok(Self::ExternalIn(
175 parser.unpack(()).context("ext_in_msg_info")?,
176 )),
177 true => Ok(Self::ExternalOut(
179 parser.unpack(()).context("ext_out_msg_info")?,
180 )),
181 },
182 }
183 }
184}
185
186#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
194#[derive(Debug, Clone, PartialEq, Eq)]
195pub struct InternalMsgInfo {
196 pub ihr_disabled: bool,
198 pub bounce: bool,
201 pub bounced: bool,
203 pub src: MsgAddress,
205 pub dst: MsgAddress,
207 pub value: CurrencyCollection,
209 pub ihr_fee: BigUint,
211 pub fwd_fee: BigUint,
213 pub created_lt: u64,
215 pub created_at: u32,
217}
218
219impl InternalMsgInfo {
220 #[inline]
221 pub const fn transfer(dst: MsgAddress, grams: BigUint, bounce: bool) -> Self {
222 Self {
223 ihr_disabled: true,
224 bounce,
225 bounced: false,
226 src: MsgAddress::NULL,
227 dst,
228 value: CurrencyCollection {
229 grams,
230 other: ExtraCurrencyCollection(HashmapE::Empty),
231 },
232 ihr_fee: BigUint::ZERO,
233 fwd_fee: BigUint::ZERO,
234 created_lt: 0,
235 created_at: 0,
236 }
237 }
238}
239
240impl CellSerialize for InternalMsgInfo {
241 type Args = ();
242
243 fn store(&self, builder: &mut CellBuilder, (): Self::Args) -> Result<(), CellBuilderError> {
244 builder
245 .pack(self.ihr_disabled, ())?
246 .pack(self.bounce, ())?
247 .pack(self.bounced, ())?
248 .pack(self.src, ())?
249 .pack(self.dst, ())?
250 .store(&self.value, ())?
251 .pack_as::<_, &Grams>(&self.ihr_fee, ())?
252 .pack_as::<_, &Grams>(&self.fwd_fee, ())?
253 .pack(self.created_lt, ())?
254 .pack(self.created_at, ())?;
255 Ok(())
256 }
257}
258
259impl<'de> CellDeserialize<'de> for InternalMsgInfo {
260 type Args = ();
261
262 fn parse(parser: &mut CellParser<'de>, (): Self::Args) -> Result<Self, CellParserError<'de>> {
263 Ok(Self {
264 ihr_disabled: parser.unpack(())?,
265 bounce: parser.unpack(())?,
266 bounced: parser.unpack(())?,
267 src: parser.unpack(()).context("src")?,
268 dst: parser.unpack(()).context("dst")?,
269 value: parser.parse(()).context("value")?,
270 ihr_fee: parser.unpack_as::<_, Grams>(())?,
271 fwd_fee: parser.unpack_as::<_, Grams>(())?,
272 created_lt: parser.unpack(())?,
273 created_at: parser.unpack(())?,
274 })
275 }
276}
277
278#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
284#[derive(Debug, Clone, PartialEq, Eq)]
285pub struct ExternalInMsgInfo {
286 pub src: MsgAddress,
287 pub dst: MsgAddress,
288 pub import_fee: BigUint,
289}
290
291impl BitPack for ExternalInMsgInfo {
292 type Args = ();
293
294 fn pack<W>(&self, writer: &mut W, (): Self::Args) -> Result<(), W::Error>
295 where
296 W: BitWriter + ?Sized,
297 {
298 writer
299 .pack(self.src, ())?
300 .pack(self.dst, ())?
301 .pack_as::<_, &Grams>(&self.import_fee, ())?;
302 Ok(())
303 }
304}
305
306impl<'de> BitUnpack<'de> for ExternalInMsgInfo {
307 type Args = ();
308
309 fn unpack<R>(reader: &mut R, (): Self::Args) -> Result<Self, R::Error>
310 where
311 R: BitReader<'de> + ?Sized,
312 {
313 Ok(Self {
314 src: reader.unpack(())?,
315 dst: reader.unpack(())?,
316 import_fee: reader.unpack_as::<_, Grams>(())?,
317 })
318 }
319}
320
321#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
327#[derive(Debug, Clone, PartialEq, Eq)]
328pub struct ExternalOutMsgInfo {
329 pub src: MsgAddress,
330 pub dst: MsgAddress,
331 pub created_lt: u64,
332 pub created_at: u32,
333}
334
335impl BitPack for ExternalOutMsgInfo {
336 type Args = ();
337
338 fn pack<W>(&self, writer: &mut W, (): Self::Args) -> Result<(), W::Error>
339 where
340 W: BitWriter + ?Sized,
341 {
342 writer
343 .pack(self.src, ())?
344 .pack(self.dst, ())?
345 .pack(self.created_lt, ())?
346 .pack(self.created_at, ())?;
347 Ok(())
348 }
349}
350
351impl<'de> BitUnpack<'de> for ExternalOutMsgInfo {
352 type Args = ();
353
354 fn unpack<R>(reader: &mut R, (): Self::Args) -> Result<Self, R::Error>
355 where
356 R: BitReader<'de> + ?Sized,
357 {
358 Ok(Self {
359 src: reader.unpack(())?,
360 dst: reader.unpack(())?,
361 created_lt: reader.unpack(())?,
362 created_at: reader.unpack(())?,
363 })
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use tlb::ser::CellSerializeExt;
370
371 use super::*;
372
373 #[test]
374 fn message_serde() {
375 let msg = Message::<(), (), ()> {
376 info: CommonMsgInfo::Internal(InternalMsgInfo {
377 ihr_disabled: true,
378 bounce: true,
379 bounced: false,
380 src: MsgAddress::NULL,
381 dst: MsgAddress::NULL,
382 value: Default::default(),
383 ihr_fee: BigUint::ZERO,
384 fwd_fee: BigUint::ZERO,
385 created_lt: 0,
386 created_at: 0,
387 }),
388 init: None,
389 body: (),
390 };
391
392 let cell = msg.to_cell(()).unwrap();
393 let got: Message<(), (), ()> = cell.parse_fully(()).unwrap();
394
395 assert_eq!(got, msg);
396 }
397
398 #[test]
399 fn internal_msg_info_serde() {
400 let info = CommonMsgInfo::Internal(InternalMsgInfo {
401 ihr_disabled: true,
402 bounce: true,
403 bounced: false,
404 src: MsgAddress::NULL,
405 dst: MsgAddress::NULL,
406 value: Default::default(),
407 ihr_fee: BigUint::ZERO,
408 fwd_fee: BigUint::ZERO,
409 created_lt: 0,
410 created_at: 0,
411 });
412
413 let cell = info.to_cell(()).unwrap();
414 let got: CommonMsgInfo = cell.parse_fully(()).unwrap();
415
416 assert_eq!(got, info);
417 }
418
419 #[test]
420 fn external_in_msg_info_serde() {
421 let info = CommonMsgInfo::ExternalIn(ExternalInMsgInfo {
422 src: MsgAddress::NULL,
423 dst: MsgAddress::NULL,
424 import_fee: BigUint::ZERO,
425 });
426
427 let cell = info.to_cell(()).unwrap();
428 let got: CommonMsgInfo = cell.parse_fully(()).unwrap();
429
430 assert_eq!(got, info);
431 }
432
433 #[test]
434 fn external_out_msg_info_serde() {
435 let info = CommonMsgInfo::ExternalOut(ExternalOutMsgInfo {
436 src: MsgAddress::NULL,
437 dst: MsgAddress::NULL,
438 created_lt: 0,
439 created_at: 0,
440 });
441
442 let cell = info.to_cell(()).unwrap();
443 let got: CommonMsgInfo = cell.parse_fully(()).unwrap();
444
445 assert_eq!(got, info);
446 }
447}