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::{BorrowedInstructionAccount, 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 BorrowedInstructionAccount,
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 BorrowedInstructionAccount,
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 BorrowedInstructionAccount,
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, $instruction_context:ident, $instruction_accounts:ident) => {
263            $invoke_context
264                .transaction_context
265                .configure_next_instruction_for_tests(2, $instruction_accounts, vec![])
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, $transaction_context: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!(
316            invoke_context,
317            instruction_accounts,
318            rent,
319            transaction_context
320        );
321        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
409            invoke_context,
410            instruction_accounts,
411            rent,
412            transaction_context
413        );
414        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
440            invoke_context,
441            instruction_accounts,
442            rent,
443            transaction_context
444        );
445        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
461            invoke_context,
462            instruction_accounts,
463            rent,
464            transaction_context
465        );
466        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
480            invoke_context,
481            instruction_accounts,
482            rent,
483            transaction_context
484        );
485        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
507            invoke_context,
508            instruction_accounts,
509            rent,
510            transaction_context
511        );
512        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
531            invoke_context,
532            instruction_accounts,
533            rent,
534            transaction_context
535        );
536        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
578            invoke_context,
579            instruction_accounts,
580            rent,
581            transaction_context
582        );
583        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
612            invoke_context,
613            instruction_accounts,
614            rent,
615            transaction_context
616        );
617        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
643            invoke_context,
644            instruction_accounts,
645            rent,
646            transaction_context
647        );
648        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
713            invoke_context,
714            instruction_accounts,
715            rent,
716            transaction_context
717        );
718        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
802            invoke_context,
803            instruction_accounts,
804            rent,
805            transaction_context
806        );
807        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
837            invoke_context,
838            instruction_accounts,
839            rent,
840            transaction_context
841        );
842        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
869            invoke_context,
870            instruction_accounts,
871            rent,
872            transaction_context
873        );
874        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
901            invoke_context,
902            instruction_accounts,
903            rent,
904            transaction_context
905        );
906        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
933            invoke_context,
934            instruction_accounts,
935            rent,
936            transaction_context
937        );
938        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
965            invoke_context,
966            instruction_accounts,
967            rent,
968            transaction_context
969        );
970        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
986            invoke_context,
987            instruction_accounts,
988            rent,
989            transaction_context
990        );
991        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
1006            invoke_context,
1007            instruction_accounts,
1008            rent,
1009            transaction_context
1010        );
1011        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
1036            invoke_context,
1037            instruction_accounts,
1038            rent,
1039            transaction_context
1040        );
1041        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
1059            invoke_context,
1060            instruction_accounts,
1061            rent,
1062            transaction_context
1063        );
1064        push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
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!(
1081            invoke_context,
1082            instruction_accounts,
1083            rent,
1084            transaction_context
1085        );
1086
1087        let blockhash = {
1088            push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
1089            let mut nonce_account = instruction_context
1090                .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
1091                .unwrap();
1092            let mut signers = HashSet::new();
1093            signers.insert(nonce_account.get_key());
1094            let versions: Versions = nonce_account.get_state().unwrap();
1095            // New is in Uninitialzed state
1096            assert_eq!(versions.state(), &State::Uninitialized);
1097            set_invoke_context_blockhash!(invoke_context, 0);
1098            let authorized = *nonce_account.get_key();
1099            initialize_nonce_account(&mut nonce_account, &authorized, &rent, &invoke_context)
1100                .unwrap();
1101            drop(nonce_account);
1102            invoke_context.environment_config.blockhash
1103        };
1104
1105        transaction_context.pop().unwrap();
1106        let post_accounts = transaction_context.deconstruct_without_keys().unwrap();
1107        assert_matches!(
1108            verify_nonce_account(
1109                post_accounts.get(NONCE_ACCOUNT_INDEX as usize).unwrap(),
1110                DurableNonce::from_blockhash(&blockhash).as_hash(),
1111            ),
1112            Some(_)
1113        );
1114    }
1115
1116    #[test]
1117    fn verify_nonce_bad_acc_state_fail() {
1118        prepare_mockup!(
1119            invoke_context,
1120            instruction_accounts,
1121            rent,
1122            transaction_context
1123        );
1124
1125        {
1126            push_instruction_context!(invoke_context, _instruction_context, instruction_accounts);
1127        }
1128
1129        transaction_context.pop().unwrap();
1130        let post_accounts = transaction_context.deconstruct_without_keys().unwrap();
1131        assert_eq!(
1132            verify_nonce_account(
1133                post_accounts.get(NONCE_ACCOUNT_INDEX as usize).unwrap(),
1134                &Hash::default(),
1135            ),
1136            None
1137        );
1138    }
1139
1140    #[test]
1141    fn verify_nonce_bad_query_hash_fail() {
1142        prepare_mockup!(
1143            invoke_context,
1144            instruction_accounts,
1145            rent,
1146            transaction_context
1147        );
1148
1149        let blockhash = {
1150            push_instruction_context!(invoke_context, instruction_context, instruction_accounts);
1151            let mut nonce_account = instruction_context
1152                .try_borrow_instruction_account(NONCE_ACCOUNT_INDEX)
1153                .unwrap();
1154            let mut signers = HashSet::new();
1155            signers.insert(nonce_account.get_key());
1156            let versions: Versions = nonce_account.get_state().unwrap();
1157            // New is in Uninitialzed state
1158            assert_eq!(versions.state(), &State::Uninitialized);
1159            set_invoke_context_blockhash!(invoke_context, 0);
1160            let authorized = *nonce_account.get_key();
1161            initialize_nonce_account(
1162                &mut nonce_account,
1163                &authorized,
1164                &Rent::free(),
1165                &invoke_context,
1166            )
1167            .unwrap();
1168            set_invoke_context_blockhash!(invoke_context, 1);
1169            drop(nonce_account);
1170            invoke_context.environment_config.blockhash
1171        };
1172
1173        transaction_context.pop().unwrap();
1174        let post_accounts = transaction_context.deconstruct_without_keys().unwrap();
1175
1176        assert_eq!(
1177            verify_nonce_account(
1178                post_accounts.get(NONCE_ACCOUNT_INDEX as usize).unwrap(),
1179                DurableNonce::from_blockhash(&blockhash).as_hash(),
1180            ),
1181            None
1182        );
1183    }
1184}