ab_core_primitives/
address.rs

1//! Address-related primitives
2
3use crate::shard::ShardIndex;
4use ab_io_type::metadata::IoTypeMetadataKind;
5use ab_io_type::trivial_type::TrivialType;
6use core::cmp::Ordering;
7use core::mem::MaybeUninit;
8use core::{fmt, ptr};
9
10/// Logically the same as `u128`, but aligned to `8` bytes instead of `16`.
11///
12/// Byte layout is the same as `u128`, just alignment is different
13#[derive(Default, Copy, Clone, Eq, PartialEq, Hash)]
14#[repr(C)]
15pub struct Address(u64, u64);
16
17unsafe impl TrivialType for Address {
18    const METADATA: &[u8] = &[IoTypeMetadataKind::Address as u8];
19}
20
21// Ensure this never mismatches with code in `ab-io-type` despite being in different crate
22const _: () = {
23    let (type_details, _metadata) = IoTypeMetadataKind::type_details(Address::METADATA)
24        .expect("Statically correct metadata; qed");
25    assert!(size_of::<Address>() == type_details.recommended_capacity as usize);
26    assert!(align_of::<Address>() == type_details.alignment.get() as usize);
27};
28
29impl fmt::Debug for Address {
30    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31        f.debug_tuple("Address").field(&self.as_u128()).finish()
32    }
33}
34
35impl fmt::Display for Address {
36    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
37        // TODO: Human-readable formatting rather than a huge number
38        self.as_u128().fmt(f)
39    }
40}
41
42impl PartialEq<&Address> for Address {
43    #[inline(always)]
44    fn eq(&self, other: &&Address) -> bool {
45        self.0 == other.0
46    }
47}
48
49impl PartialEq<Address> for &Address {
50    #[inline(always)]
51    fn eq(&self, other: &Address) -> bool {
52        self.0 == other.0
53    }
54}
55
56impl Ord for Address {
57    #[inline(always)]
58    fn cmp(&self, other: &Address) -> Ordering {
59        self.as_u128().cmp(&other.as_u128())
60    }
61}
62
63impl PartialOrd for Address {
64    #[inline(always)]
65    fn partial_cmp(&self, other: &Address) -> Option<Ordering> {
66        Some(self.cmp(other))
67    }
68}
69
70impl From<u128> for Address {
71    #[inline(always)]
72    fn from(value: u128) -> Self {
73        Self::new(value)
74    }
75}
76
77impl From<Address> for u128 {
78    #[inline(always)]
79    fn from(value: Address) -> Self {
80        value.as_u128()
81    }
82}
83
84// TODO: Method for getting creation shard out of the address
85// TODO: There should be a notion of global address
86impl Address {
87    // TODO: Various system contracts
88    /// Sentinel contract address, inaccessible and not owned by anyone
89    pub const NULL: Self = Self::new(0);
90    /// System contract for managing code of other contracts
91    pub const SYSTEM_CODE: Self = Self::new(1);
92    /// System contract for managing block state
93    pub const SYSTEM_BLOCK: Self = Self::new(2);
94    /// System contract for managing state of other contracts
95    pub const SYSTEM_STATE: Self = Self::new(3);
96    /// System contract for native token
97    pub const SYSTEM_NATIVE_TOKEN: Self = Self::new(4);
98    /// System simple wallet base contract that can be used by end user wallets
99    pub const SYSTEM_SIMPLE_WALLET_BASE: Self = Self::new(10);
100
101    /// Create a value from `u128`
102    #[inline(always)]
103    const fn new(n: u128) -> Self {
104        let mut result = MaybeUninit::<Self>::uninit();
105        // SAFETY: correct size, valid pointer, and all bits are valid
106        unsafe {
107            result.as_mut_ptr().cast::<u128>().write_unaligned(n);
108            result.assume_init()
109        }
110    }
111
112    /// Turn value into `u128`
113    #[inline(always)]
114    const fn as_u128(self) -> u128 {
115        // SAFETY: correct size, valid pointer, and all bits are valid
116        unsafe { ptr::from_ref(&self).cast::<u128>().read_unaligned() }
117    }
118
119    /// System contract for address allocation on a particular shard index
120    #[inline(always)]
121    pub const fn system_address_allocator(shard_index: ShardIndex) -> Self {
122        // Shard `0` doesn't have its own allocator because there are no user-deployable contracts
123        // there, so address `0` is `NULL`, the rest up to `ShardIndex::MAX_SHARD_INDEX` correspond
124        // to address allocators of respective shards
125        Self::new(shard_index.as_u32() as u128 * ShardIndex::MAX_ADDRESSES_PER_SHARD.get())
126    }
127}