cow_settlement_interface/pda/
buffer.rs1use solana_address::Address;
28use solana_program_error::ProgramError;
29use solana_pubkey::Pubkey;
30
31use crate::data::intent::ENCODED_NATIVE_SOL_TRANSFER;
32use crate::pda::{is_pda_with_signer_seeds, SETTLEMENT_SEED};
33use crate::SettlementError;
34
35mod known_mints;
36pub use known_mints::KNOWN_MINTS;
37
38pub const BUFFER_SEED: &[u8] = b"buffer";
40
41pub const fn buffer_pda_seeds(mint: &[u8; 32]) -> [&[u8]; 3] {
48 [SETTLEMENT_SEED, mint, BUFFER_SEED]
49}
50
51pub fn buffer_pda_signer_seeds<'a>(mint: &'a [u8; 32], bump: &'a [u8; 1]) -> [&'a [u8]; 4] {
53 let [s0, s1, s2] = buffer_pda_seeds(mint);
54 [s0, s1, s2, bump]
55}
56
57pub fn find_buffer_pda(program_id: &Pubkey, mint: &Pubkey) -> (Pubkey, u8) {
59 Pubkey::find_program_address(&buffer_pda_seeds(mint.as_array()), program_id)
60}
61
62pub const NATIVE_SOL_BUFFER_SEED: &[u8] = b"buffer native sol";
64
65pub const NATIVE_SOL_BUFFER_PDA_SEEDS: [&[u8]; 2] = [SETTLEMENT_SEED, NATIVE_SOL_BUFFER_SEED];
67
68pub const NATIVE_SOL_BUFFER_PDA_AND_BUMP: ([u8; 32], u8) =
69 const_crypto::ed25519::derive_program_address(
70 &NATIVE_SOL_BUFFER_PDA_SEEDS,
71 crate::ID.as_array(),
72 );
73
74pub const NATIVE_SOL_BUFFER_PDA: Address =
77 Address::new_from_array(NATIVE_SOL_BUFFER_PDA_AND_BUMP.0);
78
79pub fn find_native_sol_buffer_pda(program_id: &Pubkey) -> (Pubkey, u8) {
81 Pubkey::find_program_address(&NATIVE_SOL_BUFFER_PDA_SEEDS, program_id)
82}
83
84struct KnownBuffer {
86 mint: [u8; 32],
87 address: [u8; 32],
88}
89
90#[allow(long_running_const_eval)]
93const KNOWN_BUFFERS: [KnownBuffer; KNOWN_MINTS.len() + 1] = {
94 let mut buffers = [const {
95 KnownBuffer {
96 mint: [0; 32],
97 address: [0; 32],
98 }
99 }; KNOWN_MINTS.len() + 1];
100 let mut i = 0;
101 while i < KNOWN_MINTS.len() {
102 let mint = const_crypto::bs58::decode_pubkey(KNOWN_MINTS[i]);
103 let (address, _) = const_crypto::ed25519::derive_program_address(
104 &buffer_pda_seeds(&mint),
105 crate::ID.as_array(),
106 );
107 buffers[i] = KnownBuffer { mint, address };
108 i += 1;
109 }
110 buffers[i] = KnownBuffer {
111 mint: ENCODED_NATIVE_SOL_TRANSFER.to_bytes(),
112 address: *NATIVE_SOL_BUFFER_PDA.as_array(),
113 };
114 assert!(i + 1 == buffers.len());
115 buffers
116};
117
118#[cfg(test)]
120const KNOWN_MINT_BUFFERS: &[KnownBuffer; KNOWN_MINTS.len()] = KNOWN_BUFFERS
121 .first_chunk()
122 .expect("KNOWN_BUFFERS ends with the known mint buffers");
123
124#[cfg(test)]
125const KNOWN_NATIVE_MINT_BUFFER: &KnownBuffer = &KNOWN_BUFFERS[KNOWN_MINTS.len()];
126
127const SLOT_BITS: u32 = 9;
138
139const KNOWN_BUFFER_MULTIPLIER: u64 = find_slot_multiplier(&KNOWN_BUFFERS);
141
142const KNOWN_BUFFER_SLOTS: [u8; 1 << SLOT_BITS] = {
145 assert!(
147 KNOWN_BUFFERS.len() < u8::MAX as usize,
148 "too many known mints for u8 indices"
149 );
150 let mut slots = [u8::MAX; 1 << SLOT_BITS];
151 let mut i = 0;
152 while i < KNOWN_BUFFERS.len() {
153 slots[slot(&KNOWN_BUFFERS[i].mint, KNOWN_BUFFER_MULTIPLIER)] = i as u8;
154 i += 1;
155 }
156 slots
157};
158
159const fn slot(mint: &[u8; 32], multiplier: u64) -> usize {
162 let key = u64::from_le_bytes(*mint.first_chunk().expect("a mint is longer than 8 bytes"));
163 (key.wrapping_mul(multiplier) >> (u64::BITS - SLOT_BITS)) as usize
164}
165
166const fn find_slot_multiplier(buffers: &[KnownBuffer]) -> u64 {
176 const ATTEMPTS: u32 = 100_000;
177 let mut attempt: u32 = 0;
178 while attempt < ATTEMPTS {
179 let digest = const_crypto::sha2::Sha256::new()
180 .update(&attempt.to_le_bytes())
181 .finalize();
182 let multiplier = u64::from_le_bytes(
183 *digest
184 .first_chunk()
185 .expect("a digest is longer than 8 bytes"),
186 );
187 if has_distinct_slots(buffers, multiplier) {
188 return multiplier;
189 }
190 attempt = attempt
191 .checked_add(1)
192 .expect("attempt stays below ATTEMPTS");
193 }
194 panic!("no multiplier gives every known mint its own slot");
195}
196
197const fn has_distinct_slots(buffers: &[KnownBuffer], multiplier: u64) -> bool {
199 let mut taken = [false; 1 << SLOT_BITS];
200 let mut i = 0;
201 while i < buffers.len() {
202 let slot = slot(&buffers[i].mint, multiplier);
203 if taken[slot] {
204 return false;
205 }
206 taken[slot] = true;
207 i = i.checked_add(1).expect("i stays below the buffer count");
208 }
209 true
210}
211
212fn known_buffer(mint: &[u8; 32]) -> Option<&'static KnownBuffer> {
214 let index = KNOWN_BUFFER_SLOTS[slot(mint, KNOWN_BUFFER_MULTIPLIER)];
215 KNOWN_BUFFERS
216 .get(usize::from(index))
217 .filter(|known| &known.mint == mint)
218}
219
220#[inline]
230#[must_use = "ignoring the output means ignoring the validation result"]
231pub fn validate_buffer_pda(
232 buffer: &Address,
233 mint: &[u8; 32],
234 bump: u8,
235) -> Result<(), ProgramError> {
236 let is_buffer = match known_buffer(mint) {
237 Some(known) => buffer.as_array() == &known.address,
238 None => {
239 is_pda_with_signer_seeds(buffer, &crate::ID, buffer_pda_signer_seeds(mint, &[bump]))
240 }
241 };
242 is_buffer
243 .then_some(())
244 .ok_or(SettlementError::PushSourceNotBuffer.into())
245}
246
247#[cfg(test)]
248mod tests {
249 use super::*;
250 use crate::pda::tests::assert_distinct_versions_yield_distinct_pdas;
251
252 #[test]
253 fn find_buffer_pda_uses_canonical_seeds() {
254 let token = Pubkey::new_unique();
255
256 crate::pda::tests::assert_canonical_bump(
257 |program_id| find_buffer_pda(program_id, &token),
258 buffer_pda_seeds(token.as_array()),
259 );
260 }
261
262 #[test]
263 fn distinct_versions_yield_distinct_buffer_pdas() {
264 let mint = Pubkey::new_unique();
265 let (pda, _) = find_buffer_pda(&crate::ID, &mint);
266
267 assert_distinct_versions_yield_distinct_pdas(&pda, &[mint.as_array(), BUFFER_SEED]);
268 }
269
270 #[test]
271 fn accepts_a_valid_address() {
272 let mint = Pubkey::new_unique();
273 let (pda, bump) = find_buffer_pda(&crate::ID, &mint);
274
275 let buffer = crate::instruction::fixtures::fake_account(pda);
276 validate_buffer_pda(buffer.address(), mint.as_array(), bump)
277 .expect("the canonical buffer PDA must be accepted");
278 }
279
280 #[test]
281 fn rejects_an_invalid_address() {
282 let mint = Pubkey::new_unique();
283 let (_, bump) = find_buffer_pda(&crate::ID, &mint);
284
285 let buffer = crate::instruction::fixtures::fake_account(Pubkey::new_unique());
287 let err = validate_buffer_pda(buffer.address(), mint.as_array(), bump)
288 .expect_err("a non-canonical address must be rejected");
289 assert_eq!(err, SettlementError::PushSourceNotBuffer.into());
290 }
291
292 #[test]
293 fn rejects_a_wrong_bump() {
294 let mint = Pubkey::new_unique();
295 let (pda, bump) = find_buffer_pda(&crate::ID, &mint);
296
297 let buffer = crate::instruction::fixtures::fake_account(pda);
299 let err = validate_buffer_pda(buffer.address(), mint.as_array(), bump ^ 1)
300 .expect_err("a wrong bump must be rejected");
301 assert_eq!(err, SettlementError::PushSourceNotBuffer.into());
302 }
303
304 const A_KNOWN_MINT: Pubkey = Pubkey::from_str_const(KNOWN_MINTS[0]);
305
306 #[test]
307 fn pinned_native_sol_buffer_pda_is_canonical() {
308 let (pda, bump) = find_native_sol_buffer_pda(&crate::ID);
309 assert_eq!(NATIVE_SOL_BUFFER_PDA_AND_BUMP, (*pda.as_array(), bump));
310 crate::pda::tests::assert_canonical_bump(
311 find_native_sol_buffer_pda,
312 NATIVE_SOL_BUFFER_PDA_SEEDS,
313 );
314 }
315
316 #[test]
317 fn distinct_versions_yield_distinct_native_sol_buffer_pdas() {
318 assert_distinct_versions_yield_distinct_pdas(
319 &NATIVE_SOL_BUFFER_PDA,
320 &[NATIVE_SOL_BUFFER_SEED],
321 );
322 }
323
324 #[test]
325 fn native_sol_buffer_is_not_the_native_markers_mint_buffer() {
326 let (marker_buffer, _) = find_buffer_pda(&crate::ID, &ENCODED_NATIVE_SOL_TRANSFER);
327 assert_ne!(NATIVE_SOL_BUFFER_PDA, marker_buffer);
328 }
329
330 #[test]
331 fn known_buffers_stores_canonical_native_sol_transfer() {
332 assert_eq!(
333 KNOWN_NATIVE_MINT_BUFFER.mint,
334 ENCODED_NATIVE_SOL_TRANSFER.to_bytes()
335 );
336 assert_eq!(
337 KNOWN_NATIVE_MINT_BUFFER.address,
338 NATIVE_SOL_BUFFER_PDA.to_bytes()
339 );
340 }
341
342 #[test]
343 fn known_mint_buffers_are_canonical() {
344 for known in KNOWN_MINT_BUFFERS {
345 let (pda, _) = find_buffer_pda(&crate::ID, &Pubkey::new_from_array(known.mint));
346 assert_eq!(known.address, *pda.as_array());
347 }
348 }
349
350 #[test]
351 fn every_known_mint_looks_up_its_own_buffer() {
352 for mint in KNOWN_MINTS {
353 let mint = Pubkey::from_str_const(mint);
354 let known = known_buffer(mint.as_array())
355 .unwrap_or_else(|| panic!("{mint} must have a known buffer"));
356 assert_eq!(known.mint, *mint.as_array());
357 }
358 }
359
360 #[test]
361 fn unknown_mint_sharing_a_slot_has_no_known_buffer() {
362 let mut mint = *A_KNOWN_MINT.as_array();
363 mint[31] ^= 1;
364
365 assert_ne!(
367 KNOWN_BUFFER_SLOTS[slot(&mint, KNOWN_BUFFER_MULTIPLIER)],
368 u8::MAX
369 );
370
371 assert!(known_buffer(&mint).is_none());
373 }
374
375 #[test]
376 fn unknown_mint_in_an_empty_slot_has_no_known_buffer() {
377 let mint = (0u64..)
378 .map(|seed| *crate::fixtures::pubkey_from_seed(&seed.to_string()).as_array())
379 .find(|mint| KNOWN_BUFFER_SLOTS[slot(mint, KNOWN_BUFFER_MULTIPLIER)] == u8::MAX)
380 .expect("most slots are empty");
381 assert!(known_buffer(&mint).is_none());
382 }
383
384 #[test]
387 #[should_panic(expected = "no multiplier gives every known mint its own slot")]
388 fn find_slot_multiplier_rejects_mints_sharing_their_leading_bytes() {
389 let mut other = *A_KNOWN_MINT.as_array();
390 other[31] ^= 1;
391 let _ = find_slot_multiplier(&[
392 KnownBuffer {
393 mint: *A_KNOWN_MINT.as_array(),
394 address: [0; 32],
395 },
396 KnownBuffer {
397 mint: other,
398 address: [0; 32],
399 },
400 ]);
401 }
402
403 #[test]
404 fn accepts_the_known_buffer() {
405 let (pda, bump) = find_buffer_pda(&crate::ID, &A_KNOWN_MINT);
406
407 validate_buffer_pda(&pda, A_KNOWN_MINT.as_array(), bump)
408 .expect("the known buffer PDA must be accepted");
409 }
410
411 #[test]
412 fn rejects_an_invalid_address_for_a_known_mint() {
413 let (_, bump) = find_buffer_pda(&crate::ID, &A_KNOWN_MINT);
414
415 let err = validate_buffer_pda(&Pubkey::new_unique(), A_KNOWN_MINT.as_array(), bump)
416 .expect_err("a non-canonical address must be rejected");
417 assert_eq!(err, SettlementError::PushSourceNotBuffer.into());
418 }
419
420 #[test]
421 fn accepts_the_native_sol_buffer() {
422 validate_buffer_pda(
423 &NATIVE_SOL_BUFFER_PDA,
424 ENCODED_NATIVE_SOL_TRANSFER.as_array(),
425 NATIVE_SOL_BUFFER_PDA_AND_BUMP.1,
426 )
427 .expect("the native SOL buffer must be accepted");
428 }
429
430 #[test]
431 fn rejects_the_native_markers_mint_buffer_as_the_native_sol_buffer() {
432 let (pda, bump) = find_buffer_pda(&crate::ID, &ENCODED_NATIVE_SOL_TRANSFER);
433
434 let err = validate_buffer_pda(&pda, ENCODED_NATIVE_SOL_TRANSFER.as_array(), bump)
435 .expect_err("only the native SOL buffer holds native SOL");
436 assert_eq!(err, SettlementError::PushSourceNotBuffer.into());
437 }
438
439 #[test]
440 fn ignores_the_bump_for_a_known_mint() {
441 let (pda, bump) = find_buffer_pda(&crate::ID, &A_KNOWN_MINT);
442
443 validate_buffer_pda(&pda, A_KNOWN_MINT.as_array(), bump ^ 1)
444 .expect("the known buffer PDA must be accepted whatever the bump");
445 }
446
447 mod proptest {
448 use ::proptest::prelude::*;
449
450 use super::*;
451
452 proptest! {
453 #[test]
454 fn distinct_tokens_yield_distinct_pdas(
455 program_id in any::<[u8; 32]>(),
456 token1 in any::<[u8; 32]>(),
457 token2 in any::<[u8; 32]>(),
458 ) {
459 prop_assume!(token1 != token2);
460 let program_id = Pubkey::new_from_array(program_id);
461 let (pda1, _) = find_buffer_pda(&program_id, &Pubkey::new_from_array(token1));
462 let (pda2, _) = find_buffer_pda(&program_id, &Pubkey::new_from_array(token2));
463 prop_assert_ne!(pda1, pda2);
464 }
465 }
466 }
467}