1use chrono::{DateTime, Utc};
3use 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, UnixTimestamp,
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 #[cfg_attr(
217 feature = "arbitrary",
218 arbitrary(with = UnixTimestamp::arbitrary_option)
219 )]
220 pub created_at: Option<DateTime<Utc>>,
221}
222
223impl InternalMsgInfo {
224 #[inline]
225 pub const fn transfer(dst: MsgAddress, grams: BigUint, bounce: bool) -> Self {
226 InternalMsgInfo {
227 ihr_disabled: true,
228 bounce,
229 bounced: false,
230 src: MsgAddress::NULL,
231 dst,
232 value: CurrencyCollection {
233 grams,
234 other: ExtraCurrencyCollection(HashmapE::Empty),
235 },
236 ihr_fee: BigUint::ZERO,
237 fwd_fee: BigUint::ZERO,
238 created_lt: 0,
239 created_at: None,
240 }
241 }
242}
243
244impl CellSerialize for InternalMsgInfo {
245 type Args = ();
246
247 fn store(&self, builder: &mut CellBuilder, _: Self::Args) -> Result<(), CellBuilderError> {
248 builder
249 .pack(self.ihr_disabled, ())?
250 .pack(self.bounce, ())?
251 .pack(self.bounced, ())?
252 .pack(self.src, ())?
253 .pack(self.dst, ())?
254 .store(&self.value, ())?
255 .pack_as::<_, &Grams>(&self.ihr_fee, ())?
256 .pack_as::<_, &Grams>(&self.fwd_fee, ())?
257 .pack(self.created_lt, ())?
258 .pack_as::<_, UnixTimestamp>(self.created_at.unwrap_or(DateTime::UNIX_EPOCH), ())?;
259 Ok(())
260 }
261}
262
263impl<'de> CellDeserialize<'de> for InternalMsgInfo {
264 type Args = ();
265
266 fn parse(parser: &mut CellParser<'de>, _: Self::Args) -> Result<Self, CellParserError<'de>> {
267 Ok(Self {
268 ihr_disabled: parser.unpack(())?,
269 bounce: parser.unpack(())?,
270 bounced: parser.unpack(())?,
271 src: parser.unpack(()).context("src")?,
272 dst: parser.unpack(()).context("dst")?,
273 value: parser.parse(()).context("value")?,
274 ihr_fee: parser.unpack_as::<_, Grams>(())?,
275 fwd_fee: parser.unpack_as::<_, Grams>(())?,
276 created_lt: parser.unpack(())?,
277 created_at: Some(parser.unpack_as::<_, UnixTimestamp>(())?)
278 .filter(|dt| *dt != DateTime::UNIX_EPOCH),
279 })
280 }
281}
282
283#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
289#[derive(Debug, Clone, PartialEq, Eq)]
290pub struct ExternalInMsgInfo {
291 pub src: MsgAddress,
292 pub dst: MsgAddress,
293 pub import_fee: BigUint,
294}
295
296impl BitPack for ExternalInMsgInfo {
297 type Args = ();
298
299 fn pack<W>(&self, writer: &mut W, _: Self::Args) -> Result<(), W::Error>
300 where
301 W: BitWriter + ?Sized,
302 {
303 writer
304 .pack(self.src, ())?
305 .pack(self.dst, ())?
306 .pack_as::<_, &Grams>(&self.import_fee, ())?;
307 Ok(())
308 }
309}
310
311impl<'de> BitUnpack<'de> for ExternalInMsgInfo {
312 type Args = ();
313
314 fn unpack<R>(reader: &mut R, _: Self::Args) -> Result<Self, R::Error>
315 where
316 R: BitReader<'de> + ?Sized,
317 {
318 Ok(Self {
319 src: reader.unpack(())?,
320 dst: reader.unpack(())?,
321 import_fee: reader.unpack_as::<_, Grams>(())?,
322 })
323 }
324}
325
326#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
332#[derive(Debug, Clone, PartialEq, Eq)]
333pub struct ExternalOutMsgInfo {
334 pub src: MsgAddress,
335 pub dst: MsgAddress,
336 pub created_lt: u64,
337 pub created_at: DateTime<Utc>,
338}
339
340impl BitPack for ExternalOutMsgInfo {
341 type Args = ();
342
343 fn pack<W>(&self, writer: &mut W, _: Self::Args) -> Result<(), W::Error>
344 where
345 W: BitWriter + ?Sized,
346 {
347 writer
348 .pack(self.src, ())?
349 .pack(self.dst, ())?
350 .pack(self.created_lt, ())?
351 .pack_as::<_, UnixTimestamp>(self.created_at, ())?;
352 Ok(())
353 }
354}
355
356impl<'de> BitUnpack<'de> for ExternalOutMsgInfo {
357 type Args = ();
358
359 fn unpack<R>(reader: &mut R, _: Self::Args) -> Result<Self, R::Error>
360 where
361 R: BitReader<'de> + ?Sized,
362 {
363 Ok(Self {
364 src: reader.unpack(())?,
365 dst: reader.unpack(())?,
366 created_lt: reader.unpack(())?,
367 created_at: reader.unpack_as::<_, UnixTimestamp>(())?,
368 })
369 }
370}
371
372#[cfg(test)]
373mod tests {
374 use tlb::ser::CellSerializeExt;
375
376 use super::*;
377
378 #[test]
379 fn message_serde() {
380 let msg = Message::<(), (), ()> {
381 info: CommonMsgInfo::Internal(InternalMsgInfo {
382 ihr_disabled: true,
383 bounce: true,
384 bounced: false,
385 src: MsgAddress::NULL,
386 dst: MsgAddress::NULL,
387 value: Default::default(),
388 ihr_fee: BigUint::ZERO,
389 fwd_fee: BigUint::ZERO,
390 created_lt: 0,
391 created_at: None,
392 }),
393 init: None,
394 body: (),
395 };
396
397 let cell = msg.to_cell(()).unwrap();
398 let got: Message<(), (), ()> = cell.parse_fully(()).unwrap();
399
400 assert_eq!(got, msg);
401 }
402
403 #[test]
404 fn internal_msg_info_serde() {
405 let info = CommonMsgInfo::Internal(InternalMsgInfo {
406 ihr_disabled: true,
407 bounce: true,
408 bounced: false,
409 src: MsgAddress::NULL,
410 dst: MsgAddress::NULL,
411 value: Default::default(),
412 ihr_fee: BigUint::ZERO,
413 fwd_fee: BigUint::ZERO,
414 created_lt: 0,
415 created_at: None,
416 });
417
418 let cell = info.to_cell(()).unwrap();
419 let got: CommonMsgInfo = cell.parse_fully(()).unwrap();
420
421 assert_eq!(got, info);
422 }
423
424 #[test]
425 fn external_in_msg_info_serde() {
426 let info = CommonMsgInfo::ExternalIn(ExternalInMsgInfo {
427 src: MsgAddress::NULL,
428 dst: MsgAddress::NULL,
429 import_fee: BigUint::ZERO,
430 });
431
432 let cell = info.to_cell(()).unwrap();
433 let got: CommonMsgInfo = cell.parse_fully(()).unwrap();
434
435 assert_eq!(got, info);
436 }
437
438 #[test]
439 fn external_out_msg_info_serde() {
440 let info = CommonMsgInfo::ExternalOut(ExternalOutMsgInfo {
441 src: MsgAddress::NULL,
442 dst: MsgAddress::NULL,
443 created_lt: 0,
444 created_at: DateTime::UNIX_EPOCH,
445 });
446
447 let cell = info.to_cell(()).unwrap();
448 let got: CommonMsgInfo = cell.parse_fully(()).unwrap();
449
450 assert_eq!(got, info);
451 }
452}