1use crate::{instruction::*, state::*, *};
4use solana_program::{
5 account_info::{next_account_info, AccountInfo},
6 entrypoint::ProgramResult,
7 msg,
8 program_error::ProgramError,
9 program_pack::Pack,
10 pubkey::Pubkey,
11};
12
13#[cfg(test)]
14mod test_admin {
15 solana_program::declare_id!("563B79TEFBRx8f6vwJH1XWo85MSsJRaV3E2EdmwUtjmG");
16}
17
18fn is_admin(address: &Pubkey) -> bool {
19 if crate::admin::id() == *address {
20 return true;
21 }
22
23 #[cfg(test)]
24 if test_admin::id() == *address {
25 return true;
26 }
27
28 false
29}
30
31fn authenticate_admin(admin_info: &AccountInfo) -> ProgramResult {
32 if !is_admin(admin_info.key) {
33 msg!("Error: {} is not the admin", admin_info.key);
34 return Err(ProgramError::InvalidArgument);
35 }
36 if !admin_info.is_signer {
37 msg!("Error: {} is not a signer", admin_info.key);
38 return Err(ProgramError::MissingRequiredSignature);
39 }
40
41 Ok(())
42}
43
44pub fn process_instruction(
45 _program_id: &Pubkey,
46 accounts: &[AccountInfo],
47 input: &[u8],
48) -> ProgramResult {
49 let instruction = RegistryInstruction::unpack_from_slice(input)?;
50 let account_info_iter = &mut accounts.iter();
51 let participant_info = next_account_info(account_info_iter)?;
52 let mut participant = Participant::unpack_from_slice(&participant_info.data.borrow())?;
53
54 match instruction {
55 RegistryInstruction::Apply => {
56 msg!("Apply");
57 if participant.state != ParticipantState::Uninitialized {
58 msg!("Error: participant account is already initialized");
59 return Err(ProgramError::AccountAlreadyInitialized);
60 }
61
62 let mainnet_identity_info = next_account_info(account_info_iter)?;
63 let testnet_identity_info = next_account_info(account_info_iter)?;
64
65 if !mainnet_identity_info.is_signer {
66 msg!("Error: {} is not a signer", mainnet_identity_info.key);
67 return Err(ProgramError::MissingRequiredSignature);
68 }
69 if !testnet_identity_info.is_signer {
70 msg!("Error: {} is not a signer", testnet_identity_info.key);
71 return Err(ProgramError::MissingRequiredSignature);
72 }
73
74 participant.testnet_identity = *testnet_identity_info.key;
75 participant.mainnet_identity = *mainnet_identity_info.key;
76 participant.state = ParticipantState::Pending;
77 }
78 RegistryInstruction::Withdraw => {
79 msg!("Withdraw");
80 let identity_info = next_account_info(account_info_iter)?;
81 let refundee_info = next_account_info(account_info_iter)?;
82
83 if !identity_info.is_signer {
84 msg!("Error: {} is not a signer", identity_info.key);
85 return Err(ProgramError::MissingRequiredSignature);
86 }
87
88 if *identity_info.key != participant.testnet_identity
89 && *identity_info.key != participant.mainnet_identity
90 {
91 msg!("Error: {} is not authorized", identity_info.key);
92 return Err(ProgramError::MissingRequiredSignature);
93 }
94
95 **refundee_info.lamports.borrow_mut() += participant_info.lamports();
96 **participant_info.lamports.borrow_mut() = 0;
97 }
98 RegistryInstruction::Approve => {
99 msg!("Approve");
100 authenticate_admin(next_account_info(account_info_iter)?)?;
101 participant.state = ParticipantState::Approved;
102 }
103 RegistryInstruction::Reject => {
104 msg!("Reject");
105 authenticate_admin(next_account_info(account_info_iter)?)?;
106 participant.state = ParticipantState::Rejected;
107 }
108 RegistryInstruction::Rewrite(new_participant) => {
109 msg!("Rewrite");
110 authenticate_admin(next_account_info(account_info_iter)?)?;
111 participant = new_participant;
112 }
113 }
114
115 if participant.testnet_identity == participant.mainnet_identity {
116 msg!("Error: mainnet and testnet identities must be unique",);
117 Err(ProgramError::InvalidAccountData)
118 } else {
119 participant.pack_into_slice(&mut participant_info.data.borrow_mut());
120 Ok(())
121 }
122}
123
124#[cfg(test)]
125mod test {
126 use {
127 super::*,
128 assert_matches::assert_matches,
129 solana_program::{system_instruction::create_account, sysvar::rent::Rent},
130 solana_program_test::*,
131 solana_sdk::{
132 signature::{Keypair, Signer},
133 transaction::Transaction,
134 },
135 };
136
137 fn test_admin_keypair() -> Keypair {
138 let keypair = Keypair::from_bytes(&[
139 195, 121, 73, 133, 212, 8, 231, 45, 116, 99, 128, 66, 118, 174, 197, 26, 112, 146, 204,
140 201, 119, 40, 97, 2, 86, 10, 98, 116, 235, 40, 163, 221, 60, 185, 28, 52, 69, 70, 108,
141 96, 236, 253, 114, 203, 81, 219, 79, 136, 0, 185, 165, 101, 147, 67, 207, 255, 69, 83,
142 242, 34, 36, 32, 80, 87,
143 ])
144 .unwrap();
145 assert_eq!(keypair.pubkey(), test_admin::id());
146 keypair
147 }
148
149 #[tokio::test]
150 async fn test_signup() {
151 let program_id = crate::id();
152
153 let participant = Keypair::new();
154 let mainnet_validator_identity = Keypair::new();
155 let testnet_validator_identity = Keypair::new();
156
157 let (mut banks_client, payer, recent_blockhash) = ProgramTest::new(
158 "solana_foundation_delegation_program_registry",
159 program_id,
160 processor!(process_instruction),
161 )
162 .start()
163 .await;
164
165 let rent = Rent::default().minimum_balance(Participant::get_packed_len());
166
167 let mut transaction = Transaction::new_with_payer(
169 &[
170 create_account(
171 &payer.pubkey(),
172 &participant.pubkey(),
173 rent,
174 Participant::get_packed_len() as u64,
175 &program_id,
176 ),
177 apply(
178 participant.pubkey(),
179 mainnet_validator_identity.pubkey(),
180 testnet_validator_identity.pubkey(),
181 ),
182 ],
183 Some(&payer.pubkey()),
184 );
185 transaction.sign(
186 &[
187 &payer,
188 &participant,
189 &mainnet_validator_identity,
190 &testnet_validator_identity,
191 ],
192 recent_blockhash,
193 );
194 assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
195
196 let participant_state = banks_client
197 .get_packed_account_data::<Participant>(participant.pubkey())
198 .await
199 .unwrap();
200 assert_eq!(
201 participant_state,
202 Participant {
203 state: ParticipantState::Pending,
204 testnet_identity: testnet_validator_identity.pubkey(),
205 mainnet_identity: mainnet_validator_identity.pubkey()
206 }
207 );
208
209 let mut transaction = Transaction::new_with_payer(
211 &[apply(
212 participant.pubkey(),
213 mainnet_validator_identity.pubkey(),
214 testnet_validator_identity.pubkey(),
215 )],
216 Some(&payer.pubkey()),
217 );
218 transaction.sign(
219 &[
220 &payer,
221 &mainnet_validator_identity,
222 &testnet_validator_identity,
223 ],
224 recent_blockhash,
225 );
226 assert_matches!(banks_client.process_transaction(transaction).await, Err(_));
227
228 let mut transaction = Transaction::new_with_payer(
230 &[reject(participant.pubkey(), test_admin::id())],
231 Some(&payer.pubkey()),
232 );
233 transaction.sign(&[&payer, &test_admin_keypair()], recent_blockhash);
234 assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
235
236 assert_eq!(
237 banks_client
238 .get_packed_account_data::<Participant>(participant.pubkey())
239 .await
240 .unwrap()
241 .state,
242 ParticipantState::Rejected
243 );
244
245 let mut transaction = Transaction::new_with_payer(
247 &[approve(participant.pubkey(), test_admin::id())],
248 Some(&payer.pubkey()),
249 );
250 transaction.sign(&[&payer, &test_admin_keypair()], recent_blockhash);
251 assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
252
253 assert_eq!(
254 banks_client
255 .get_packed_account_data::<Participant>(participant.pubkey())
256 .await
257 .unwrap()
258 .state,
259 ParticipantState::Approved
260 );
261
262 let mut transaction = Transaction::new_with_payer(
264 &[approve(
265 participant.pubkey(),
266 testnet_validator_identity.pubkey(),
267 )],
268 Some(&payer.pubkey()),
269 );
270 transaction.sign(&[&payer, &testnet_validator_identity], recent_blockhash);
271 assert_matches!(banks_client.process_transaction(transaction).await, Err(_));
272
273 let mut transaction = Transaction::new_with_payer(
275 &[rewrite(
276 participant.pubkey(),
277 testnet_validator_identity.pubkey(),
278 Participant::default(),
279 )],
280 Some(&payer.pubkey()),
281 );
282 transaction.sign(&[&payer, &testnet_validator_identity], recent_blockhash);
283 assert_matches!(banks_client.process_transaction(transaction).await, Err(_));
284
285 let mut transaction = Transaction::new_with_payer(
287 &[rewrite(
288 participant.pubkey(),
289 test_admin::id(),
290 Participant {
291 state: ParticipantState::Pending,
292 testnet_identity: testnet_validator_identity.pubkey(),
293 mainnet_identity: testnet_validator_identity.pubkey(),
294 },
295 )],
296 Some(&payer.pubkey()),
297 );
298 transaction.sign(&[&payer, &test_admin_keypair()], recent_blockhash);
299 assert_matches!(banks_client.process_transaction(transaction).await, Err(_));
300
301 let mut transaction = Transaction::new_with_payer(
303 &[rewrite(
304 participant.pubkey(),
305 test_admin::id(),
306 Participant {
307 state: ParticipantState::Pending,
308 testnet_identity: testnet_validator_identity.pubkey(),
309 mainnet_identity: Pubkey::default(),
310 },
311 )],
312 Some(&payer.pubkey()),
313 );
314 transaction.sign(&[&payer, &test_admin_keypair()], recent_blockhash);
315 assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
316
317 let participant_state = banks_client
318 .get_packed_account_data::<Participant>(participant.pubkey())
319 .await
320 .unwrap();
321 assert_eq!(
322 participant_state,
323 Participant {
324 state: ParticipantState::Pending,
325 testnet_identity: testnet_validator_identity.pubkey(),
326 mainnet_identity: Pubkey::default(),
327 }
328 );
329
330 assert_eq!(
332 banks_client
333 .get_balance(testnet_validator_identity.pubkey())
334 .await
335 .unwrap(),
336 0
337 );
338 assert_eq!(
339 banks_client
340 .get_balance(participant.pubkey())
341 .await
342 .unwrap(),
343 rent
344 );
345 let mut transaction = Transaction::new_with_payer(
346 &[withdraw(
347 participant.pubkey(),
348 testnet_validator_identity.pubkey(),
349 testnet_validator_identity.pubkey(),
350 )],
351 Some(&payer.pubkey()),
352 );
353 transaction.sign(&[&payer, &testnet_validator_identity], recent_blockhash);
354 assert_matches!(banks_client.process_transaction(transaction).await, Ok(()));
355
356 assert_eq!(
357 banks_client
358 .get_balance(testnet_validator_identity.pubkey())
359 .await
360 .unwrap(),
361 rent
362 );
363 assert_eq!(
364 banks_client
365 .get_balance(participant.pubkey())
366 .await
367 .unwrap(),
368 0
369 );
370 }
371}