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#[must_use = "ignoring the output means ignoring the validation result"]
55pub fn validate_buffer_pda(
56 program_id: &Address,
57 buffer: &Address,
58 mint: &Address,
59 bump: u8,
60) -> Result<(), ProgramError> {
61 is_pda_with_signer_seeds(
62 buffer,
63 program_id,
64 buffer_pda_signer_seeds(&mint.to_bytes(), &[bump]),
65 )
66 .then_some(())
67 .ok_or(SettlementError::PushSourceNotBuffer.into())
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::pda::tests::assert_distinct_versions_yield_distinct_pdas;
74
75 #[test]
76 fn find_buffer_pda_uses_canonical_seeds() {
77 let token = Pubkey::new_unique();
78
79 crate::pda::tests::assert_canonical_bump(
80 |program_id| find_buffer_pda(program_id, &token),
81 buffer_pda_seeds(token.as_array()),
82 );
83 }
84
85 #[test]
86 fn distinct_versions_yield_distinct_buffer_pdas() {
87 let mint = Pubkey::new_unique();
88 let (pda, _) = find_buffer_pda(&crate::ID, &mint);
89
90 assert_distinct_versions_yield_distinct_pdas(&pda, &[mint.as_array(), BUFFER_SEED]);
91 }
92
93 #[test]
94 fn accepts_a_valid_address() {
95 let program_id = Pubkey::new_unique();
96 let mint = Pubkey::new_unique();
97 let (pda, bump) = find_buffer_pda(&program_id, &mint);
98
99 let buffer = crate::instruction::fixtures::fake_account(pda);
100 validate_buffer_pda(&program_id, buffer.address(), &mint, bump)
101 .expect("the canonical buffer PDA must be accepted");
102 }
103
104 #[test]
105 fn rejects_an_invalid_address() {
106 let program_id = Pubkey::new_unique();
107 let mint = Pubkey::new_unique();
108 let (_, bump) = find_buffer_pda(&program_id, &mint);
109
110 let buffer = crate::instruction::fixtures::fake_account(Pubkey::new_unique());
112 let err = validate_buffer_pda(&program_id, buffer.address(), &mint, bump)
113 .expect_err("a non-canonical address must be rejected");
114 assert_eq!(err, SettlementError::PushSourceNotBuffer.into());
115 }
116
117 #[test]
118 fn rejects_a_wrong_bump() {
119 let program_id = Pubkey::new_unique();
120 let mint = Pubkey::new_unique();
121 let (pda, bump) = find_buffer_pda(&program_id, &mint);
122
123 let buffer = crate::instruction::fixtures::fake_account(pda);
125 let err = validate_buffer_pda(&program_id, buffer.address(), &mint, bump ^ 1)
126 .expect_err("a wrong bump must be rejected");
127 assert_eq!(err, SettlementError::PushSourceNotBuffer.into());
128 }
129
130 mod proptest {
131 use ::proptest::prelude::*;
132
133 use super::*;
134
135 proptest! {
136 #[test]
137 fn distinct_tokens_yield_distinct_pdas(
138 program_id in any::<[u8; 32]>(),
139 token1 in any::<[u8; 32]>(),
140 token2 in any::<[u8; 32]>(),
141 ) {
142 prop_assume!(token1 != token2);
143 let program_id = Pubkey::new_from_array(program_id);
144 let (pda1, _) = find_buffer_pda(&program_id, &Pubkey::new_from_array(token1));
145 let (pda2, _) = find_buffer_pda(&program_id, &Pubkey::new_from_array(token2));
146 prop_assert_ne!(pda1, pda2);
147 }
148 }
149 }
150}