1use crate::base58::Pubkey;
40use curve25519_dalek::edwards::CompressedEdwardsY;
41use sha2::{Digest, Sha256};
42
43const PDA_MARKER: &[u8; 21] = b"ProgramDerivedAddress";
49
50pub fn is_on_curve(pk: &Pubkey) -> bool {
56 CompressedEdwardsY(*pk.as_bytes()).decompress().is_some()
57}
58
59pub fn find_program_address(seeds: &[&[u8]], program_id: &Pubkey) -> (Pubkey, u8) {
68 let program_bytes = program_id.as_bytes();
72 for bump in (1u8..=255).rev() {
73 let mut hasher = Sha256::new();
74 for seed in seeds {
75 hasher.update(seed);
76 }
77 hasher.update([bump]);
78 hasher.update(program_bytes);
79 hasher.update(PDA_MARKER);
80 let hash: [u8; 32] = hasher.finalize().into();
81 let pda = Pubkey::from_bytes(hash);
82 if !is_on_curve(&pda) {
83 return (pda, bump);
84 }
85 }
86 unreachable!("no off-curve PDA found across 255 bumps (probability ~0.5^255)");
88}
89
90#[cfg(test)]
91mod solana_oracle {
92 use super::*;
93 #[test]
97 fn matches_canonical_solana_program_derivation_two_seeds() {
98 let program_id = Pubkey::from_bytes([0x42u8; 32]);
99 let seeds: &[&[u8]] = &[b"palinurus", b"depin-attest"];
100
101 let (ref_pda, ref_bump) =
102 solana_program::pubkey::Pubkey::find_program_address(seeds, &solana_program::pubkey::Pubkey::new_from_array(program_id.to_bytes()));
103 let ref_b58 = bs58::encode(ref_pda.to_bytes()).into_string();
104
105 let (my_pda, my_bump) = find_program_address(seeds, &program_id);
106
107 eprintln!(
108 "[oracle] solana-program ref: {ref_b58} bump {ref_bump} | ours: {my_pda} bump {my_bump}"
109 );
110 assert_eq!(my_pda.to_bytes(), ref_pda.to_bytes(), "PDA must match canonical solana-program derivation");
111 assert_eq!(my_bump, ref_bump, "bump must match canonical solana-program derivation");
112 }
113
114 #[test]
115 fn matches_canonical_solana_program_derivation_single_seed() {
116 let program_id = Pubkey::from_bytes([0x42u8; 32]);
117 let seeds: &[&[u8]] = &[b"palinurus"];
118
119 let (ref_pda, ref_bump) =
120 solana_program::pubkey::Pubkey::find_program_address(seeds, &solana_program::pubkey::Pubkey::new_from_array(program_id.to_bytes()));
121 let ref_b58 = bs58::encode(ref_pda.to_bytes()).into_string();
122
123 let (my_pda, my_bump) = find_program_address(seeds, &program_id);
124
125 eprintln!(
126 "[oracle] solana-program ref: {ref_b58} bump {ref_bump} | ours: {my_pda} bump {my_bump}"
127 );
128 assert_eq!(my_pda.to_bytes(), ref_pda.to_bytes(), "PDA must match canonical solana-program derivation");
129 assert_eq!(my_bump, ref_bump, "bump must match canonical solana-program derivation");
130 }
131}
132
133#[cfg(test)]
134mod tests {
135 use super::*;
136
137 fn test_program_id() -> Pubkey {
141 Pubkey::from_bytes([0x42; 32])
142 }
143
144 #[test]
145 fn pda_is_off_curve() {
146 let program_id = test_program_id();
147 let (pda, _bump) = find_program_address(&[b"palinurus", b"depin-attest"], &program_id);
148 assert!(
149 !is_on_curve(&pda),
150 "derived PDA must be off-curve (no associated private key)"
151 );
152 }
153
154 #[test]
155 fn pda_is_deterministic() {
156 let program_id = test_program_id();
157 let (pda1, bump1) = find_program_address(&[b"palinurus"], &program_id);
158 let (pda2, bump2) = find_program_address(&[b"palinurus"], &program_id);
159 assert_eq!(pda1, pda2, "same seeds must yield the same PDA");
160 assert_eq!(bump1, bump2, "same seeds must yield the same bump");
161 }
162
163 #[test]
164 fn different_seeds_yield_different_pdas() {
165 let program_id = test_program_id();
166 let (pda_a, _) = find_program_address(&[b"palinurus"], &program_id);
167 let (pda_b, _) = find_program_address(&[b"oracle"], &program_id);
168 assert_ne!(pda_a, pda_b, "different seeds must yield different PDAs");
169 }
170
171 #[test]
172 fn different_programs_yield_different_pdas() {
173 let seeds: &[&[u8]] = &[b"palinurus"];
174 let prog_a = Pubkey::from_bytes([0x42; 32]);
175 let prog_b = Pubkey::from_bytes([0x43; 32]);
176 let (pda_a, _) = find_program_address(seeds, &prog_a);
177 let (pda_b, _) = find_program_address(seeds, &prog_b);
178 assert_ne!(
179 pda_a, pda_b,
180 "same seeds under different programs must yield different PDAs"
181 );
182 }
183
184 #[test]
185 fn bump_is_the_highest_off_curve() {
186 let program_id = test_program_id();
191 let program_bytes = program_id.as_bytes();
192 let (pda, bump) = find_program_address(&[b"palinurus"], &program_id);
193
194 for higher_u16 in ((bump as u16) + 1)..=255u16 {
195 let higher = higher_u16 as u8;
196 let mut hasher = Sha256::new();
197 hasher.update(b"palinurus");
198 hasher.update([higher]);
199 hasher.update(program_bytes);
200 hasher.update(PDA_MARKER);
201 let h: [u8; 32] = hasher.finalize().into();
202 let h_pk = Pubkey::from_bytes(h);
203 assert!(
204 is_on_curve(&h_pk),
205 "bump {higher} is higher than returned bump {bump} but is off-curve — search order is wrong"
206 );
207 }
208
209 let mut hasher = Sha256::new();
211 hasher.update(b"palinurus");
212 hasher.update([bump]);
213 hasher.update(program_bytes);
214 hasher.update(PDA_MARKER);
215 let h: [u8; 32] = hasher.finalize().into();
216 assert_eq!(pda.to_bytes(), h, "PDA must equal sha256(seeds || bump || program || PDA_MARKER)");
217 }
218
219 #[test]
220 fn empty_seeds_still_derive_an_off_curve_pda() {
221 let program_id = test_program_id();
223 let (pda, _bump) = find_program_address(&[], &program_id);
224 assert!(!is_on_curve(&pda), "empty-seed PDA must still be off-curve");
225 }
226
227 #[test]
237 fn matches_solana_web3_js_reference_two_seeds() {
238 let expected_pda_b58: &str = "Et5CGkEYE5YqNYbReBAaBTmkZ8txz2D4kcjCVhWcvT2p";
240 let expected_bump: u8 = 252;
241
242 let program_id = test_program_id();
243 let (pda, bump) = find_program_address(&[b"palinurus", b"depin-attest"], &program_id);
244 assert_eq!(pda.to_string(), expected_pda_b58, "PDA base58 must match @solana/web3.js reference");
245 assert_eq!(bump, expected_bump, "bump must match @solana/web3.js reference");
246 }
247
248 #[test]
250 fn matches_solana_web3_js_reference_single_seed() {
251 let expected_pda_b58: &str = "2Jbijq1B93p6CpXv2yz4hwoz1gn3hQLFHjy7zPobWWgo";
253 let expected_bump: u8 = 255;
254
255 let program_id = test_program_id();
256 let (pda, bump) = find_program_address(&[b"palinurus"], &program_id);
257 assert_eq!(pda.to_string(), expected_pda_b58, "PDA base58 must match @solana/web3.js reference");
258 assert_eq!(bump, expected_bump, "bump must match @solana/web3.js reference");
259 }
260}