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