1use crate::address::Address;
4use crate::error::ProgramError;
5use crate::AccountView;
6
7#[inline]
12pub fn create_program_address(
13 seeds: &[&[u8]],
14 program_id: &Address,
15) -> Result<Address, ProgramError> {
16 crate::compat::create_program_address(seeds, program_id)
17}
18
19#[inline]
23pub fn find_program_address(seeds: &[&[u8]], program_id: &Address) -> (Address, u8) {
24 #[cfg(target_os = "solana")]
25 {
26 crate::compat::find_program_address(seeds, program_id)
27 }
28 #[cfg(not(target_os = "solana"))]
29 {
30 let _ = (seeds, program_id);
31 (Address::default(), 0)
32 }
33}
34
35#[inline(always)]
37pub fn derive(seeds: &[&[u8]], program_id: &Address) -> (Address, u8) {
38 find_program_address(seeds, program_id)
39}
40
41#[inline]
43pub fn verify_pda(
44 account: &AccountView,
45 seeds: &[&[u8]],
46 program_id: &Address,
47) -> Result<(), ProgramError> {
48 #[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
49 {
50 hopper_native::pda::verify_pda(
51 account.as_backend(),
52 seeds,
53 crate::compat::as_backend_address(program_id),
54 )
55 .map_err(ProgramError::from)
56 }
57
58 #[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
59 {
60 let expected = create_program_address(seeds, program_id)?;
61 if crate::address::address_eq(account.address(), &expected) {
62 Ok(())
63 } else {
64 Err(ProgramError::InvalidSeeds)
65 }
66 }
67}
68
69#[inline]
71pub fn verify_pda_with_bump(
72 account: &AccountView,
73 seeds: &[&[u8]],
74 bump: u8,
75 program_id: &Address,
76) -> Result<(), ProgramError> {
77 #[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
78 {
79 hopper_native::pda::verify_pda_with_bump(
80 account.as_backend(),
81 seeds,
82 bump,
83 crate::compat::as_backend_address(program_id),
84 )
85 .map_err(ProgramError::from)
86 }
87
88 #[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
89 {
90 let mut full_seeds: [&[u8]; 17] = [&[]; 17];
91 let num = seeds.len().min(15);
92 let mut i = 0;
93 while i < num {
94 full_seeds[i] = seeds[i];
95 i += 1;
96 }
97 let bump_bytes = [bump];
98 full_seeds[num] = &bump_bytes;
99
100 let expected = create_program_address(&full_seeds[..num + 1], program_id)?;
101 if crate::address::address_eq(account.address(), &expected) {
102 Ok(())
103 } else {
104 Err(ProgramError::InvalidSeeds)
105 }
106 }
107}
108
109#[inline]
121pub fn find_and_verify_pda(
122 account: &AccountView,
123 seeds: &[&[u8]],
124 program_id: &Address,
125) -> Result<u8, ProgramError> {
126 #[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
127 {
128 let expected_addr = account.as_backend().address();
129 let backend_expected =
130 unsafe { &*(expected_addr as *const hopper_native::address::Address) };
132 verify_pda_sha256_loop(backend_expected, seeds, program_id)
133 }
134
135 #[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
136 {
137 let (expected, bump) = find_program_address(seeds, program_id);
138 if crate::address::address_eq(account.address(), &expected) {
139 Ok(bump)
140 } else {
141 Err(ProgramError::InvalidSeeds)
142 }
143 }
144}
145
146#[inline]
150pub fn verify_pda_strict(
151 expected: &Address,
152 seeds: &[&[u8]],
153 program_id: &Address,
154) -> Result<(), ProgramError> {
155 #[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
156 {
157 let backend_expected =
158 unsafe { &*(expected as *const Address as *const hopper_native::address::Address) };
160 verify_pda_sha256_loop(backend_expected, seeds, program_id).map(|_| ())
161 }
162
163 #[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
164 {
165 let (derived, _) = find_program_address(seeds, program_id);
166 if crate::address::address_eq(&derived, expected) {
167 Ok(())
168 } else {
169 Err(ProgramError::InvalidSeeds)
170 }
171 }
172}
173
174#[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
180#[inline(always)]
181fn verify_pda_sha256_loop(
182 expected: &hopper_native::address::Address,
183 seeds: &[&[u8]],
184 program_id: &Address,
185) -> Result<u8, ProgramError> {
186 let backend_pid = crate::compat::as_backend_address(program_id);
187 let n = seeds.len().min(16);
188 let mut slices = core::mem::MaybeUninit::<[&[u8]; 19]>::uninit();
189 let sptr = slices.as_mut_ptr() as *mut &[u8];
190 let mut i = 0;
191 while i < n {
192 unsafe { sptr.add(i).write(seeds[i]) };
194 i += 1;
195 }
196 let mut bump_byte = [255u8];
197 unsafe {
199 sptr.add(n).write(&bump_byte as &[u8]);
200 sptr.add(n + 1).write(backend_pid.as_ref());
201 sptr.add(n + 2)
202 .write(hopper_native::address::PDA_MARKER.as_slice());
203 }
204 let input = unsafe { core::slice::from_raw_parts(sptr as *const &[u8], n + 3) };
206
207 let mut bump: u16 = 256;
208 while bump > 0 {
209 bump -= 1;
210 bump_byte[0] = bump as u8;
211
212 let mut hash = core::mem::MaybeUninit::<[u8; 32]>::uninit();
213 unsafe {
215 hopper_native::syscalls::sol_sha256(
216 input as *const _ as *const u8,
217 input.len() as u64,
218 hash.as_mut_ptr() as *mut u8,
219 );
220 }
221 let derived =
222 unsafe { &*(hash.as_ptr() as *const hopper_native::address::Address) };
224 if hopper_native::address::address_eq(derived, expected) {
225 return Ok(bump as u8);
226 }
227 }
228
229 Err(ProgramError::InvalidSeeds)
230}
231
232#[inline]
237pub fn verify_pda_from_stored_bump(
238 account: &AccountView,
239 seeds: &[&[u8]],
240 bump_offset: usize,
241 program_id: &Address,
242) -> Result<(), ProgramError> {
243 #[cfg(all(target_os = "solana", feature = "hopper-native-backend"))]
244 {
245 hopper_native::verify_pda_from_stored_bump(
246 account.as_backend(),
247 seeds,
248 bump_offset,
249 crate::compat::as_backend_address(program_id),
250 )
251 .map_err(ProgramError::from)
252 }
253
254 #[cfg(not(all(target_os = "solana", feature = "hopper-native-backend")))]
255 {
256 let data = account.try_borrow()?;
258 if bump_offset >= data.len() {
259 return Err(ProgramError::AccountDataTooSmall);
260 }
261 let bump = data[bump_offset];
262 let mut full_seeds: [&[u8]; 17] = [&[]; 17];
263 let num = seeds.len().min(15);
264 let mut i = 0;
265 while i < num {
266 full_seeds[i] = seeds[i];
267 i += 1;
268 }
269 let bump_bytes = [bump];
270 full_seeds[num] = &bump_bytes;
271
272 let expected = create_program_address(&full_seeds[..num + 1], program_id)?;
273 if crate::address::address_eq(account.address(), &expected) {
274 Ok(())
275 } else {
276 Err(ProgramError::InvalidSeeds)
277 }
278 }
279}