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
19fn 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 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 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 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 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 assert_eq!(nonce_account.get_lamports(), expect_nonce_lamports);
418 assert_eq!(to_account.get_lamports(), expect_to_lamports);
420 let versions = nonce_account.get_state::<Versions>().unwrap();
421 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 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 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 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}