solana_system_program/
system_instruction.rs

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