hopper_runtime/
address.rs1pub const ADDRESS_BYTES: usize = 32;
11
12pub const MAX_SEED_LEN: usize = 32;
14
15pub const MAX_SEEDS: usize = 16;
17
18pub const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
20
21#[repr(transparent)]
34#[derive(Clone, Copy, Default, PartialOrd, Ord)]
35pub struct Address(pub(crate) [u8; 32]);
36
37unsafe impl crate::pod::Zeroable for Address {}
43unsafe impl crate::pod::Pod for Address {}
44unsafe impl crate::zerocopy::__sealed::HopperZeroCopySealed for Address {}
46
47impl Address {
48 #[inline(always)]
50 pub const fn new(bytes: [u8; 32]) -> Self {
51 Self(bytes)
52 }
53
54 #[inline(always)]
56 pub const fn new_from_array(bytes: [u8; 32]) -> Self {
57 Self(bytes)
58 }
59
60 #[inline(always)]
62 pub const fn to_bytes(&self) -> [u8; 32] {
63 self.0
64 }
65
66 #[inline(always)]
68 pub const fn as_array(&self) -> &[u8; 32] {
69 &self.0
70 }
71
72 #[inline(always)]
74 pub const fn as_bytes(&self) -> &[u8; 32] {
75 &self.0
76 }
77
78 #[cfg(target_os = "solana")]
83 pub fn find_program_address(seeds: &[&[u8]], program_id: &Address) -> (Address, u8) {
84 crate::native_boundary::find_program_address(seeds, program_id)
85 }
86
87 #[cfg(target_os = "solana")]
91 pub fn create_program_address(
92 seeds: &[&[u8]],
93 program_id: &Address,
94 ) -> Result<Address, crate::ProgramError> {
95 crate::native_boundary::create_program_address(seeds, program_id)
96 }
97}
98
99impl From<[u8; 32]> for Address {
102 #[inline(always)]
103 fn from(bytes: [u8; 32]) -> Self {
104 Self(bytes)
105 }
106}
107
108impl From<Address> for [u8; 32] {
109 #[inline(always)]
110 fn from(addr: Address) -> [u8; 32] {
111 addr.0
112 }
113}
114
115impl TryFrom<&[u8]> for Address {
116 type Error = core::array::TryFromSliceError;
117
118 #[inline]
119 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
120 let arr: [u8; 32] = slice.try_into()?;
121 Ok(Self(arr))
122 }
123}
124
125impl AsRef<[u8]> for Address {
126 #[inline(always)]
127 fn as_ref(&self) -> &[u8] {
128 &self.0
129 }
130}
131
132impl AsMut<[u8]> for Address {
133 #[inline(always)]
134 fn as_mut(&mut self) -> &mut [u8] {
135 &mut self.0
136 }
137}
138
139impl AsRef<[u8; 32]> for Address {
140 #[inline(always)]
141 fn as_ref(&self) -> &[u8; 32] {
142 &self.0
143 }
144}
145
146impl PartialEq for Address {
147 #[inline(always)]
150 fn eq(&self, other: &Self) -> bool {
151 address_eq(self, other)
152 }
153}
154
155impl Eq for Address {}
158
159impl core::hash::Hash for Address {
160 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
161 self.0.hash(state);
162 }
163}
164
165impl core::fmt::Debug for Address {
166 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
167 write!(f, "Address({:?})", &self.0[..4])
168 }
169}
170
171impl core::fmt::Display for Address {
172 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173 for byte in &self.0[..4] {
175 write!(f, "{byte:02x}")?;
176 }
177 write!(f, "..")
178 }
179}
180
181#[inline(always)]
185pub fn address_eq(a: &Address, b: &Address) -> bool {
186 keys_eq(&a.0, &b.0)
187}
188
189#[inline(always)]
198pub fn keys_eq(a: &[u8; 32], b: &[u8; 32]) -> bool {
199 let a_ptr = a.as_ptr() as *const u64;
200 let b_ptr = b.as_ptr() as *const u64;
201 unsafe {
205 core::ptr::read_unaligned(a_ptr) == core::ptr::read_unaligned(b_ptr)
206 && core::ptr::read_unaligned(a_ptr.add(1)) == core::ptr::read_unaligned(b_ptr.add(1))
207 && core::ptr::read_unaligned(a_ptr.add(2)) == core::ptr::read_unaligned(b_ptr.add(2))
208 && core::ptr::read_unaligned(a_ptr.add(3)) == core::ptr::read_unaligned(b_ptr.add(3))
209 }
210}
211
212#[inline(always)]
220pub fn keys_eq_bytes(a: &[u8], b: &[u8; 32]) -> bool {
221 if a.len() != 32 {
222 return false;
223 }
224 let a_ptr = a.as_ptr() as *const u64;
225 let b_ptr = b.as_ptr() as *const u64;
226 unsafe {
230 core::ptr::read_unaligned(a_ptr) == core::ptr::read_unaligned(b_ptr)
231 && core::ptr::read_unaligned(a_ptr.add(1)) == core::ptr::read_unaligned(b_ptr.add(1))
232 && core::ptr::read_unaligned(a_ptr.add(2)) == core::ptr::read_unaligned(b_ptr.add(2))
233 && core::ptr::read_unaligned(a_ptr.add(3)) == core::ptr::read_unaligned(b_ptr.add(3))
234 }
235}
236
237#[inline(always)]
243pub fn address_is_zero(a: &Address) -> bool {
244 let ptr = a.0.as_ptr() as *const u64;
245 unsafe {
248 (core::ptr::read_unaligned(ptr)
249 | core::ptr::read_unaligned(ptr.add(1))
250 | core::ptr::read_unaligned(ptr.add(2))
251 | core::ptr::read_unaligned(ptr.add(3)))
252 == 0
253 }
254}
255
256#[cfg(test)]
259mod tests {
260 use super::*;
261
262 fn edge_patterns() -> [[u8; 32]; 6] {
264 let mut ramp = [0u8; 32];
265 for (i, b) in ramp.iter_mut().enumerate() {
266 *b = i as u8;
267 }
268 let mut last_hi = [0u8; 32];
269 last_hi[31] = 0xFF;
270 let mut first_hi = [0u8; 32];
271 first_hi[0] = 0xFF;
272 [
273 [0u8; 32],
274 [0xFFu8; 32],
275 ramp,
276 last_hi,
277 first_hi,
278 [0xA5u8; 32],
279 ]
280 }
281
282 #[test]
283 fn keys_eq_matches_bytewise_on_equal_arrays() {
284 for pat in edge_patterns() {
285 let copy = pat;
286 assert!(keys_eq(&pat, ©));
287 assert!(address_eq(&Address::new(pat), &Address::new(pat)));
288 assert_eq!(Address::new(pat), Address::new(pat));
289 }
290 }
291
292 #[test]
293 fn keys_eq_detects_single_byte_difference_at_every_index() {
294 for base in edge_patterns() {
295 for idx in 0..32 {
296 let mut other = base;
297 other[idx] ^= 0x01;
298 assert!(!keys_eq(&base, &other), "missed diff at byte {idx}");
299 assert!(!address_eq(&Address::new(base), &Address::new(other)));
300 assert_ne!(Address::new(base), Address::new(other));
301 assert_eq!(keys_eq(&base, &other), base == other);
303 }
304 }
305 }
306
307 #[test]
308 fn keys_eq_differs_only_in_last_byte() {
309 let a = [7u8; 32];
310 let mut b = a;
311 b[31] = 8;
312 assert!(!keys_eq(&a, &b));
313 assert_ne!(Address::new(a), Address::new(b));
314 }
315
316 #[test]
317 fn keys_eq_bytes_matches_slice_semantics() {
318 for pat in edge_patterns() {
319 assert!(keys_eq_bytes(&pat[..], &pat));
320 for idx in 0..32 {
321 let mut other = pat;
322 other[idx] ^= 0x80;
323 assert_eq!(keys_eq_bytes(&other[..], &pat), other == pat);
324 }
325 }
326 let key = [0u8; 32];
328 assert!(!keys_eq_bytes(&[], &key));
329 assert!(!keys_eq_bytes(&key[..31], &key));
330 let long = [0u8; 33];
331 assert!(!keys_eq_bytes(&long[..], &key));
332 }
333
334 #[test]
335 fn eq_is_consistent_with_derived_ord() {
336 use core::cmp::Ordering;
337 let patterns = edge_patterns();
338 for a in patterns {
339 for b in patterns {
340 let (aa, ab) = (Address::new(a), Address::new(b));
341 assert_eq!(aa == ab, aa.cmp(&ab) == Ordering::Equal);
343 assert_eq!(aa == ab, a == b);
345 for idx in 0..32 {
346 let mut c = a;
347 c[idx] = c[idx].wrapping_add(1);
348 let ac = Address::new(c);
349 assert_eq!(aa == ac, aa.cmp(&ac) == Ordering::Equal);
350 assert_eq!(aa == ac, a == c);
351 }
352 }
353 }
354 }
355
356 #[test]
357 fn address_is_zero_or_fold() {
358 assert!(address_is_zero(&Address::new([0u8; 32])));
359 assert!(address_is_zero(&Address::default()));
360 for idx in 0..32 {
361 let mut bytes = [0u8; 32];
362 bytes[idx] = 1;
363 assert!(!address_is_zero(&Address::new(bytes)));
364 }
365 assert!(!address_is_zero(&Address::new([0xFFu8; 32])));
366 }
367}