solana_system_program/
system_instruction.rs

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