1use core::{
2 fmt::{self, Debug, Display},
3 str::FromStr,
4};
5
6use base64::{
7 Engine, engine::general_purpose::STANDARD_NO_PAD, engine::general_purpose::URL_SAFE_NO_PAD,
8};
9use crc::Crc;
10use digest::{Digest, Output};
11use strum::Display;
12use tlb::{
13 Context, Error, StringError,
14 bits::{
15 NBits, NoArgs, VarLen,
16 bitvec::{order::Msb0, vec::BitVec},
17 de::{BitReader, BitReaderExt, BitUnpack},
18 ser::{BitPack, BitWriter, BitWriterExt},
19 },
20 ser::{CellBuilderError, CellSerialize, CellSerializeExt},
21};
22
23use crate::state_init::StateInit;
24
25const CRC_16_XMODEM: Crc<u16> = Crc::<u16>::new(&crc::CRC_16_XMODEM);
26
27#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
41#[cfg_attr(
42 feature = "schemars_1",
43 derive(::schemars_1::JsonSchema),
44 schemars(crate = "::schemars_1", with = "String")
45)]
46#[cfg_attr(
47 feature = "serde",
48 derive(::serde_with::SerializeDisplay, ::serde_with::DeserializeFromStr)
49)]
50#[derive(Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
51pub struct MsgAddress {
52 #[cfg_attr(
53 feature = "arbitrary",
54 arbitrary(with = |u: &mut ::arbitrary::Unstructured| u.int_in_range(i8::MIN as i32..=i8::MAX as i32))
55 )]
56 pub workchain_id: i32,
57 pub address: [u8; 32],
58}
59
60impl MsgAddress {
61 pub const NULL: Self = Self {
62 workchain_id: 0,
63 address: [0; 32],
64 };
65
66 #[cfg(feature = "sha2")]
69 #[inline]
70 pub fn derive<C, D>(
71 workchain_id: i32,
72 state_init: StateInit<C, D>,
73 ) -> Result<Self, CellBuilderError>
74 where
75 C: CellSerialize<Args: NoArgs>,
76 D: CellSerialize<Args: NoArgs>,
77 {
78 Self::derive_digest::<C, D, sha2::Sha256>(workchain_id, state_init)
79 }
80
81 #[inline]
82 pub fn derive_digest<C, D, H>(
83 workchain_id: i32,
84 state_init: StateInit<C, D>,
85 ) -> Result<Self, CellBuilderError>
86 where
87 C: CellSerialize<Args: NoArgs>,
88 D: CellSerialize<Args: NoArgs>,
89 H: Digest,
90 Output<H>: Into<[u8; 32]>,
91 {
92 Ok(Self {
93 workchain_id,
94 address: state_init.to_cell(())?.hash_digest::<H>(),
95 })
96 }
97
98 pub fn from_hex(s: impl AsRef<str>) -> Result<Self, StringError> {
99 let s = s.as_ref();
100 let (workchain, addr) = s
101 .split_once(':')
102 .ok_or_else(|| Error::custom("wrong format"))?;
103 let workchain_id = workchain.parse::<i32>().map_err(Error::custom)?;
104 let mut address = [0; 32];
105 hex::decode_to_slice(addr, &mut address).map_err(Error::custom)?;
106 Ok(Self {
107 workchain_id,
108 address,
109 })
110 }
111
112 #[inline]
115 pub fn to_hex(&self) -> String {
116 format!("{}:{}", self.workchain_id, hex::encode(self.address))
117 }
118
119 #[inline]
121 pub fn from_base64_url(s: impl AsRef<str>) -> Result<Self, StringError> {
122 Self::from_base64_url_flags(s).map(|(addr, _, _)| addr)
123 }
124
125 #[inline]
129 pub fn from_base64_url_flags(s: impl AsRef<str>) -> Result<(Self, bool, bool), StringError> {
130 Self::from_base64_repr(URL_SAFE_NO_PAD, s)
131 }
132
133 #[inline]
135 pub fn from_base64_std(s: impl AsRef<str>) -> Result<Self, StringError> {
136 Self::from_base64_std_flags(s).map(|(addr, _, _)| addr)
137 }
138
139 #[inline]
143 pub fn from_base64_std_flags(s: impl AsRef<str>) -> Result<(Self, bool, bool), StringError> {
144 Self::from_base64_repr(STANDARD_NO_PAD, s)
145 }
146
147 #[inline]
149 pub fn to_base64_url(self) -> String {
150 self.to_base64_url_flags(false, false)
151 }
152
153 #[inline]
155 pub fn to_base64_url_flags(self, non_bounceable: bool, non_production: bool) -> String {
156 self.to_base64_flags(non_bounceable, non_production, URL_SAFE_NO_PAD)
157 }
158
159 #[inline]
161 pub fn to_base64_std(self) -> String {
162 self.to_base64_std_flags(false, false)
163 }
164
165 #[inline]
167 pub fn to_base64_std_flags(self, non_bounceable: bool, non_production: bool) -> String {
168 self.to_base64_flags(non_bounceable, non_production, STANDARD_NO_PAD)
169 }
170
171 fn from_base64_repr(
176 engine: impl Engine,
177 s: impl AsRef<str>,
178 ) -> Result<(Self, bool, bool), StringError> {
179 let mut bytes = [0; 36];
180 if engine
181 .decode_slice(s.as_ref(), &mut bytes)
182 .map_err(Error::custom)
183 .context("base64")?
184 != bytes.len()
185 {
186 return Err(Error::custom("invalid length"));
187 };
188
189 let (non_production, non_bounceable) = match bytes[0] {
190 0x11 => (false, false),
191 0x51 => (false, true),
192 0x91 => (true, false),
193 0xD1 => (true, true),
194 flags => return Err(Error::custom(format!("unsupported flags: {flags:#x}"))),
195 };
196 let workchain_id = bytes[1] as i8 as i32;
197 let crc = ((bytes[34] as u16) << 8) | bytes[35] as u16;
198 if crc != CRC_16_XMODEM.checksum(&bytes[0..34]) {
199 return Err(Error::custom("CRC mismatch"));
200 }
201 let mut address = [0_u8; 32];
202 address.clone_from_slice(&bytes[2..34]);
203 Ok((
204 Self {
205 workchain_id,
206 address,
207 },
208 non_bounceable,
209 non_production,
210 ))
211 }
212
213 fn to_base64_flags(
214 self,
215 non_bounceable: bool,
216 non_production: bool,
217 engine: impl Engine,
218 ) -> String {
219 let mut bytes = [0; 36];
220 let tag: u8 = match (non_production, non_bounceable) {
221 (false, false) => 0x11,
222 (false, true) => 0x51,
223 (true, false) => 0x91,
224 (true, true) => 0xD1,
225 };
226 bytes[0] = tag;
227 bytes[1] = (self.workchain_id & 0xff) as u8;
228 bytes[2..34].clone_from_slice(&self.address);
229 let crc = CRC_16_XMODEM.checksum(&bytes[0..34]);
230 bytes[34] = ((crc >> 8) & 0xff) as u8;
231 bytes[35] = (crc & 0xff) as u8;
232 engine.encode(bytes)
233 }
234
235 #[inline]
237 pub fn is_null(&self) -> bool {
238 *self == Self::NULL
239 }
240}
241
242impl Debug for MsgAddress {
243 #[inline]
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 f.write_str(self.to_hex().as_str())
246 }
247}
248
249impl Display for MsgAddress {
250 #[inline]
251 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
252 f.write_str(self.to_base64_url().as_str())
253 }
254}
255
256impl FromStr for MsgAddress {
257 type Err = StringError;
258
259 fn from_str(s: &str) -> Result<Self, Self::Err> {
260 if s.len() == 48 {
261 if s.contains(['-', '_']) {
262 Self::from_base64_url(s)
263 } else {
264 Self::from_base64_std(s)
265 }
266 } else {
267 Self::from_hex(s)
268 }
269 }
270}
271
272impl BitPack for MsgAddress {
273 type Args = ();
274
275 #[inline]
276 fn pack<W>(&self, writer: &mut W, (): Self::Args) -> Result<(), W::Error>
277 where
278 W: BitWriter + ?Sized,
279 {
280 if self.is_null() {
281 writer.pack(MsgAddressTag::Null, ())?;
282 } else {
283 writer
284 .pack(MsgAddressTag::Std, ())?
285 .pack::<Option<Anycast>>(None, ())?
287 .pack(self.workchain_id as i8, ())?
289 .pack(self.address, ())?;
291 }
292 Ok(())
293 }
294}
295
296impl<'de> BitUnpack<'de> for MsgAddress {
297 type Args = ();
298
299 #[inline]
300 fn unpack<R>(reader: &mut R, (): Self::Args) -> Result<Self, R::Error>
301 where
302 R: BitReader<'de> + ?Sized,
303 {
304 match reader.unpack(())? {
305 MsgAddressTag::Null => Ok(Self::NULL),
306 MsgAddressTag::Std => {
307 let _: Option<Anycast> = reader.unpack(())?;
309 Ok(Self {
310 workchain_id: reader.unpack::<i8>(())? as i32,
312 address: reader.unpack(())?,
314 })
315 }
316 MsgAddressTag::Var => {
317 let _: Option<Anycast> = reader.unpack(())?;
319 let addr_len: u16 = reader.unpack_as::<_, NBits<9>>(())?;
321 if addr_len != 256 {
322 return Err(Error::custom(format!(
324 "only 256-bit addresses are supported for addr_var$11, got {addr_len} bits"
325 )));
326 }
327 Ok(Self {
328 workchain_id: reader.unpack(())?,
330 address: reader.unpack(())?,
332 })
333 }
334 tag => Err(Error::custom(format!("unsupported address tag: {tag}"))),
335 }
336 }
337}
338
339#[derive(Clone, Copy, Display)]
340#[repr(u8)]
341enum MsgAddressTag {
342 #[strum(serialize = "addr_none$00")]
343 Null,
344 #[strum(serialize = "addr_extern$01")]
345 Extern,
346 #[strum(serialize = "addr_std$10")]
347 Std,
348 #[strum(serialize = "addr_var$11")]
349 Var,
350}
351
352impl BitPack for MsgAddressTag {
353 type Args = ();
354
355 #[inline]
356 fn pack<W>(&self, writer: &mut W, (): Self::Args) -> Result<(), W::Error>
357 where
358 W: BitWriter + ?Sized,
359 {
360 writer.pack_as::<_, NBits<2>>(*self as u8, ())?;
361 Ok(())
362 }
363}
364
365impl<'de> BitUnpack<'de> for MsgAddressTag {
366 type Args = ();
367
368 #[inline]
369 fn unpack<R>(reader: &mut R, (): Self::Args) -> Result<Self, R::Error>
370 where
371 R: BitReader<'de> + ?Sized,
372 {
373 Ok(match reader.unpack_as::<u8, NBits<2>>(())? {
374 0b00 => Self::Null,
375 0b01 => Self::Extern,
376 0b10 => Self::Std,
377 0b11 => Self::Var,
378 _ => unreachable!(),
379 })
380 }
381}
382
383pub struct Anycast {
387 pub rewrite_pfx: BitVec<u8, Msb0>,
388}
389
390impl BitPack for Anycast {
391 type Args = ();
392
393 fn pack<W>(&self, writer: &mut W, (): Self::Args) -> Result<(), W::Error>
394 where
395 W: BitWriter + ?Sized,
396 {
397 if self.rewrite_pfx.is_empty() {
398 return Err(Error::custom("depth >= 1"));
399 }
400 writer.pack_as::<_, &VarLen<_, 5>>(&self.rewrite_pfx, ())?;
401 Ok(())
402 }
403}
404
405impl<'de> BitUnpack<'de> for Anycast {
406 type Args = ();
407
408 fn unpack<R>(reader: &mut R, (): Self::Args) -> Result<Self, R::Error>
409 where
410 R: BitReader<'de> + ?Sized,
411 {
412 let rewrite_pfx: BitVec<u8, Msb0> = reader.unpack_as::<_, VarLen<_, 5>>(())?;
413 if rewrite_pfx.is_empty() {
414 return Err(Error::custom("depth >= 1"));
415 }
416 Ok(Self { rewrite_pfx })
417 }
418}
419
420#[cfg(feature = "schemars_0_8")]
421const _: () = {
423 use schemars_0_8::{JsonSchema, r#gen::SchemaGenerator, schema::Schema};
424
425 impl JsonSchema for MsgAddress {
426 fn schema_name() -> String {
427 String::schema_name()
428 }
429
430 fn json_schema(generator: &mut SchemaGenerator) -> Schema {
431 String::json_schema(generator)
432 }
433 }
434};
435
436#[cfg(test)]
437mod tests {
438 use super::*;
439
440 #[test]
441 fn parse_address() {
442 let _: MsgAddress = "EQBGXZ9ddZeWypx8EkJieHJX75ct0bpkmu0Y4YoYr3NM0Z9e"
443 .parse()
444 .unwrap();
445 }
446
447 #[cfg(feature = "serde")]
448 #[test]
449 fn serde() {
450 use serde_json::json;
451
452 let _: MsgAddress =
453 serde_json::from_value(json!("EQBGXZ9ddZeWypx8EkJieHJX75ct0bpkmu0Y4YoYr3NM0Z9e"))
454 .unwrap();
455 }
456}