ab_core_primitives/
address.rs1use crate::shard::ShardIndex;
4use ab_io_type::metadata::IoTypeMetadataKind;
5use ab_io_type::trivial_type::TrivialType;
6use bech32::primitives::decode::CheckedHrpstring;
7use bech32::{Bech32m, ByteIterExt, Fe32IterExt, Hrp};
8use core::cmp::Ordering;
9use core::mem::MaybeUninit;
10use core::ops::Deref;
11use core::{fmt, ptr};
12use derive_more::Deref;
13
14#[derive(Copy, Clone)]
16pub struct FormattedAddress {
17 buffer:
18 [u8; ShortHrp::MAX_HRP_LENGTH + FormattedAddress::MAX_ENCODING_WITHOUT_HRP_WITH_SEPARATOR],
19 length: usize,
20}
21
22impl fmt::Debug for FormattedAddress {
23 #[inline(always)]
24 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25 self.as_str().fmt(f)
26 }
27}
28
29impl fmt::Display for FormattedAddress {
30 #[inline(always)]
31 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32 self.as_str().fmt(f)
33 }
34}
35
36impl Deref for FormattedAddress {
37 type Target = str;
38
39 #[inline(always)]
40 fn deref(&self) -> &Self::Target {
41 self.as_str()
42 }
43}
44
45impl FormattedAddress {
46 const MAX_ENCODING_WITHOUT_HRP_NO_SEPARATOR: usize = 33;
47 const MAX_ENCODING_WITHOUT_HRP_WITH_SEPARATOR: usize = 39;
48
49 #[inline(always)]
51 pub const fn as_str(&self) -> &str {
52 unsafe { str::from_utf8_unchecked(self.buffer.split_at_unchecked(self.length).0) }
54 }
55}
56
57#[derive(Debug, Copy, Clone, Eq, PartialEq, Deref)]
59pub struct ShortHrp(Hrp);
60
61impl ShortHrp {
62 pub const MAX_HRP_LENGTH: usize = 5;
64 pub const MAINNET: Self = Self(Hrp::parse_unchecked("abc"));
66 pub const TESTNET: Self = Self(Hrp::parse_unchecked("xyz"));
68
69 pub fn new(hrp: Hrp) -> Option<Self> {
74 if hrp.len() > Self::MAX_HRP_LENGTH {
75 return None;
76 }
77
78 Some(Self(hrp))
79 }
80}
81
82#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
91#[repr(C)]
92pub struct Address(u64, u64);
93
94unsafe impl TrivialType for Address {
95 const METADATA: &[u8] = &[IoTypeMetadataKind::Address as u8];
96}
97
98const _: () = {
100 let (type_details, _metadata) = IoTypeMetadataKind::type_details(Address::METADATA)
101 .expect("Statically correct metadata; qed");
102 assert!(size_of::<Address>() == type_details.recommended_capacity as usize);
103 assert!(align_of::<Address>() == type_details.alignment.get() as usize);
104};
105
106impl fmt::Debug for Address {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.debug_tuple("Address").field(&self.as_u128()).finish()
109 }
110}
111
112impl PartialEq<&Address> for Address {
113 #[inline(always)]
114 fn eq(&self, other: &&Address) -> bool {
115 self.0 == other.0
116 }
117}
118
119impl PartialEq<Address> for &Address {
120 #[inline(always)]
121 fn eq(&self, other: &Address) -> bool {
122 self.0 == other.0
123 }
124}
125
126impl Ord for Address {
127 #[inline(always)]
128 fn cmp(&self, other: &Address) -> Ordering {
129 self.as_u128().cmp(&other.as_u128())
130 }
131}
132
133impl PartialOrd for Address {
134 #[inline(always)]
135 fn partial_cmp(&self, other: &Address) -> Option<Ordering> {
136 Some(self.cmp(other))
137 }
138}
139
140impl From<u128> for Address {
141 #[inline(always)]
142 fn from(value: u128) -> Self {
143 Self::new(value)
144 }
145}
146
147impl From<Address> for u128 {
148 #[inline(always)]
149 fn from(value: Address) -> Self {
150 value.as_u128()
151 }
152}
153
154impl Address {
157 pub const NULL: Self = Self::new(0);
160 pub const SYSTEM_CODE: Self = Self::new(1);
162 pub const SYSTEM_BLOCK: Self = Self::new(2);
164 pub const SYSTEM_STATE: Self = Self::new(3);
166 pub const SYSTEM_NATIVE_TOKEN: Self = Self::new(4);
168 pub const SYSTEM_SIMPLE_WALLET_BASE: Self = Self::new(10);
170
171 const FORMAT_SEPARATOR_INTERVAL: [usize; 7] = [
173 1 + 4,
175 4,
176 3,
177 4,
178 4,
179 3,
180 4,
181 ];
182 const FORMAT_SEPARATOR: u8 = b'-';
183 const FORMAT_ALL_ZEROES: u8 = b'q';
184 const FORMAT_CHECKSUM_LENGTH: usize = 6;
185
186 #[inline(always)]
188 const fn new(n: u128) -> Self {
189 let mut result = MaybeUninit::<Self>::uninit();
190 unsafe {
192 result.as_mut_ptr().cast::<u128>().write_unaligned(n);
193 result.assume_init()
194 }
195 }
196
197 pub fn parse(s: &str) -> Option<(ShortHrp, Self)> {
201 let (hrp, other) = s.split_once('1')?;
202 if hrp.len() > ShortHrp::MAX_HRP_LENGTH {
203 return None;
204 }
205
206 let mut scratch = FormattedAddress {
207 buffer: [Self::FORMAT_ALL_ZEROES; _],
208 length: 0,
209 };
210
211 scratch.buffer[..hrp.len() + 1].copy_from_slice(&s.as_bytes()[..hrp.len() + 1]);
213 scratch.length = hrp.len() + FormattedAddress::MAX_ENCODING_WITHOUT_HRP_NO_SEPARATOR;
215
216 let mut chunks = other.rsplit(char::from(Self::FORMAT_SEPARATOR));
217 {
219 let checksum = chunks.next()?;
220
221 if checksum.len() != Self::FORMAT_CHECKSUM_LENGTH {
222 return None;
223 }
224 scratch.buffer[..scratch.length][scratch.length - Self::FORMAT_CHECKSUM_LENGTH..]
225 .copy_from_slice(checksum.as_bytes());
226 }
227
228 {
229 let mut buffer = &mut scratch.buffer[..scratch.length - Self::FORMAT_CHECKSUM_LENGTH];
230 let mut iterator = chunks
231 .zip(Self::FORMAT_SEPARATOR_INTERVAL.into_iter().rev())
232 .peekable();
233 while let Some((chunk, max_chunk_length)) = iterator.next() {
234 let chunk = chunk.as_bytes();
235
236 if iterator.peek().is_none() {
237 buffer[hrp.len() + 1..][..chunk.len()].copy_from_slice(chunk);
239 break;
240 }
241
242 if chunk.len() > max_chunk_length {
243 return None;
244 }
245
246 let target_chunk;
247 (buffer, target_chunk) = buffer.split_at_mut(buffer.len() - max_chunk_length);
248
249 target_chunk[max_chunk_length - chunk.len()..].copy_from_slice(chunk);
250 }
251 }
252
253 let checked_hrp_string = CheckedHrpstring::new::<Bech32m>(&scratch).ok()?;
254 let short_hrp = ShortHrp::new(checked_hrp_string.hrp())?;
255
256 let mut address_bytes = 0u128.to_be_bytes();
257 {
259 let mut address_bytes = address_bytes.as_mut_slice();
260 for byte in checked_hrp_string.byte_iter() {
261 if address_bytes.is_empty() {
262 return None;
263 }
264
265 address_bytes[0] = byte;
266
267 address_bytes = &mut address_bytes[1..];
268 }
269
270 if !address_bytes.is_empty() {
271 return None;
272 }
273 }
274 let address = Address::from(u128::from_be_bytes(address_bytes));
275
276 Some((short_hrp, address))
277 }
278
279 #[inline]
281 pub fn format(&self, hrp: &ShortHrp) -> FormattedAddress {
282 let mut scratch = FormattedAddress {
283 buffer: [0; _],
284 length: 0,
285 };
286
287 for char in self
288 .as_u128()
289 .to_be_bytes()
290 .into_iter()
291 .bytes_to_fes()
292 .with_checksum::<Bech32m>(hrp)
293 .bytes()
294 {
295 scratch.buffer[scratch.length] = char;
296 scratch.length += 1;
297 }
298
299 let (prefix_with_shard, other) = scratch
300 .as_str()
301 .split_at(hrp.len() + Self::FORMAT_SEPARATOR_INTERVAL[0]);
302 let (mut address_within_shard, checksum) =
303 other.split_at(Self::FORMAT_SEPARATOR_INTERVAL[1..].iter().sum());
304
305 let mut formatted_address = FormattedAddress {
306 buffer: [0; _],
307 length: 0,
308 };
309
310 {
312 let prefix_with_shard = prefix_with_shard
313 .trim_end_matches(char::from(Self::FORMAT_ALL_ZEROES))
314 .as_bytes();
315
316 formatted_address.buffer[..prefix_with_shard.len()].copy_from_slice(prefix_with_shard);
317 formatted_address.length = prefix_with_shard.len();
318
319 formatted_address.buffer[prefix_with_shard.len()] = Self::FORMAT_SEPARATOR;
320 formatted_address.length += 1;
321 }
322 {
324 let mut finished_trimming = false;
325
326 for &chunk_size in Self::FORMAT_SEPARATOR_INTERVAL[1..].iter() {
327 let mut chunk;
328 (chunk, address_within_shard) = address_within_shard.split_at(chunk_size);
329
330 if !finished_trimming {
331 chunk = chunk.trim_start_matches(char::from(Self::FORMAT_ALL_ZEROES));
332
333 if chunk.is_empty() {
334 continue;
335 }
336
337 finished_trimming = true;
338 }
339
340 formatted_address.buffer[formatted_address.length..][..chunk.len()]
341 .copy_from_slice(chunk.as_bytes());
342 formatted_address.length += chunk.len();
343
344 formatted_address.buffer[formatted_address.length] = Self::FORMAT_SEPARATOR;
345 formatted_address.length += 1;
346 }
347 }
348 {
350 formatted_address.buffer[formatted_address.length..][..checksum.len()]
351 .copy_from_slice(checksum.as_bytes());
352 formatted_address.length += checksum.len();
353 }
354
355 formatted_address
356 }
357
358 #[inline(always)]
360 const fn as_u128(self) -> u128 {
361 unsafe { ptr::from_ref(&self).cast::<u128>().read_unaligned() }
363 }
364
365 #[inline(always)]
367 pub const fn system_address_allocator(shard_index: ShardIndex) -> Self {
368 Self::new((shard_index.as_u32() as u128).reverse_bits())
372 }
373}