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