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