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