solana_system_program/
system_instruction.rs

1use {
2    solana_instruction::error::InstructionError,
3    solana_nonce::{
4        self as nonce,
5        state::{DurableNonce, State},
6        versions::{AuthorizeNonceError, Versions},
7    },
8    solana_program_runtime::invoke_context::InvokeContext,
9    solana_pubkey::Pubkey,
10    solana_svm_log_collector::ic_msg,
11    solana_system_interface::error::SystemError,
12    solana_sysvar::rent::Rent,
13    solana_transaction_context::{BorrowedAccount, IndexOfAccount, InstructionContext},
14    std::collections::HashSet,
15};
16
17/// Addition that returns [`InstructionError::InsufficientFunds`] on overflow.
18fn checked_add(a: u64, b: u64) -> Result<u64, InstructionError> {
19    a.checked_add(b).ok_or(InstructionError::InsufficientFunds)
20}
21
22pub fn advance_nonce_account(
23    account: &mut BorrowedAccount,
24    signers: &HashSet<Pubkey>,
25    invoke_context: &InvokeContext,
26) -> Result<(), InstructionError> {
27    if !account.is_writable() {
28        ic_msg!(
29            invoke_context,
30            "Advance nonce account: Account {} must be writeable",
31            account.get_key()
32        );
33        return Err(InstructionError::InvalidArgument);
34    }
35
36    let state: Versions = account.get_state()?;
37    match state.state() {
38        State::Initialized(data) => {
39            if !signers.contains(&data.authority) {
40                ic_msg!(
41                    invoke_context,
42                    "Advance nonce account: Account {} must be a signer",
43                    data.authority
44                );
45                return Err(InstructionError::MissingRequiredSignature);
46            }
47            let next_durable_nonce =
48                DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash);
49            if data.durable_nonce == next_durable_nonce {
50                ic_msg!(
51                    invoke_context,
52                    "Advance nonce account: nonce can only advance once per slot"
53                );
54                return Err(SystemError::NonceBlockhashNotExpired.into());
55            }
56
57            let new_data = nonce::state::Data::new(
58                data.authority,
59                next_durable_nonce,
60                invoke_context
61                    .environment_config
62                    .blockhash_lamports_per_signature,
63            );
64            account.set_state(&Versions::new(State::Initialized(new_data)))
65        }
66        State::Uninitialized => {
67            ic_msg!(
68                invoke_context,
69                "Advance nonce account: Account {} state is invalid",
70                account.get_key()
71            );
72            Err(InstructionError::InvalidAccountData)
73        }
74    }
75}
76
77pub(crate) fn withdraw_nonce_account(
78    from_account_index: IndexOfAccount,
79    lamports: u64,
80    to_account_index: IndexOfAccount,
81    rent: &Rent,
82    signers: &HashSet<Pubkey>,
83    invoke_context: &InvokeContext,
84    instruction_context: &InstructionContext,
85) -> Result<(), InstructionError> {
86    let mut from = instruction_context.try_borrow_instruction_account(from_account_index)?;
87    if !from.is_writable() {
88        ic_msg!(
89            invoke_context,
90            "Withdraw nonce account: Account {} must be writeable",
91            from.get_key()
92        );
93        return Err(InstructionError::InvalidArgument);
94    }
95
96    let state: Versions = from.get_state()?;
97    let signer = match state.state() {
98        State::Uninitialized => {
99            if lamports > from.get_lamports() {
100                ic_msg!(
101                    invoke_context,
102                    "Withdraw nonce account: insufficient lamports {}, need {}",
103                    from.get_lamports(),
104                    lamports,
105                );
106                return Err(InstructionError::InsufficientFunds);
107            }
108            *from.get_key()
109        }
110        State::Initialized(ref data) => {
111            if lamports == from.get_lamports() {
112                let durable_nonce =
113                    DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash);
114                if data.durable_nonce == durable_nonce {
115                    ic_msg!(
116                        invoke_context,
117                        "Withdraw nonce account: nonce can only advance once per slot"
118                    );
119                    return Err(SystemError::NonceBlockhashNotExpired.into());
120                }
121                from.set_state(&Versions::new(State::Uninitialized))?;
122            } else {
123                let min_balance = rent.minimum_balance(from.get_data().len());
124                let amount = checked_add(lamports, min_balance)?;
125                if amount > from.get_lamports() {
126                    ic_msg!(
127                        invoke_context,
128                        "Withdraw nonce account: insufficient lamports {}, need {}",
129                        from.get_lamports(),
130                        amount,
131                    );
132                    return Err(InstructionError::InsufficientFunds);
133                }
134            }
135            data.authority
136        }
137    };
138
139    if !signers.contains(&signer) {
140        ic_msg!(
141            invoke_context,
142            "Withdraw nonce account: Account {} must sign",
143            signer
144        );
145        return Err(InstructionError::MissingRequiredSignature);
146    }
147
148    from.checked_sub_lamports(lamports)?;
149    drop(from);
150    let mut to = instruction_context.try_borrow_instruction_account(to_account_index)?;
151    to.checked_add_lamports(lamports)?;
152
153    Ok(())
154}
155
156pub(crate) fn initialize_nonce_account(
157    account: &mut BorrowedAccount,
158    nonce_authority: &Pubkey,
159    rent: &Rent,
160    invoke_context: &InvokeContext,
161) -> Result<(), InstructionError> {
162    if !account.is_writable() {
163        ic_msg!(
164            invoke_context,
165            "Initialize nonce account: Account {} must be writeable",
166            account.get_key()
167        );
168        return Err(InstructionError::InvalidArgument);
169    }
170
171    match account.get_state::<Versions>()?.state() {
172        State::Uninitialized => {
173            let min_balance = rent.minimum_balance(account.get_data().len());
174            if account.get_lamports() < min_balance {
175                ic_msg!(
176                    invoke_context,
177                    "Initialize nonce account: insufficient lamports {}, need {}",
178                    account.get_lamports(),
179                    min_balance
180                );
181                return Err(InstructionError::InsufficientFunds);
182            }
183            let durable_nonce =
184                DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash);
185            let data = nonce::state::Data::new(
186                *nonce_authority,
187                durable_nonce,
188                invoke_context
189                    .environment_config
190                    .blockhash_lamports_per_signature,
191            );
192            let state = State::Initialized(data);
193            account.set_state(&Versions::new(state))
194        }
195        State::Initialized(_) => {
196            ic_msg!(
197                invoke_context,
198                "Initialize nonce account: Account {} state is invalid",
199                account.get_key()
200            );
201            Err(InstructionError::InvalidAccountData)
202        }
203    }
204}
205
206pub(crate) fn authorize_nonce_account(
207    account: &mut BorrowedAccount,
208    nonce_authority: &Pubkey,
209    signers: &HashSet<Pubkey>,
210    invoke_context: &InvokeContext,
211) -> Result<(), InstructionError> {
212    if !account.is_writable() {
213        ic_msg!(
214            invoke_context,
215            "Authorize nonce account: Account {} must be writeable",
216            account.get_key()
217        );
218        return Err(InstructionError::InvalidArgument);
219    }
220    match account
221        .get_state::<Versions>()?
222        .authorize(signers, *nonce_authority)
223    {
224        Ok(versions) => account.set_state(&versions),
225        Err(AuthorizeNonceError::Uninitialized) => {
226            ic_msg!(
227                invoke_context,
228                "Authorize nonce account: Account {} state is invalid",
229                account.get_key()
230            );
231            Err(InstructionError::InvalidAccountData)
232        }
233        Err(AuthorizeNonceError::MissingRequiredSignature(account_authority)) => {
234            ic_msg!(
235                invoke_context,
236                "Authorize nonce account: Account {} must sign",
237                account_authority
238            );
239            Err(InstructionError::MissingRequiredSignature)
240        }
241    }
242}
243
244#[cfg(test)]
245mod test {
246    use {
247        super::*,
248        assert_matches::assert_matches,
249        solana_account::AccountSharedData,
250        solana_nonce::{self as nonce, state::State},
251        solana_nonce_account::{create_account, verify_nonce_account},
252        solana_program_runtime::with_mock_invoke_context,
253        solana_sdk_ids::system_program,
254        solana_sha256_hasher::hash,
255        solana_transaction_context::InstructionAccount,
256    };
257
258    pub const NONCE_ACCOUNT_INDEX: IndexOfAccount = 0;
259    pub const WITHDRAW_TO_ACCOUNT_INDEX: IndexOfAccount = 1;
260
261    macro_rules! push_instruction_context {
262        ($invoke_context:expr, $transaction_context:ident, $instruction_context:ident, $instruction_accounts:ident) => {
263            $invoke_context
264                .transaction_context
265                .configure_next_instruction_for_tests(2, $instruction_accounts, &[])
266                .unwrap();
267            $invoke_context.push().unwrap();
268            let $transaction_context = &$invoke_context.transaction_context;
269            let $instruction_context = $transaction_context
270                .get_current_instruction_context()
271                .unwrap();
272        };
273    }
274
275    macro_rules! prepare_mockup {
276        ($invoke_context:ident, $instruction_accounts:ident, $rent:ident) => {
277            let $rent = Rent {
278                lamports_per_byte_year: 42,
279                ..Rent::default()
280            };
281            let from_lamports = $rent.minimum_balance(State::size()) + 42;
282            let transaction_accounts = vec![
283                (
284                    Pubkey::new_unique(),
285                    create_account(from_lamports).into_inner(),
286                ),
287                (Pubkey::new_unique(), create_account(42).into_inner()),
288                (system_program::id(), AccountSharedData::default()),
289            ];
290            let $instruction_accounts = vec![
291                InstructionAccount::new(0, true, true),
292                InstructionAccount::new(1, false, true),
293            ];
294            with_mock_invoke_context!($invoke_context, transaction_context, transaction_accounts);
295        };
296    }
297
298    macro_rules! set_invoke_context_blockhash {
299        ($invoke_context:expr, $seed:expr) => {
300            $invoke_context.environment_config.blockhash =
301                hash(&bincode::serialize(&$seed).unwrap());
302            $invoke_context
303                .environment_config
304                .blockhash_lamports_per_signature = ($seed as u64).saturating_mul(100);
305        };
306    }
307
308    #[test]
309    fn default_is_uninitialized() {
310        assert_eq!(State::default(), State::Uninitialized)
311    }
312
313    #[test]
314    fn expected_behavior() {
315        prepare_mockup!(invoke_context, instruction_accounts, rent);
316        push_instruction_context!(
317            invoke_context,
318            transaction_context,
319            instruction_context,
320            instruction_accounts
321        );
322        let mut nonce_account = instruction_context
323            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
324            .unwrap();
325        let data = nonce::state::Data {
326            authority: *nonce_account.get_key(),
327            ..nonce::state::Data::default()
328        };
329        let mut signers = HashSet::new();
330        signers.insert(*nonce_account.get_key());
331        let versions = nonce_account.get_state::<Versions>().unwrap();
332        // New is in Uninitialzed state
333        assert_eq!(versions.state(), &State::Uninitialized);
334        set_invoke_context_blockhash!(invoke_context, 95);
335        let authorized = *nonce_account.get_key();
336        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
337        let versions = nonce_account.get_state::<Versions>().unwrap();
338        let data = nonce::state::Data::new(
339            data.authority,
340            DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash),
341            invoke_context
342                .environment_config
343                .blockhash_lamports_per_signature,
344        );
345        // First nonce instruction drives state from Uninitialized to Initialized
346        assert_eq!(versions.state(), &State::Initialized(data.clone()));
347        set_invoke_context_blockhash!(invoke_context, 63);
348        advance_nonce_account(&mut nonce_account, &signers, &invoke_context).unwrap();
349        let versions = nonce_account.get_state::<Versions>().unwrap();
350        let data = nonce::state::Data::new(
351            data.authority,
352            DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash),
353            invoke_context
354                .environment_config
355                .blockhash_lamports_per_signature,
356        );
357        // Second nonce instruction consumes and replaces stored nonce
358        assert_eq!(versions.state(), &State::Initialized(data.clone()));
359        set_invoke_context_blockhash!(invoke_context, 31);
360        advance_nonce_account(&mut nonce_account, &signers, &invoke_context).unwrap();
361        let versions = nonce_account.get_state::<Versions>().unwrap();
362        let data = nonce::state::Data::new(
363            data.authority,
364            DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash),
365            invoke_context
366                .environment_config
367                .blockhash_lamports_per_signature,
368        );
369        // Third nonce instruction for fun and profit
370        assert_eq!(versions.state(), &State::Initialized(data));
371
372        set_invoke_context_blockhash!(invoke_context, 0);
373        let to_account = instruction_context
374            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
375            .unwrap();
376        let withdraw_lamports = nonce_account.get_lamports();
377        let expect_nonce_lamports = nonce_account.get_lamports() - withdraw_lamports;
378        let expect_to_lamports = to_account.get_lamports() + withdraw_lamports;
379        drop(nonce_account);
380        drop(to_account);
381        withdraw_nonce_account(
382            NONCE_ACCOUNT_INDEX,
383            withdraw_lamports,
384            WITHDRAW_TO_ACCOUNT_INDEX,
385            &rent,
386            &signers,
387            &invoke_context,
388            &instruction_context,
389        )
390        .unwrap();
391        let nonce_account = instruction_context
392            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
393            .unwrap();
394        let to_account = instruction_context
395            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
396            .unwrap();
397        // Empties Account balance
398        assert_eq!(nonce_account.get_lamports(), expect_nonce_lamports);
399        // Account balance goes to `to`
400        assert_eq!(to_account.get_lamports(), expect_to_lamports);
401        let versions = nonce_account.get_state::<Versions>().unwrap();
402        // Empty balance deinitializes data
403        assert_eq!(versions.state(), &State::Uninitialized);
404    }
405
406    #[test]
407    fn nonce_inx_initialized_account_not_signer_fail() {
408        prepare_mockup!(invoke_context, instruction_accounts, rent);
409        push_instruction_context!(
410            invoke_context,
411            transaction_context,
412            instruction_context,
413            instruction_accounts
414        );
415        let mut nonce_account = instruction_context
416            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
417            .unwrap();
418        set_invoke_context_blockhash!(invoke_context, 31);
419        let authority = *nonce_account.get_key();
420        initialize_nonce_account(&mut nonce_account, &authority, &rent, &invoke_context).unwrap();
421        let versions = nonce_account.get_state::<Versions>().unwrap();
422        let data = nonce::state::Data::new(
423            authority,
424            DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash),
425            invoke_context
426                .environment_config
427                .blockhash_lamports_per_signature,
428        );
429        assert_eq!(versions.state(), &State::Initialized(data));
430        // Nonce account did not sign
431        let signers = HashSet::new();
432        set_invoke_context_blockhash!(invoke_context, 0);
433        let result = advance_nonce_account(&mut nonce_account, &signers, &invoke_context);
434        assert_eq!(result, Err(InstructionError::MissingRequiredSignature));
435    }
436
437    #[test]
438    fn nonce_inx_too_early_fail() {
439        prepare_mockup!(invoke_context, instruction_accounts, rent);
440        push_instruction_context!(
441            invoke_context,
442            transaction_context,
443            instruction_context,
444            instruction_accounts
445        );
446        let mut nonce_account = instruction_context
447            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
448            .unwrap();
449        let mut signers = HashSet::new();
450        signers.insert(*nonce_account.get_key());
451        set_invoke_context_blockhash!(invoke_context, 63);
452        let authorized = *nonce_account.get_key();
453        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
454        let result = advance_nonce_account(&mut nonce_account, &signers, &invoke_context);
455        assert_eq!(result, Err(SystemError::NonceBlockhashNotExpired.into()));
456    }
457
458    #[test]
459    fn nonce_inx_uninitialized_account_fail() {
460        prepare_mockup!(invoke_context, instruction_accounts, rent);
461        push_instruction_context!(
462            invoke_context,
463            transaction_context,
464            instruction_context,
465            instruction_accounts
466        );
467        let mut nonce_account = instruction_context
468            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
469            .unwrap();
470        let mut signers = HashSet::new();
471        signers.insert(*nonce_account.get_key());
472        set_invoke_context_blockhash!(invoke_context, 63);
473        let result = advance_nonce_account(&mut nonce_account, &signers, &invoke_context);
474        assert_eq!(result, Err(InstructionError::InvalidAccountData));
475    }
476
477    #[test]
478    fn nonce_inx_independent_nonce_authority_ok() {
479        prepare_mockup!(invoke_context, instruction_accounts, rent);
480        push_instruction_context!(
481            invoke_context,
482            transaction_context,
483            instruction_context,
484            instruction_accounts
485        );
486        let mut nonce_account = instruction_context
487            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
488            .unwrap();
489        let nonce_authority = instruction_context
490            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX + 1)
491            .unwrap();
492        let mut signers = HashSet::new();
493        signers.insert(*nonce_account.get_key());
494        set_invoke_context_blockhash!(invoke_context, 63);
495        let authorized = *nonce_authority.get_key();
496        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
497        let mut signers = HashSet::new();
498        signers.insert(authorized);
499        set_invoke_context_blockhash!(invoke_context, 31);
500        let result = advance_nonce_account(&mut nonce_account, &signers, &invoke_context);
501        assert_eq!(result, Ok(()));
502    }
503
504    #[test]
505    fn nonce_inx_no_nonce_authority_sig_fail() {
506        prepare_mockup!(invoke_context, instruction_accounts, rent);
507        push_instruction_context!(
508            invoke_context,
509            transaction_context,
510            instruction_context,
511            instruction_accounts
512        );
513        let mut nonce_account = instruction_context
514            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
515            .unwrap();
516        let nonce_authority = instruction_context
517            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX + 1)
518            .unwrap();
519        let mut signers = HashSet::new();
520        signers.insert(*nonce_account.get_key());
521        set_invoke_context_blockhash!(invoke_context, 63);
522        let authorized = *nonce_authority.get_key();
523        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
524        let result = advance_nonce_account(&mut nonce_account, &signers, &invoke_context);
525        assert_eq!(result, Err(InstructionError::MissingRequiredSignature));
526    }
527
528    #[test]
529    fn withdraw_inx_unintialized_acc_ok() {
530        prepare_mockup!(invoke_context, instruction_accounts, rent);
531        push_instruction_context!(
532            invoke_context,
533            transaction_context,
534            instruction_context,
535            instruction_accounts
536        );
537        let nonce_account = instruction_context
538            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
539            .unwrap();
540        let to_account = instruction_context
541            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
542            .unwrap();
543        let versions = nonce_account.get_state::<Versions>().unwrap();
544        assert_eq!(versions.state(), &State::Uninitialized);
545        let mut signers = HashSet::new();
546        signers.insert(*nonce_account.get_key());
547        set_invoke_context_blockhash!(invoke_context, 0);
548        let withdraw_lamports = nonce_account.get_lamports();
549        let expect_from_lamports = nonce_account.get_lamports() - withdraw_lamports;
550        let expect_to_lamports = to_account.get_lamports() + withdraw_lamports;
551        drop(nonce_account);
552        drop(to_account);
553        withdraw_nonce_account(
554            NONCE_ACCOUNT_INDEX,
555            withdraw_lamports,
556            WITHDRAW_TO_ACCOUNT_INDEX,
557            &rent,
558            &signers,
559            &invoke_context,
560            &instruction_context,
561        )
562        .unwrap();
563        let nonce_account = instruction_context
564            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
565            .unwrap();
566        let to_account = instruction_context
567            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
568            .unwrap();
569        let versions = nonce_account.get_state::<Versions>().unwrap();
570        assert_eq!(versions.state(), &State::Uninitialized);
571        assert_eq!(nonce_account.get_lamports(), expect_from_lamports);
572        assert_eq!(to_account.get_lamports(), expect_to_lamports);
573    }
574
575    #[test]
576    fn withdraw_inx_unintialized_acc_unsigned_fail() {
577        prepare_mockup!(invoke_context, instruction_accounts, rent);
578        push_instruction_context!(
579            invoke_context,
580            transaction_context,
581            instruction_context,
582            instruction_accounts
583        );
584        let nonce_account = instruction_context
585            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
586            .unwrap();
587        let to_account = instruction_context
588            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
589            .unwrap();
590        let versions = nonce_account.get_state::<Versions>().unwrap();
591        assert_eq!(versions.state(), &State::Uninitialized);
592        let signers = HashSet::new();
593        set_invoke_context_blockhash!(invoke_context, 0);
594        let withdraw_lamports = nonce_account.get_lamports();
595        drop(nonce_account);
596        drop(to_account);
597        let result = withdraw_nonce_account(
598            NONCE_ACCOUNT_INDEX,
599            withdraw_lamports,
600            WITHDRAW_TO_ACCOUNT_INDEX,
601            &rent,
602            &signers,
603            &invoke_context,
604            &instruction_context,
605        );
606        assert_eq!(result, Err(InstructionError::MissingRequiredSignature));
607    }
608
609    #[test]
610    fn withdraw_inx_unintialized_acc_insuff_funds_fail() {
611        prepare_mockup!(invoke_context, instruction_accounts, rent);
612        push_instruction_context!(
613            invoke_context,
614            transaction_context,
615            instruction_context,
616            instruction_accounts
617        );
618        let nonce_account = instruction_context
619            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
620            .unwrap();
621        let versions = nonce_account.get_state::<Versions>().unwrap();
622        assert_eq!(versions.state(), &State::Uninitialized);
623        let mut signers = HashSet::new();
624        signers.insert(*nonce_account.get_key());
625        set_invoke_context_blockhash!(invoke_context, 0);
626        let withdraw_lamports = nonce_account.get_lamports() + 1;
627        drop(nonce_account);
628        let result = withdraw_nonce_account(
629            NONCE_ACCOUNT_INDEX,
630            withdraw_lamports,
631            WITHDRAW_TO_ACCOUNT_INDEX,
632            &rent,
633            &signers,
634            &invoke_context,
635            &instruction_context,
636        );
637        assert_eq!(result, Err(InstructionError::InsufficientFunds));
638    }
639
640    #[test]
641    fn withdraw_inx_uninitialized_acc_two_withdraws_ok() {
642        prepare_mockup!(invoke_context, instruction_accounts, rent);
643        push_instruction_context!(
644            invoke_context,
645            transaction_context,
646            instruction_context,
647            instruction_accounts
648        );
649        let nonce_account = instruction_context
650            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
651            .unwrap();
652        let to_account = instruction_context
653            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
654            .unwrap();
655        let mut signers = HashSet::new();
656        signers.insert(*nonce_account.get_key());
657        set_invoke_context_blockhash!(invoke_context, 0);
658        let withdraw_lamports = nonce_account.get_lamports() / 2;
659        let from_expect_lamports = nonce_account.get_lamports() - withdraw_lamports;
660        let to_expect_lamports = to_account.get_lamports() + withdraw_lamports;
661        drop(nonce_account);
662        drop(to_account);
663        withdraw_nonce_account(
664            NONCE_ACCOUNT_INDEX,
665            withdraw_lamports,
666            WITHDRAW_TO_ACCOUNT_INDEX,
667            &rent,
668            &signers,
669            &invoke_context,
670            &instruction_context,
671        )
672        .unwrap();
673        let nonce_account = instruction_context
674            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
675            .unwrap();
676        let to_account = instruction_context
677            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
678            .unwrap();
679        let versions = nonce_account.get_state::<Versions>().unwrap();
680        assert_eq!(versions.state(), &State::Uninitialized);
681        assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
682        assert_eq!(to_account.get_lamports(), to_expect_lamports);
683        let withdraw_lamports = nonce_account.get_lamports();
684        let from_expect_lamports = nonce_account.get_lamports() - withdraw_lamports;
685        let to_expect_lamports = to_account.get_lamports() + withdraw_lamports;
686        drop(nonce_account);
687        drop(to_account);
688        withdraw_nonce_account(
689            NONCE_ACCOUNT_INDEX,
690            withdraw_lamports,
691            WITHDRAW_TO_ACCOUNT_INDEX,
692            &rent,
693            &signers,
694            &invoke_context,
695            &instruction_context,
696        )
697        .unwrap();
698        let nonce_account = instruction_context
699            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
700            .unwrap();
701        let to_account = instruction_context
702            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
703            .unwrap();
704        let versions = nonce_account.get_state::<Versions>().unwrap();
705        assert_eq!(versions.state(), &State::Uninitialized);
706        assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
707        assert_eq!(to_account.get_lamports(), to_expect_lamports);
708    }
709
710    #[test]
711    fn withdraw_inx_initialized_acc_two_withdraws_ok() {
712        prepare_mockup!(invoke_context, instruction_accounts, rent);
713        push_instruction_context!(
714            invoke_context,
715            transaction_context,
716            instruction_context,
717            instruction_accounts
718        );
719        let mut nonce_account = instruction_context
720            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
721            .unwrap();
722        let to_account = instruction_context
723            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
724            .unwrap();
725        let mut signers = HashSet::new();
726        signers.insert(*nonce_account.get_key());
727        set_invoke_context_blockhash!(invoke_context, 31);
728        let authority = *nonce_account.get_key();
729        initialize_nonce_account(&mut nonce_account, &authority, &rent, &invoke_context).unwrap();
730        let versions = nonce_account.get_state::<Versions>().unwrap();
731        let data = nonce::state::Data::new(
732            authority,
733            DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash),
734            invoke_context
735                .environment_config
736                .blockhash_lamports_per_signature,
737        );
738        assert_eq!(versions.state(), &State::Initialized(data.clone()));
739        let withdraw_lamports = 42;
740        let from_expect_lamports = nonce_account.get_lamports() - withdraw_lamports;
741        let to_expect_lamports = to_account.get_lamports() + withdraw_lamports;
742        drop(nonce_account);
743        drop(to_account);
744        withdraw_nonce_account(
745            NONCE_ACCOUNT_INDEX,
746            withdraw_lamports,
747            WITHDRAW_TO_ACCOUNT_INDEX,
748            &rent,
749            &signers,
750            &invoke_context,
751            &instruction_context,
752        )
753        .unwrap();
754        let nonce_account = instruction_context
755            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
756            .unwrap();
757        let to_account = instruction_context
758            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
759            .unwrap();
760        let versions = nonce_account.get_state::<Versions>().unwrap();
761        let data = nonce::state::Data::new(
762            data.authority,
763            DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash),
764            invoke_context
765                .environment_config
766                .blockhash_lamports_per_signature,
767        );
768        assert_eq!(versions.state(), &State::Initialized(data));
769        assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
770        assert_eq!(to_account.get_lamports(), to_expect_lamports);
771        set_invoke_context_blockhash!(invoke_context, 0);
772        let withdraw_lamports = nonce_account.get_lamports();
773        let from_expect_lamports = nonce_account.get_lamports() - withdraw_lamports;
774        let to_expect_lamports = to_account.get_lamports() + withdraw_lamports;
775        drop(nonce_account);
776        drop(to_account);
777        withdraw_nonce_account(
778            NONCE_ACCOUNT_INDEX,
779            withdraw_lamports,
780            WITHDRAW_TO_ACCOUNT_INDEX,
781            &rent,
782            &signers,
783            &invoke_context,
784            &instruction_context,
785        )
786        .unwrap();
787        let nonce_account = instruction_context
788            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
789            .unwrap();
790        let to_account = instruction_context
791            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
792            .unwrap();
793        let versions = nonce_account.get_state::<Versions>().unwrap();
794        assert_eq!(versions.state(), &State::Uninitialized);
795        assert_eq!(nonce_account.get_lamports(), from_expect_lamports);
796        assert_eq!(to_account.get_lamports(), to_expect_lamports);
797    }
798
799    #[test]
800    fn withdraw_inx_initialized_acc_nonce_too_early_fail() {
801        prepare_mockup!(invoke_context, instruction_accounts, rent);
802        push_instruction_context!(
803            invoke_context,
804            transaction_context,
805            instruction_context,
806            instruction_accounts
807        );
808        let mut nonce_account = instruction_context
809            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
810            .unwrap();
811        let to_account = instruction_context
812            .try_borrow_instruction_account(WITHDRAW_TO_ACCOUNT_INDEX)
813            .unwrap();
814        set_invoke_context_blockhash!(invoke_context, 0);
815        let authorized = *nonce_account.get_key();
816        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
817        let mut signers = HashSet::new();
818        signers.insert(*nonce_account.get_key());
819        let withdraw_lamports = nonce_account.get_lamports();
820        drop(nonce_account);
821        drop(to_account);
822        let result = withdraw_nonce_account(
823            NONCE_ACCOUNT_INDEX,
824            withdraw_lamports,
825            WITHDRAW_TO_ACCOUNT_INDEX,
826            &rent,
827            &signers,
828            &invoke_context,
829            &instruction_context,
830        );
831        assert_eq!(result, Err(SystemError::NonceBlockhashNotExpired.into()));
832    }
833
834    #[test]
835    fn withdraw_inx_initialized_acc_insuff_funds_fail() {
836        prepare_mockup!(invoke_context, instruction_accounts, rent);
837        push_instruction_context!(
838            invoke_context,
839            transaction_context,
840            instruction_context,
841            instruction_accounts
842        );
843        let mut nonce_account = instruction_context
844            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
845            .unwrap();
846        set_invoke_context_blockhash!(invoke_context, 95);
847        let authorized = *nonce_account.get_key();
848        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
849        set_invoke_context_blockhash!(invoke_context, 63);
850        let mut signers = HashSet::new();
851        signers.insert(*nonce_account.get_key());
852        let withdraw_lamports = nonce_account.get_lamports() + 1;
853        drop(nonce_account);
854        let result = withdraw_nonce_account(
855            NONCE_ACCOUNT_INDEX,
856            withdraw_lamports,
857            WITHDRAW_TO_ACCOUNT_INDEX,
858            &rent,
859            &signers,
860            &invoke_context,
861            &instruction_context,
862        );
863        assert_eq!(result, Err(InstructionError::InsufficientFunds));
864    }
865
866    #[test]
867    fn withdraw_inx_initialized_acc_insuff_rent_fail() {
868        prepare_mockup!(invoke_context, instruction_accounts, rent);
869        push_instruction_context!(
870            invoke_context,
871            transaction_context,
872            instruction_context,
873            instruction_accounts
874        );
875        let mut nonce_account = instruction_context
876            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
877            .unwrap();
878        set_invoke_context_blockhash!(invoke_context, 95);
879        let authorized = *nonce_account.get_key();
880        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
881        set_invoke_context_blockhash!(invoke_context, 63);
882        let mut signers = HashSet::new();
883        signers.insert(*nonce_account.get_key());
884        let withdraw_lamports = 42 + 1;
885        drop(nonce_account);
886        let result = withdraw_nonce_account(
887            NONCE_ACCOUNT_INDEX,
888            withdraw_lamports,
889            WITHDRAW_TO_ACCOUNT_INDEX,
890            &rent,
891            &signers,
892            &invoke_context,
893            &instruction_context,
894        );
895        assert_eq!(result, Err(InstructionError::InsufficientFunds));
896    }
897
898    #[test]
899    fn withdraw_inx_overflow() {
900        prepare_mockup!(invoke_context, instruction_accounts, rent);
901        push_instruction_context!(
902            invoke_context,
903            transaction_context,
904            instruction_context,
905            instruction_accounts
906        );
907        let mut nonce_account = instruction_context
908            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
909            .unwrap();
910        set_invoke_context_blockhash!(invoke_context, 95);
911        let authorized = *nonce_account.get_key();
912        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
913        set_invoke_context_blockhash!(invoke_context, 63);
914        let mut signers = HashSet::new();
915        signers.insert(*nonce_account.get_key());
916        let withdraw_lamports = u64::MAX - 54;
917        drop(nonce_account);
918        let result = withdraw_nonce_account(
919            NONCE_ACCOUNT_INDEX,
920            withdraw_lamports,
921            WITHDRAW_TO_ACCOUNT_INDEX,
922            &rent,
923            &signers,
924            &invoke_context,
925            &instruction_context,
926        );
927        assert_eq!(result, Err(InstructionError::InsufficientFunds));
928    }
929
930    #[test]
931    fn initialize_inx_ok() {
932        prepare_mockup!(invoke_context, instruction_accounts, rent);
933        push_instruction_context!(
934            invoke_context,
935            transaction_context,
936            instruction_context,
937            instruction_accounts
938        );
939        let mut nonce_account = instruction_context
940            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
941            .unwrap();
942        let versions = nonce_account.get_state::<Versions>().unwrap();
943        assert_eq!(versions.state(), &State::Uninitialized);
944        let mut signers = HashSet::new();
945        signers.insert(*nonce_account.get_key());
946        set_invoke_context_blockhash!(invoke_context, 0);
947        let authorized = *nonce_account.get_key();
948        let result =
949            initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context);
950        let data = nonce::state::Data::new(
951            authorized,
952            DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash),
953            invoke_context
954                .environment_config
955                .blockhash_lamports_per_signature,
956        );
957        assert_eq!(result, Ok(()));
958        let versions = nonce_account.get_state::<Versions>().unwrap();
959        assert_eq!(versions.state(), &State::Initialized(data));
960    }
961
962    #[test]
963    fn initialize_inx_initialized_account_fail() {
964        prepare_mockup!(invoke_context, instruction_accounts, rent);
965        push_instruction_context!(
966            invoke_context,
967            transaction_context,
968            instruction_context,
969            instruction_accounts
970        );
971        let mut nonce_account = instruction_context
972            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
973            .unwrap();
974        set_invoke_context_blockhash!(invoke_context, 31);
975        let authorized = *nonce_account.get_key();
976        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
977        set_invoke_context_blockhash!(invoke_context, 0);
978        let result =
979            initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context);
980        assert_eq!(result, Err(InstructionError::InvalidAccountData));
981    }
982
983    #[test]
984    fn initialize_inx_uninitialized_acc_insuff_funds_fail() {
985        prepare_mockup!(invoke_context, instruction_accounts, rent);
986        push_instruction_context!(
987            invoke_context,
988            transaction_context,
989            instruction_context,
990            instruction_accounts
991        );
992        let mut nonce_account = instruction_context
993            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
994            .unwrap();
995        nonce_account.checked_sub_lamports(42 * 2).unwrap();
996        set_invoke_context_blockhash!(invoke_context, 63);
997        let authorized = *nonce_account.get_key();
998        let result =
999            initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context);
1000        assert_eq!(result, Err(InstructionError::InsufficientFunds));
1001    }
1002
1003    #[test]
1004    fn authorize_inx_ok() {
1005        prepare_mockup!(invoke_context, instruction_accounts, rent);
1006        push_instruction_context!(
1007            invoke_context,
1008            transaction_context,
1009            instruction_context,
1010            instruction_accounts
1011        );
1012        let mut nonce_account = instruction_context
1013            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
1014            .unwrap();
1015        let mut signers = HashSet::new();
1016        signers.insert(*nonce_account.get_key());
1017        set_invoke_context_blockhash!(invoke_context, 31);
1018        let authorized = *nonce_account.get_key();
1019        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
1020        let authority = Pubkey::default();
1021        let data = nonce::state::Data::new(
1022            authority,
1023            DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash),
1024            invoke_context
1025                .environment_config
1026                .blockhash_lamports_per_signature,
1027        );
1028        authorize_nonce_account(&mut nonce_account, &authority, &signers, &invoke_context).unwrap();
1029        let versions = nonce_account.get_state::<Versions>().unwrap();
1030        assert_eq!(versions.state(), &State::Initialized(data));
1031    }
1032
1033    #[test]
1034    fn authorize_inx_uninitialized_state_fail() {
1035        prepare_mockup!(invoke_context, instruction_accounts, rent);
1036        push_instruction_context!(
1037            invoke_context,
1038            transaction_context,
1039            instruction_context,
1040            instruction_accounts
1041        );
1042        let mut nonce_account = instruction_context
1043            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
1044            .unwrap();
1045        let mut signers = HashSet::new();
1046        signers.insert(*nonce_account.get_key());
1047        let result = authorize_nonce_account(
1048            &mut nonce_account,
1049            &Pubkey::default(),
1050            &signers,
1051            &invoke_context,
1052        );
1053        assert_eq!(result, Err(InstructionError::InvalidAccountData));
1054    }
1055
1056    #[test]
1057    fn authorize_inx_bad_authority_fail() {
1058        prepare_mockup!(invoke_context, instruction_accounts, rent);
1059        push_instruction_context!(
1060            invoke_context,
1061            transaction_context,
1062            instruction_context,
1063            instruction_accounts
1064        );
1065        let mut nonce_account = instruction_context
1066            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
1067            .unwrap();
1068        let mut signers = HashSet::new();
1069        signers.insert(*nonce_account.get_key());
1070        set_invoke_context_blockhash!(invoke_context, 31);
1071        let authorized = Pubkey::default();
1072        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
1073        let result =
1074            authorize_nonce_account(&mut nonce_account, &authorized, &signers, &invoke_context);
1075        assert_eq!(result, Err(InstructionError::MissingRequiredSignature));
1076    }
1077
1078    #[test]
1079    fn verify_nonce_ok() {
1080        prepare_mockup!(invoke_context, instruction_accounts, rent);
1081        push_instruction_context!(
1082            invoke_context,
1083            transaction_context,
1084            instruction_context,
1085            instruction_accounts
1086        );
1087        let mut nonce_account = instruction_context
1088            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
1089            .unwrap();
1090        let mut signers = HashSet::new();
1091        signers.insert(nonce_account.get_key());
1092        let versions: Versions = nonce_account.get_state().unwrap();
1093        // New is in Uninitialzed state
1094        assert_eq!(versions.state(), &State::Uninitialized);
1095        set_invoke_context_blockhash!(invoke_context, 0);
1096        let authorized = *nonce_account.get_key();
1097        initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context).unwrap();
1098        drop(nonce_account);
1099        assert_matches!(
1100            verify_nonce_account(
1101                &transaction_context
1102                    .accounts()
1103                    .try_borrow(NONCE_ACCOUNT_INDEX)
1104                    .unwrap(),
1105                DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash)
1106                    .as_hash(),
1107            ),
1108            Some(_)
1109        );
1110    }
1111
1112    #[test]
1113    fn verify_nonce_bad_acc_state_fail() {
1114        prepare_mockup!(invoke_context, instruction_accounts, rent);
1115        push_instruction_context!(
1116            invoke_context,
1117            transaction_context,
1118            _instruction_context,
1119            instruction_accounts
1120        );
1121        assert_eq!(
1122            verify_nonce_account(
1123                &transaction_context
1124                    .accounts()
1125                    .try_borrow(NONCE_ACCOUNT_INDEX)
1126                    .unwrap(),
1127                &Hash::default(),
1128            ),
1129            None
1130        );
1131    }
1132
1133    #[test]
1134    fn verify_nonce_bad_query_hash_fail() {
1135        prepare_mockup!(invoke_context, instruction_accounts, rent);
1136        push_instruction_context!(
1137            invoke_context,
1138            transaction_context,
1139            instruction_context,
1140            instruction_accounts
1141        );
1142        let mut nonce_account = instruction_context
1143            .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
1144            .unwrap();
1145        let mut signers = HashSet::new();
1146        signers.insert(nonce_account.get_key());
1147        let versions: Versions = nonce_account.get_state().unwrap();
1148        // New is in Uninitialzed state
1149        assert_eq!(versions.state(), &State::Uninitialized);
1150        set_invoke_context_blockhash!(invoke_context, 0);
1151        let authorized = *nonce_account.get_key();
1152        initialize_nonce_account(
1153            &mut nonce_account,
1154            &authorized,
1155            &Rent::free(),
1156            &invoke_context,
1157        )
1158        .unwrap();
1159        set_invoke_context_blockhash!(invoke_context, 1);
1160        drop(nonce_account);
1161        assert_eq!(
1162            verify_nonce_account(
1163                &transaction_context
1164                    .accounts()
1165                    .try_borrow(NONCE_ACCOUNT_INDEX)
1166                    .unwrap(),
1167                DurableNonce::from_blockhash(&invoke_context.environment_config.blockhash)
1168                    .as_hash(),
1169            ),
1170            None
1171        );
1172    }
1173}