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