cow_settlement_interface/pda/
buffer.rs1use solana_address::Address;
23use solana_program_error::ProgramError;
24use solana_pubkey::Pubkey;
25
26use crate::pda::{is_pda_with_signer_seeds, SETTLEMENT_SEED};
27use crate::SettlementError;
28
29pub const BUFFER_SEED: &[u8] = b"buffer";
31
32pub fn buffer_pda_seeds(mint: &[u8; 32]) -> [&[u8]; 3] {
39 [SETTLEMENT_SEED, mint, BUFFER_SEED]
40}
41
42pub fn buffer_pda_signer_seeds<'a>(mint: &'a [u8; 32], bump: &'a [u8; 1]) -> [&'a [u8]; 4] {
44 let [s0, s1, s2] = buffer_pda_seeds(mint);
45 [s0, s1, s2, bump]
46}
47
48pub fn find_buffer_pda(program_id: &Pubkey, mint: &Pubkey) -> (Pubkey, u8) {
50 Pubkey::find_program_address(&buffer_pda_seeds(mint.as_array()), program_id)
51}
52
53#[inline]
56#[must_use = "ignoring the output means ignoring the validation result"]
57pub fn validate_buffer_pda(
58 program_id: &Address,
59 buffer: &Address,
60 mint: &[u8; 32],
61 bump: u8,
62) -> Result<(), ProgramError> {
63 is_pda_with_signer_seeds(buffer, program_id, buffer_pda_signer_seeds(mint, &[bump]))
64 .then_some(())
65 .ok_or(SettlementError::PushSourceNotBuffer.into())
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71 use crate::pda::tests::assert_distinct_versions_yield_distinct_pdas;
72
73 #[test]
74 fn find_buffer_pda_uses_canonical_seeds() {
75 let token = Pubkey::new_unique();
76
77 crate::pda::tests::assert_canonical_bump(
78 |program_id| find_buffer_pda(program_id, &token),
79 buffer_pda_seeds(token.as_array()),
80 );
81 }
82
83 #[test]
84 fn distinct_versions_yield_distinct_buffer_pdas() {
85 let mint = Pubkey::new_unique();
86 let (pda, _) = find_buffer_pda(&crate::ID, &mint);
87
88 assert_distinct_versions_yield_distinct_pdas(&pda, &[mint.as_array(), BUFFER_SEED]);
89 }
90
91 #[test]
92 fn accepts_a_valid_address() {
93 let program_id = Pubkey::new_unique();
94 let mint = Pubkey::new_unique();
95 let (pda, bump) = find_buffer_pda(&program_id, &mint);
96
97 let buffer = crate::instruction::fixtures::fake_account(pda);
98 validate_buffer_pda(&program_id, buffer.address(), mint.as_array(), bump)
99 .expect("the canonical buffer PDA must be accepted");
100 }
101
102 #[test]
103 fn rejects_an_invalid_address() {
104 let program_id = Pubkey::new_unique();
105 let mint = Pubkey::new_unique();
106 let (_, bump) = find_buffer_pda(&program_id, &mint);
107
108 let buffer = crate::instruction::fixtures::fake_account(Pubkey::new_unique());
110 let err = validate_buffer_pda(&program_id, buffer.address(), mint.as_array(), bump)
111 .expect_err("a non-canonical address must be rejected");
112 assert_eq!(err, SettlementError::PushSourceNotBuffer.into());
113 }
114
115 #[test]
116 fn rejects_a_wrong_bump() {
117 let program_id = Pubkey::new_unique();
118 let mint = Pubkey::new_unique();
119 let (pda, bump) = find_buffer_pda(&program_id, &mint);
120
121 let buffer = crate::instruction::fixtures::fake_account(pda);
123 let err = validate_buffer_pda(&program_id, buffer.address(), mint.as_array(), bump ^ 1)
124 .expect_err("a wrong bump must be rejected");
125 assert_eq!(err, SettlementError::PushSourceNotBuffer.into());
126 }
127
128 mod proptest {
129 use ::proptest::prelude::*;
130
131 use super::*;
132
133 proptest! {
134 #[test]
135 fn distinct_tokens_yield_distinct_pdas(
136 program_id in any::<[u8; 32]>(),
137 token1 in any::<[u8; 32]>(),
138 token2 in any::<[u8; 32]>(),
139 ) {
140 prop_assume!(token1 != token2);
141 let program_id = Pubkey::new_from_array(program_id);
142 let (pda1, _) = find_buffer_pda(&program_id, &Pubkey::new_from_array(token1));
143 let (pda2, _) = find_buffer_pda(&program_id, &Pubkey::new_from_array(token2));
144 prop_assert_ne!(pda1, pda2);
145 }
146 }
147 }
148}