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