solana_foundation_delegation_program_registry/
processor.rs

1//! Program state processor
2
3use 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        // Create/Apply...
168        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        // Cannot Apply twice...
210        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        // Reject..
229        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        // Approve...
246        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        // Approve with wrong admin key, failure...
263        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        // Rewrite with wrong admin key, failure...
274        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        // Rewrite with duplicate identities, failure...
286        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        // Rewrite...
302        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        // Withdraw...
331        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}