ab_contracts_common/
address.rs

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