nonce.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. #![allow(clippy::arithmetic_side_effects)]
  2. use {
  3. solana_cli::{
  4. check_balance,
  5. cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
  6. spend_utils::SpendAmount,
  7. test_utils::check_ready,
  8. },
  9. solana_cli_output::{parse_sign_only_reply_string, OutputFormat},
  10. solana_commitment_config::CommitmentConfig,
  11. solana_faucet::faucet::run_local_faucet_with_unique_port_for_tests,
  12. solana_hash::Hash,
  13. solana_keypair::{keypair_from_seed, Keypair},
  14. solana_native_token::LAMPORTS_PER_SOL,
  15. solana_net_utils::SocketAddrSpace,
  16. solana_pubkey::Pubkey,
  17. solana_rpc_client::nonblocking::rpc_client::RpcClient,
  18. solana_rpc_client_nonce_utils::nonblocking::blockhash_query::{BlockhashQuery, Source},
  19. solana_signer::Signer,
  20. solana_system_interface::program as system_program,
  21. solana_test_validator::TestValidator,
  22. test_case::test_case,
  23. };
  24. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  25. #[test_case(None, false, None; "base")]
  26. #[test_case(Some(String::from("seed")), false, None; "with_seed")]
  27. #[test_case(None, true, None; "with_authority")]
  28. #[test_case(None, false, Some(1_000_000); "with_compute_unit_price")]
  29. async fn test_nonce(
  30. seed: Option<String>,
  31. use_nonce_authority: bool,
  32. compute_unit_price: Option<u64>,
  33. ) {
  34. let mint_keypair = Keypair::new();
  35. let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
  36. let test_validator = TestValidator::async_with_no_fees(
  37. &mint_keypair,
  38. Some(faucet_addr),
  39. SocketAddrSpace::Unspecified,
  40. )
  41. .await;
  42. let rpc_client =
  43. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  44. let json_rpc_url = test_validator.rpc_url();
  45. let mut config_payer = CliConfig::recent_for_tests();
  46. config_payer.json_rpc_url.clone_from(&json_rpc_url);
  47. let payer = Keypair::new();
  48. config_payer.signers = vec![&payer];
  49. request_and_confirm_airdrop(
  50. &rpc_client,
  51. &config_payer,
  52. &config_payer.signers[0].pubkey(),
  53. 2000 * LAMPORTS_PER_SOL,
  54. )
  55. .await
  56. .unwrap();
  57. check_balance!(
  58. 2000 * LAMPORTS_PER_SOL,
  59. &rpc_client,
  60. &config_payer.signers[0].pubkey(),
  61. );
  62. let mut config_nonce = CliConfig::recent_for_tests();
  63. config_nonce.json_rpc_url = json_rpc_url;
  64. let nonce_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  65. config_nonce.signers = vec![&nonce_keypair];
  66. let nonce_account = if let Some(seed) = seed.as_ref() {
  67. Pubkey::create_with_seed(
  68. &config_nonce.signers[0].pubkey(),
  69. seed,
  70. &system_program::id(),
  71. )
  72. .unwrap()
  73. } else {
  74. nonce_keypair.pubkey()
  75. };
  76. let nonce_authority = Keypair::new();
  77. let optional_authority = if use_nonce_authority {
  78. Some(nonce_authority.pubkey())
  79. } else {
  80. None
  81. };
  82. // Create nonce account
  83. config_payer.signers.push(&nonce_keypair);
  84. config_payer.command = CliCommand::CreateNonceAccount {
  85. nonce_account: 1,
  86. seed,
  87. nonce_authority: optional_authority,
  88. memo: None,
  89. amount: SpendAmount::Some(1000 * LAMPORTS_PER_SOL),
  90. compute_unit_price,
  91. };
  92. process_command(&config_payer).await.unwrap();
  93. check_balance!(
  94. 1000 * LAMPORTS_PER_SOL,
  95. &rpc_client,
  96. &config_payer.signers[0].pubkey(),
  97. );
  98. check_balance!(1000 * LAMPORTS_PER_SOL, &rpc_client, &nonce_account);
  99. // Get nonce
  100. config_payer.signers.pop();
  101. config_payer.command = CliCommand::GetNonce(nonce_account);
  102. let first_nonce_string = process_command(&config_payer).await.unwrap();
  103. let first_nonce = first_nonce_string.parse::<Hash>().unwrap();
  104. // Get nonce
  105. config_payer.command = CliCommand::GetNonce(nonce_account);
  106. let second_nonce_string = process_command(&config_payer).await.unwrap();
  107. let second_nonce = second_nonce_string.parse::<Hash>().unwrap();
  108. assert_eq!(first_nonce, second_nonce);
  109. let mut authorized_signers: Vec<&dyn Signer> = vec![&payer];
  110. let index = if use_nonce_authority {
  111. authorized_signers.push(&nonce_authority);
  112. 1
  113. } else {
  114. 0
  115. };
  116. // New nonce
  117. config_payer.signers.clone_from(&authorized_signers);
  118. config_payer.command = CliCommand::NewNonce {
  119. nonce_account,
  120. nonce_authority: index,
  121. memo: None,
  122. compute_unit_price,
  123. };
  124. process_command(&config_payer).await.unwrap();
  125. // Get nonce
  126. config_payer.signers = vec![&payer];
  127. config_payer.command = CliCommand::GetNonce(nonce_account);
  128. let third_nonce_string = process_command(&config_payer).await.unwrap();
  129. let third_nonce = third_nonce_string.parse::<Hash>().unwrap();
  130. assert_ne!(first_nonce, third_nonce);
  131. // Withdraw from nonce account
  132. let payee_pubkey = solana_pubkey::new_rand();
  133. config_payer.signers = authorized_signers;
  134. config_payer.command = CliCommand::WithdrawFromNonceAccount {
  135. nonce_account,
  136. nonce_authority: index,
  137. memo: None,
  138. destination_account_pubkey: payee_pubkey,
  139. lamports: 100 * LAMPORTS_PER_SOL,
  140. compute_unit_price,
  141. };
  142. process_command(&config_payer).await.unwrap();
  143. check_balance!(
  144. 1000 * LAMPORTS_PER_SOL,
  145. &rpc_client,
  146. &config_payer.signers[0].pubkey(),
  147. );
  148. check_balance!(900 * LAMPORTS_PER_SOL, &rpc_client, &nonce_account);
  149. check_balance!(100 * LAMPORTS_PER_SOL, &rpc_client, &payee_pubkey);
  150. // Show nonce account
  151. config_payer.command = CliCommand::ShowNonceAccount {
  152. nonce_account_pubkey: nonce_account,
  153. use_lamports_unit: true,
  154. };
  155. process_command(&config_payer).await.unwrap();
  156. // Set new authority
  157. let new_authority = Keypair::new();
  158. config_payer.command = CliCommand::AuthorizeNonceAccount {
  159. nonce_account,
  160. nonce_authority: index,
  161. memo: None,
  162. new_authority: new_authority.pubkey(),
  163. compute_unit_price,
  164. };
  165. process_command(&config_payer).await.unwrap();
  166. // Old authority fails now
  167. config_payer.command = CliCommand::NewNonce {
  168. nonce_account,
  169. nonce_authority: index,
  170. memo: None,
  171. compute_unit_price,
  172. };
  173. process_command(&config_payer).await.unwrap_err();
  174. // New authority can advance nonce
  175. config_payer.signers = vec![&payer, &new_authority];
  176. config_payer.command = CliCommand::NewNonce {
  177. nonce_account,
  178. nonce_authority: 1,
  179. memo: None,
  180. compute_unit_price,
  181. };
  182. process_command(&config_payer).await.unwrap();
  183. // New authority can withdraw from nonce account
  184. config_payer.command = CliCommand::WithdrawFromNonceAccount {
  185. nonce_account,
  186. nonce_authority: 1,
  187. memo: None,
  188. destination_account_pubkey: payee_pubkey,
  189. lamports: 100 * LAMPORTS_PER_SOL,
  190. compute_unit_price,
  191. };
  192. process_command(&config_payer).await.unwrap();
  193. check_balance!(
  194. 1000 * LAMPORTS_PER_SOL,
  195. &rpc_client,
  196. &config_payer.signers[0].pubkey(),
  197. );
  198. check_balance!(800 * LAMPORTS_PER_SOL, &rpc_client, &nonce_account);
  199. check_balance!(200 * LAMPORTS_PER_SOL, &rpc_client, &payee_pubkey);
  200. }
  201. #[tokio::test(flavor = "multi_thread", worker_threads = 1)]
  202. async fn test_create_account_with_seed() {
  203. const ONE_SIG_FEE: u64 = 5000;
  204. agave_logger::setup();
  205. let mint_keypair = Keypair::new();
  206. let faucet_addr = run_local_faucet_with_unique_port_for_tests(mint_keypair.insecure_clone());
  207. let test_validator = TestValidator::async_with_custom_fees(
  208. &mint_keypair,
  209. ONE_SIG_FEE,
  210. Some(faucet_addr),
  211. SocketAddrSpace::Unspecified,
  212. )
  213. .await;
  214. let offline_nonce_authority_signer = keypair_from_seed(&[1u8; 32]).unwrap();
  215. let online_nonce_creator_signer = keypair_from_seed(&[2u8; 32]).unwrap();
  216. let to_address = Pubkey::from([3u8; 32]);
  217. // Setup accounts
  218. let rpc_client =
  219. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  220. request_and_confirm_airdrop(
  221. &rpc_client,
  222. &CliConfig::recent_for_tests(),
  223. &offline_nonce_authority_signer.pubkey(),
  224. 42 * LAMPORTS_PER_SOL,
  225. )
  226. .await
  227. .unwrap();
  228. request_and_confirm_airdrop(
  229. &rpc_client,
  230. &CliConfig::recent_for_tests(),
  231. &online_nonce_creator_signer.pubkey(),
  232. 4242 * LAMPORTS_PER_SOL,
  233. )
  234. .await
  235. .unwrap();
  236. check_balance!(
  237. 42 * LAMPORTS_PER_SOL,
  238. &rpc_client,
  239. &offline_nonce_authority_signer.pubkey(),
  240. );
  241. check_balance!(
  242. 4242 * LAMPORTS_PER_SOL,
  243. &rpc_client,
  244. &online_nonce_creator_signer.pubkey(),
  245. );
  246. check_balance!(0, &rpc_client, &to_address);
  247. check_ready(&rpc_client).await;
  248. // Create nonce account
  249. let creator_pubkey = online_nonce_creator_signer.pubkey();
  250. let authority_pubkey = offline_nonce_authority_signer.pubkey();
  251. let seed = authority_pubkey.to_string()[0..32].to_string();
  252. let nonce_address =
  253. Pubkey::create_with_seed(&creator_pubkey, &seed, &system_program::id()).unwrap();
  254. check_balance!(0, &rpc_client, &nonce_address);
  255. let mut creator_config = CliConfig::recent_for_tests();
  256. creator_config.json_rpc_url = test_validator.rpc_url();
  257. creator_config.signers = vec![&online_nonce_creator_signer];
  258. creator_config.command = CliCommand::CreateNonceAccount {
  259. nonce_account: 0,
  260. seed: Some(seed),
  261. nonce_authority: Some(authority_pubkey),
  262. memo: None,
  263. amount: SpendAmount::Some(241 * LAMPORTS_PER_SOL),
  264. compute_unit_price: None,
  265. };
  266. process_command(&creator_config).await.unwrap();
  267. check_balance!(241 * LAMPORTS_PER_SOL, &rpc_client, &nonce_address);
  268. check_balance!(
  269. 42 * LAMPORTS_PER_SOL,
  270. &rpc_client,
  271. &offline_nonce_authority_signer.pubkey(),
  272. );
  273. check_balance!(
  274. 4001 * LAMPORTS_PER_SOL - ONE_SIG_FEE,
  275. &rpc_client,
  276. &online_nonce_creator_signer.pubkey(),
  277. );
  278. check_balance!(0, &rpc_client, &to_address);
  279. // Fetch nonce hash
  280. let nonce_hash = solana_rpc_client_nonce_utils::nonblocking::get_account_with_commitment(
  281. &rpc_client,
  282. &nonce_address,
  283. CommitmentConfig::processed(),
  284. )
  285. .await
  286. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  287. .unwrap()
  288. .blockhash();
  289. // Test by creating transfer TX with nonce, fully offline
  290. let mut authority_config = CliConfig::recent_for_tests();
  291. authority_config.json_rpc_url = String::default();
  292. authority_config.signers = vec![&offline_nonce_authority_signer];
  293. // Verify we cannot contact the cluster
  294. authority_config.command = CliCommand::ClusterVersion;
  295. process_command(&authority_config).await.unwrap_err();
  296. authority_config.command = CliCommand::Transfer {
  297. amount: SpendAmount::Some(10 * LAMPORTS_PER_SOL),
  298. to: to_address,
  299. from: 0,
  300. sign_only: true,
  301. dump_transaction_message: true,
  302. allow_unfunded_recipient: true,
  303. no_wait: false,
  304. blockhash_query: BlockhashQuery::Static(nonce_hash),
  305. nonce_account: Some(nonce_address),
  306. nonce_authority: 0,
  307. memo: None,
  308. fee_payer: 0,
  309. derived_address_seed: None,
  310. derived_address_program_id: None,
  311. compute_unit_price: None,
  312. };
  313. authority_config.output_format = OutputFormat::JsonCompact;
  314. let sign_only_reply = process_command(&authority_config).await.unwrap();
  315. let sign_only = parse_sign_only_reply_string(&sign_only_reply);
  316. let authority_presigner = sign_only.presigner_of(&authority_pubkey).unwrap();
  317. assert_eq!(sign_only.blockhash, nonce_hash);
  318. // And submit it
  319. let mut submit_config = CliConfig::recent_for_tests();
  320. submit_config.json_rpc_url = test_validator.rpc_url();
  321. submit_config.signers = vec![&authority_presigner];
  322. submit_config.command = CliCommand::Transfer {
  323. amount: SpendAmount::Some(10 * LAMPORTS_PER_SOL),
  324. to: to_address,
  325. from: 0,
  326. sign_only: false,
  327. dump_transaction_message: true,
  328. allow_unfunded_recipient: true,
  329. no_wait: false,
  330. blockhash_query: BlockhashQuery::Validated(
  331. Source::NonceAccount(nonce_address),
  332. sign_only.blockhash,
  333. ),
  334. nonce_account: Some(nonce_address),
  335. nonce_authority: 0,
  336. memo: None,
  337. fee_payer: 0,
  338. derived_address_seed: None,
  339. derived_address_program_id: None,
  340. compute_unit_price: None,
  341. };
  342. process_command(&submit_config).await.unwrap();
  343. check_balance!(241 * LAMPORTS_PER_SOL, &rpc_client, &nonce_address);
  344. check_balance!(
  345. 32 * LAMPORTS_PER_SOL - ONE_SIG_FEE,
  346. &rpc_client,
  347. &offline_nonce_authority_signer.pubkey(),
  348. );
  349. check_balance!(
  350. 4001 * LAMPORTS_PER_SOL - ONE_SIG_FEE,
  351. &rpc_client,
  352. &online_nonce_creator_signer.pubkey(),
  353. );
  354. check_balance!(10 * LAMPORTS_PER_SOL, &rpc_client, &to_address);
  355. }