Skip to main content

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