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