stake.rs 85 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465
  1. #![allow(clippy::arithmetic_side_effects)]
  2. use {
  3. assert_matches::assert_matches,
  4. solana_account::state_traits::StateMut,
  5. solana_cli::{
  6. check_balance,
  7. cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig},
  8. spend_utils::SpendAmount,
  9. stake::StakeAuthorizationIndexed,
  10. test_utils::{check_ready, wait_for_next_epoch_plus_n_slots},
  11. },
  12. solana_cli_output::{parse_sign_only_reply_string, OutputFormat},
  13. solana_commitment_config::CommitmentConfig,
  14. solana_epoch_schedule::EpochSchedule,
  15. solana_faucet::faucet::run_local_faucet,
  16. solana_fee_calculator::FeeRateGovernor,
  17. solana_fee_structure::FeeStructure,
  18. solana_keypair::{keypair_from_seed, Keypair},
  19. solana_native_token::LAMPORTS_PER_SOL,
  20. solana_nonce::state::State as NonceState,
  21. solana_pubkey::Pubkey,
  22. solana_rent::Rent,
  23. solana_rpc_client::rpc_client::RpcClient,
  24. solana_rpc_client_api::request::DELINQUENT_VALIDATOR_SLOT_DISTANCE,
  25. solana_rpc_client_nonce_utils::blockhash_query::{self, BlockhashQuery},
  26. solana_signer::Signer,
  27. solana_stake_interface::{
  28. self as stake,
  29. instruction::LockupArgs,
  30. state::{Lockup, StakeAuthorize, StakeStateV2},
  31. },
  32. solana_streamer::socket::SocketAddrSpace,
  33. solana_test_validator::{TestValidator, TestValidatorGenesis},
  34. test_case::test_case,
  35. };
  36. #[test]
  37. fn test_stake_delegation_force() {
  38. let mint_keypair = Keypair::new();
  39. let mint_pubkey = mint_keypair.pubkey();
  40. let authorized_withdrawer = Keypair::new().pubkey();
  41. let faucet_addr = run_local_faucet(mint_keypair, None);
  42. let slots_per_epoch = 32;
  43. let test_validator = TestValidatorGenesis::default()
  44. .fee_rate_governor(FeeRateGovernor::new(0, 0))
  45. .rent(Rent {
  46. lamports_per_byte_year: 1,
  47. exemption_threshold: 1.0,
  48. ..Rent::default()
  49. })
  50. .epoch_schedule(EpochSchedule::custom(
  51. slots_per_epoch,
  52. slots_per_epoch,
  53. /* enable_warmup_epochs = */ false,
  54. ))
  55. .faucet_addr(Some(faucet_addr))
  56. .warp_slot(DELINQUENT_VALIDATOR_SLOT_DISTANCE * 2) // get out in front of the cli voter delinquency check
  57. .start_with_mint_address(mint_pubkey, SocketAddrSpace::Unspecified)
  58. .expect("validator start failed");
  59. let rpc_client =
  60. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  61. let default_signer = Keypair::new();
  62. let mut config = CliConfig::recent_for_tests();
  63. config.json_rpc_url = test_validator.rpc_url();
  64. config.signers = vec![&default_signer];
  65. request_and_confirm_airdrop(
  66. &rpc_client,
  67. &config,
  68. &config.signers[0].pubkey(),
  69. 100_000_000_000,
  70. )
  71. .unwrap();
  72. // Create vote account
  73. let vote_keypair = Keypair::new();
  74. config.signers = vec![&default_signer, &vote_keypair];
  75. config.command = CliCommand::CreateVoteAccount {
  76. vote_account: 1,
  77. seed: None,
  78. identity_account: 0,
  79. authorized_voter: None,
  80. authorized_withdrawer,
  81. commission: 0,
  82. sign_only: false,
  83. dump_transaction_message: false,
  84. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  85. nonce_account: None,
  86. nonce_authority: 0,
  87. memo: None,
  88. fee_payer: 0,
  89. compute_unit_price: None,
  90. };
  91. process_command(&config).unwrap();
  92. // Create stake account
  93. let stake_keypair = Keypair::new();
  94. config.signers = vec![&default_signer, &stake_keypair];
  95. config.command = CliCommand::CreateStakeAccount {
  96. stake_account: 1,
  97. seed: None,
  98. staker: None,
  99. withdrawer: None,
  100. withdrawer_signer: None,
  101. lockup: Lockup::default(),
  102. amount: SpendAmount::Some(25_000_000_000),
  103. sign_only: false,
  104. dump_transaction_message: false,
  105. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  106. nonce_account: None,
  107. nonce_authority: 0,
  108. memo: None,
  109. fee_payer: 0,
  110. from: 0,
  111. compute_unit_price: None,
  112. };
  113. process_command(&config).unwrap();
  114. // Delegate stake succeeds despite no votes, because voter has zero stake
  115. config.signers = vec![&default_signer];
  116. config.command = CliCommand::DelegateStake {
  117. stake_account_pubkey: stake_keypair.pubkey(),
  118. vote_account_pubkey: vote_keypair.pubkey(),
  119. stake_authority: 0,
  120. force: false,
  121. sign_only: false,
  122. dump_transaction_message: false,
  123. blockhash_query: BlockhashQuery::default(),
  124. nonce_account: None,
  125. nonce_authority: 0,
  126. memo: None,
  127. fee_payer: 0,
  128. compute_unit_price: None,
  129. };
  130. process_command(&config).unwrap();
  131. // Create a second stake account
  132. let stake_keypair2 = Keypair::new();
  133. config.signers = vec![&default_signer, &stake_keypair2];
  134. config.command = CliCommand::CreateStakeAccount {
  135. stake_account: 1,
  136. seed: None,
  137. staker: None,
  138. withdrawer: None,
  139. withdrawer_signer: None,
  140. lockup: Lockup::default(),
  141. amount: SpendAmount::Some(25_000_000_000),
  142. sign_only: false,
  143. dump_transaction_message: false,
  144. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  145. nonce_account: None,
  146. nonce_authority: 0,
  147. memo: None,
  148. fee_payer: 0,
  149. from: 0,
  150. compute_unit_price: None,
  151. };
  152. process_command(&config).unwrap();
  153. wait_for_next_epoch_plus_n_slots(&rpc_client, 1);
  154. // Delegate stake2 fails because voter has not voted, but is now staked
  155. config.signers = vec![&default_signer];
  156. config.command = CliCommand::DelegateStake {
  157. stake_account_pubkey: stake_keypair2.pubkey(),
  158. vote_account_pubkey: vote_keypair.pubkey(),
  159. stake_authority: 0,
  160. force: false,
  161. sign_only: false,
  162. dump_transaction_message: false,
  163. blockhash_query: BlockhashQuery::default(),
  164. nonce_account: None,
  165. nonce_authority: 0,
  166. memo: None,
  167. fee_payer: 0,
  168. compute_unit_price: None,
  169. };
  170. process_command(&config).unwrap_err();
  171. // But if we force it, it works anyway!
  172. config.command = CliCommand::DelegateStake {
  173. stake_account_pubkey: stake_keypair2.pubkey(),
  174. vote_account_pubkey: vote_keypair.pubkey(),
  175. stake_authority: 0,
  176. force: true,
  177. sign_only: false,
  178. dump_transaction_message: false,
  179. blockhash_query: BlockhashQuery::default(),
  180. nonce_account: None,
  181. nonce_authority: 0,
  182. memo: None,
  183. fee_payer: 0,
  184. compute_unit_price: None,
  185. };
  186. process_command(&config).unwrap();
  187. }
  188. #[test_case(None; "base")]
  189. #[test_case(Some(1_000_000); "with_compute_unit_price")]
  190. fn test_seed_stake_delegation_and_deactivation(compute_unit_price: Option<u64>) {
  191. solana_logger::setup();
  192. let mint_keypair = Keypair::new();
  193. let mint_pubkey = mint_keypair.pubkey();
  194. let faucet_addr = run_local_faucet(mint_keypair, None);
  195. let test_validator =
  196. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  197. let rpc_client =
  198. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  199. let validator_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  200. let mut config_validator = CliConfig::recent_for_tests();
  201. config_validator.json_rpc_url = test_validator.rpc_url();
  202. config_validator.signers = vec![&validator_keypair];
  203. request_and_confirm_airdrop(
  204. &rpc_client,
  205. &config_validator,
  206. &config_validator.signers[0].pubkey(),
  207. 100_000_000_000,
  208. )
  209. .unwrap();
  210. check_balance!(
  211. 100_000_000_000,
  212. &rpc_client,
  213. &config_validator.signers[0].pubkey()
  214. );
  215. let stake_address = Pubkey::create_with_seed(
  216. &config_validator.signers[0].pubkey(),
  217. "hi there",
  218. &stake::program::id(),
  219. )
  220. .expect("bad seed");
  221. // Create stake account with a seed, uses the validator config as the base,
  222. // which is nice ;)
  223. config_validator.command = CliCommand::CreateStakeAccount {
  224. stake_account: 0,
  225. seed: Some("hi there".to_string()),
  226. staker: None,
  227. withdrawer: None,
  228. withdrawer_signer: None,
  229. lockup: Lockup::default(),
  230. amount: SpendAmount::Some(50_000_000_000),
  231. sign_only: false,
  232. dump_transaction_message: false,
  233. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  234. nonce_account: None,
  235. nonce_authority: 0,
  236. memo: None,
  237. fee_payer: 0,
  238. from: 0,
  239. compute_unit_price,
  240. };
  241. process_command(&config_validator).unwrap();
  242. // Delegate stake
  243. config_validator.command = CliCommand::DelegateStake {
  244. stake_account_pubkey: stake_address,
  245. vote_account_pubkey: test_validator.vote_account_address(),
  246. stake_authority: 0,
  247. force: true,
  248. sign_only: false,
  249. dump_transaction_message: false,
  250. blockhash_query: BlockhashQuery::default(),
  251. nonce_account: None,
  252. nonce_authority: 0,
  253. memo: None,
  254. fee_payer: 0,
  255. compute_unit_price,
  256. };
  257. process_command(&config_validator).unwrap();
  258. // Deactivate stake
  259. config_validator.command = CliCommand::DeactivateStake {
  260. stake_account_pubkey: stake_address,
  261. stake_authority: 0,
  262. sign_only: false,
  263. deactivate_delinquent: false,
  264. dump_transaction_message: false,
  265. blockhash_query: BlockhashQuery::default(),
  266. nonce_account: None,
  267. nonce_authority: 0,
  268. memo: None,
  269. seed: None,
  270. fee_payer: 0,
  271. compute_unit_price,
  272. };
  273. process_command(&config_validator).unwrap();
  274. }
  275. #[test]
  276. fn test_stake_delegation_and_withdraw_available() {
  277. solana_logger::setup();
  278. let mint_keypair = Keypair::new();
  279. let mint_pubkey = mint_keypair.pubkey();
  280. let faucet_addr = run_local_faucet(mint_keypair, None);
  281. let test_validator =
  282. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  283. let rpc_client =
  284. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  285. let validator_keypair = Keypair::new();
  286. let mut config_validator = CliConfig::recent_for_tests();
  287. config_validator.json_rpc_url = test_validator.rpc_url();
  288. config_validator.signers = vec![&validator_keypair];
  289. let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  290. let recipient_pubkey = Pubkey::new_unique();
  291. request_and_confirm_airdrop(
  292. &rpc_client,
  293. &config_validator,
  294. &config_validator.signers[0].pubkey(),
  295. 100 * LAMPORTS_PER_SOL,
  296. )
  297. .unwrap();
  298. check_balance!(
  299. 100_000_000_000,
  300. &rpc_client,
  301. &config_validator.signers[0].pubkey()
  302. );
  303. // Create stake account
  304. config_validator.signers.push(&stake_keypair);
  305. config_validator.command = CliCommand::CreateStakeAccount {
  306. stake_account: 1,
  307. seed: None,
  308. staker: None,
  309. withdrawer: None,
  310. withdrawer_signer: None,
  311. lockup: Lockup::default(),
  312. amount: SpendAmount::Some(50 * LAMPORTS_PER_SOL),
  313. sign_only: false,
  314. dump_transaction_message: false,
  315. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  316. nonce_account: None,
  317. nonce_authority: 0,
  318. memo: None,
  319. fee_payer: 0,
  320. from: 0,
  321. compute_unit_price: None,
  322. };
  323. process_command(&config_validator).unwrap();
  324. // Delegate stake
  325. config_validator.signers.pop();
  326. config_validator.command = CliCommand::DelegateStake {
  327. stake_account_pubkey: stake_keypair.pubkey(),
  328. vote_account_pubkey: test_validator.vote_account_address(),
  329. stake_authority: 0,
  330. force: true,
  331. sign_only: false,
  332. dump_transaction_message: false,
  333. blockhash_query: BlockhashQuery::default(),
  334. nonce_account: None,
  335. nonce_authority: 0,
  336. memo: None,
  337. fee_payer: 0,
  338. compute_unit_price: None,
  339. };
  340. process_command(&config_validator).unwrap();
  341. // Withdraw available stake
  342. config_validator.signers = vec![&validator_keypair];
  343. config_validator.command = CliCommand::WithdrawStake {
  344. stake_account_pubkey: stake_keypair.pubkey(),
  345. destination_account_pubkey: recipient_pubkey,
  346. amount: SpendAmount::Available,
  347. withdraw_authority: 0,
  348. custodian: None,
  349. sign_only: false,
  350. dump_transaction_message: false,
  351. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  352. nonce_authority: 0,
  353. nonce_account: None,
  354. memo: None,
  355. seed: None,
  356. fee_payer: 0,
  357. compute_unit_price: None,
  358. };
  359. process_command(&config_validator).unwrap();
  360. // While withdraw transaction succeeds, no lamports move because all stake
  361. // is activating
  362. check_balance!(0, &rpc_client, &recipient_pubkey);
  363. // Add extra SOL to the stake account
  364. request_and_confirm_airdrop(
  365. &rpc_client,
  366. &config_validator,
  367. &stake_keypair.pubkey(),
  368. 5 * LAMPORTS_PER_SOL,
  369. )
  370. .unwrap();
  371. check_balance!(55 * LAMPORTS_PER_SOL, &rpc_client, &stake_keypair.pubkey());
  372. // Withdraw available stake
  373. config_validator.signers = vec![&validator_keypair];
  374. config_validator.command = CliCommand::WithdrawStake {
  375. stake_account_pubkey: stake_keypair.pubkey(),
  376. destination_account_pubkey: recipient_pubkey,
  377. amount: SpendAmount::Available,
  378. withdraw_authority: 0,
  379. custodian: None,
  380. sign_only: false,
  381. dump_transaction_message: false,
  382. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  383. nonce_authority: 0,
  384. nonce_account: None,
  385. memo: None,
  386. seed: None,
  387. fee_payer: 0,
  388. compute_unit_price: None,
  389. };
  390. process_command(&config_validator).unwrap();
  391. // Extra (inactive) SOL is withdrawn
  392. check_balance!(5 * LAMPORTS_PER_SOL, &rpc_client, &recipient_pubkey);
  393. // Deactivate stake
  394. config_validator.command = CliCommand::DeactivateStake {
  395. stake_account_pubkey: stake_keypair.pubkey(),
  396. stake_authority: 0,
  397. sign_only: false,
  398. deactivate_delinquent: false,
  399. dump_transaction_message: false,
  400. blockhash_query: BlockhashQuery::default(),
  401. nonce_account: None,
  402. nonce_authority: 0,
  403. memo: None,
  404. seed: None,
  405. fee_payer: 0,
  406. compute_unit_price: None,
  407. };
  408. process_command(&config_validator).unwrap();
  409. // Withdraw available stake
  410. config_validator.signers = vec![&validator_keypair];
  411. config_validator.command = CliCommand::WithdrawStake {
  412. stake_account_pubkey: stake_keypair.pubkey(),
  413. destination_account_pubkey: recipient_pubkey,
  414. amount: SpendAmount::Available,
  415. withdraw_authority: 0,
  416. custodian: None,
  417. sign_only: false,
  418. dump_transaction_message: false,
  419. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  420. nonce_authority: 0,
  421. nonce_account: None,
  422. memo: None,
  423. seed: None,
  424. fee_payer: 0,
  425. compute_unit_price: None,
  426. };
  427. process_command(&config_validator).unwrap();
  428. // Complete balance is withdrawn because all stake is inactive
  429. check_balance!(55 * LAMPORTS_PER_SOL, &rpc_client, &recipient_pubkey);
  430. }
  431. #[test]
  432. fn test_stake_delegation_and_withdraw_all() {
  433. solana_logger::setup();
  434. let mint_keypair = Keypair::new();
  435. let mint_pubkey = mint_keypair.pubkey();
  436. let faucet_addr = run_local_faucet(mint_keypair, None);
  437. let test_validator =
  438. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  439. let rpc_client =
  440. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  441. let validator_keypair = Keypair::new();
  442. let mut config_validator = CliConfig::recent_for_tests();
  443. config_validator.json_rpc_url = test_validator.rpc_url();
  444. config_validator.signers = vec![&validator_keypair];
  445. let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  446. let recipient_pubkey = Pubkey::new_unique();
  447. request_and_confirm_airdrop(
  448. &rpc_client,
  449. &config_validator,
  450. &config_validator.signers[0].pubkey(),
  451. 100 * LAMPORTS_PER_SOL,
  452. )
  453. .unwrap();
  454. check_balance!(
  455. 100_000_000_000,
  456. &rpc_client,
  457. &config_validator.signers[0].pubkey()
  458. );
  459. // Create stake account
  460. config_validator.signers.push(&stake_keypair);
  461. config_validator.command = CliCommand::CreateStakeAccount {
  462. stake_account: 1,
  463. seed: None,
  464. staker: None,
  465. withdrawer: None,
  466. withdrawer_signer: None,
  467. lockup: Lockup::default(),
  468. amount: SpendAmount::Some(50 * LAMPORTS_PER_SOL),
  469. sign_only: false,
  470. dump_transaction_message: false,
  471. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  472. nonce_account: None,
  473. nonce_authority: 0,
  474. memo: None,
  475. fee_payer: 0,
  476. from: 0,
  477. compute_unit_price: None,
  478. };
  479. process_command(&config_validator).unwrap();
  480. // Delegate stake
  481. config_validator.signers.pop();
  482. config_validator.command = CliCommand::DelegateStake {
  483. stake_account_pubkey: stake_keypair.pubkey(),
  484. vote_account_pubkey: test_validator.vote_account_address(),
  485. stake_authority: 0,
  486. force: true,
  487. sign_only: false,
  488. dump_transaction_message: false,
  489. blockhash_query: BlockhashQuery::default(),
  490. nonce_account: None,
  491. nonce_authority: 0,
  492. memo: None,
  493. fee_payer: 0,
  494. compute_unit_price: None,
  495. };
  496. process_command(&config_validator).unwrap();
  497. // Withdraw all stake fails because stake is activating
  498. config_validator.signers = vec![&validator_keypair];
  499. config_validator.command = CliCommand::WithdrawStake {
  500. stake_account_pubkey: stake_keypair.pubkey(),
  501. destination_account_pubkey: recipient_pubkey,
  502. amount: SpendAmount::All,
  503. withdraw_authority: 0,
  504. custodian: None,
  505. sign_only: false,
  506. dump_transaction_message: false,
  507. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  508. nonce_authority: 0,
  509. nonce_account: None,
  510. memo: None,
  511. seed: None,
  512. fee_payer: 0,
  513. compute_unit_price: None,
  514. };
  515. process_command(&config_validator).unwrap_err();
  516. // Add extra SOL to the stake account
  517. request_and_confirm_airdrop(
  518. &rpc_client,
  519. &config_validator,
  520. &stake_keypair.pubkey(),
  521. 5 * LAMPORTS_PER_SOL,
  522. )
  523. .unwrap();
  524. check_balance!(55 * LAMPORTS_PER_SOL, &rpc_client, &stake_keypair.pubkey());
  525. // Withdraw all stake still fails, because it attempts to withdraw both
  526. // activating and inactive stake
  527. config_validator.signers = vec![&validator_keypair];
  528. config_validator.command = CliCommand::WithdrawStake {
  529. stake_account_pubkey: stake_keypair.pubkey(),
  530. destination_account_pubkey: recipient_pubkey,
  531. amount: SpendAmount::All,
  532. withdraw_authority: 0,
  533. custodian: None,
  534. sign_only: false,
  535. dump_transaction_message: false,
  536. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  537. nonce_authority: 0,
  538. nonce_account: None,
  539. memo: None,
  540. seed: None,
  541. fee_payer: 0,
  542. compute_unit_price: None,
  543. };
  544. process_command(&config_validator).unwrap_err();
  545. // Deactivate stake
  546. config_validator.command = CliCommand::DeactivateStake {
  547. stake_account_pubkey: stake_keypair.pubkey(),
  548. stake_authority: 0,
  549. sign_only: false,
  550. deactivate_delinquent: false,
  551. dump_transaction_message: false,
  552. blockhash_query: BlockhashQuery::default(),
  553. nonce_account: None,
  554. nonce_authority: 0,
  555. memo: None,
  556. seed: None,
  557. fee_payer: 0,
  558. compute_unit_price: None,
  559. };
  560. process_command(&config_validator).unwrap();
  561. // Withdraw stake
  562. config_validator.signers = vec![&validator_keypair];
  563. config_validator.command = CliCommand::WithdrawStake {
  564. stake_account_pubkey: stake_keypair.pubkey(),
  565. destination_account_pubkey: recipient_pubkey,
  566. amount: SpendAmount::All,
  567. withdraw_authority: 0,
  568. custodian: None,
  569. sign_only: false,
  570. dump_transaction_message: false,
  571. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  572. nonce_authority: 0,
  573. nonce_account: None,
  574. memo: None,
  575. seed: None,
  576. fee_payer: 0,
  577. compute_unit_price: None,
  578. };
  579. process_command(&config_validator).unwrap();
  580. check_balance!(55 * LAMPORTS_PER_SOL, &rpc_client, &recipient_pubkey);
  581. }
  582. #[test_case(None; "base")]
  583. #[test_case(Some(1_000_000); "with_compute_unit_price")]
  584. fn test_stake_delegation_and_deactivation(compute_unit_price: Option<u64>) {
  585. solana_logger::setup();
  586. let mint_keypair = Keypair::new();
  587. let mint_pubkey = mint_keypair.pubkey();
  588. let faucet_addr = run_local_faucet(mint_keypair, None);
  589. let test_validator =
  590. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  591. let rpc_client =
  592. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  593. let validator_keypair = Keypair::new();
  594. let mut config_validator = CliConfig::recent_for_tests();
  595. config_validator.json_rpc_url = test_validator.rpc_url();
  596. config_validator.signers = vec![&validator_keypair];
  597. let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  598. request_and_confirm_airdrop(
  599. &rpc_client,
  600. &config_validator,
  601. &config_validator.signers[0].pubkey(),
  602. 100_000_000_000,
  603. )
  604. .unwrap();
  605. check_balance!(
  606. 100_000_000_000,
  607. &rpc_client,
  608. &config_validator.signers[0].pubkey()
  609. );
  610. // Create stake account
  611. config_validator.signers.push(&stake_keypair);
  612. config_validator.command = CliCommand::CreateStakeAccount {
  613. stake_account: 1,
  614. seed: None,
  615. staker: None,
  616. withdrawer: None,
  617. withdrawer_signer: None,
  618. lockup: Lockup::default(),
  619. amount: SpendAmount::Some(50_000_000_000),
  620. sign_only: false,
  621. dump_transaction_message: false,
  622. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  623. nonce_account: None,
  624. nonce_authority: 0,
  625. memo: None,
  626. fee_payer: 0,
  627. from: 0,
  628. compute_unit_price,
  629. };
  630. process_command(&config_validator).unwrap();
  631. // Delegate stake
  632. config_validator.signers.pop();
  633. config_validator.command = CliCommand::DelegateStake {
  634. stake_account_pubkey: stake_keypair.pubkey(),
  635. vote_account_pubkey: test_validator.vote_account_address(),
  636. stake_authority: 0,
  637. force: true,
  638. sign_only: false,
  639. dump_transaction_message: false,
  640. blockhash_query: BlockhashQuery::default(),
  641. nonce_account: None,
  642. nonce_authority: 0,
  643. memo: None,
  644. fee_payer: 0,
  645. compute_unit_price,
  646. };
  647. process_command(&config_validator).unwrap();
  648. // Deactivate stake
  649. config_validator.command = CliCommand::DeactivateStake {
  650. stake_account_pubkey: stake_keypair.pubkey(),
  651. stake_authority: 0,
  652. sign_only: false,
  653. deactivate_delinquent: false,
  654. dump_transaction_message: false,
  655. blockhash_query: BlockhashQuery::default(),
  656. nonce_account: None,
  657. nonce_authority: 0,
  658. memo: None,
  659. seed: None,
  660. fee_payer: 0,
  661. compute_unit_price,
  662. };
  663. process_command(&config_validator).unwrap();
  664. }
  665. #[test_case(None; "base")]
  666. #[test_case(Some(1_000_000); "with_compute_unit_price")]
  667. fn test_offline_stake_delegation_and_deactivation(compute_unit_price: Option<u64>) {
  668. solana_logger::setup();
  669. let mint_keypair = Keypair::new();
  670. let mint_pubkey = mint_keypair.pubkey();
  671. let faucet_addr = run_local_faucet(mint_keypair, None);
  672. let test_validator =
  673. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  674. let rpc_client =
  675. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  676. let mut config_validator = CliConfig::recent_for_tests();
  677. config_validator.json_rpc_url = test_validator.rpc_url();
  678. let validator_keypair = Keypair::new();
  679. config_validator.signers = vec![&validator_keypair];
  680. let mut config_payer = CliConfig::recent_for_tests();
  681. config_payer.json_rpc_url = test_validator.rpc_url();
  682. let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  683. let mut config_offline = CliConfig::recent_for_tests();
  684. config_offline.json_rpc_url = String::default();
  685. config_offline.command = CliCommand::ClusterVersion;
  686. let offline_keypair = Keypair::new();
  687. config_offline.signers = vec![&offline_keypair];
  688. // Verify that we cannot reach the cluster
  689. process_command(&config_offline).unwrap_err();
  690. request_and_confirm_airdrop(
  691. &rpc_client,
  692. &config_validator,
  693. &config_validator.signers[0].pubkey(),
  694. 100_000_000_000,
  695. )
  696. .unwrap();
  697. check_balance!(
  698. 100_000_000_000,
  699. &rpc_client,
  700. &config_validator.signers[0].pubkey()
  701. );
  702. request_and_confirm_airdrop(
  703. &rpc_client,
  704. &config_offline,
  705. &config_offline.signers[0].pubkey(),
  706. 100_000_000_000,
  707. )
  708. .unwrap();
  709. check_balance!(
  710. 100_000_000_000,
  711. &rpc_client,
  712. &config_offline.signers[0].pubkey()
  713. );
  714. // Create stake account
  715. config_validator.signers.push(&stake_keypair);
  716. config_validator.command = CliCommand::CreateStakeAccount {
  717. stake_account: 1,
  718. seed: None,
  719. staker: Some(config_offline.signers[0].pubkey()),
  720. withdrawer: None,
  721. withdrawer_signer: None,
  722. lockup: Lockup::default(),
  723. amount: SpendAmount::Some(50_000_000_000),
  724. sign_only: false,
  725. dump_transaction_message: false,
  726. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  727. nonce_account: None,
  728. nonce_authority: 0,
  729. memo: None,
  730. fee_payer: 0,
  731. from: 0,
  732. compute_unit_price,
  733. };
  734. process_command(&config_validator).unwrap();
  735. // Delegate stake offline
  736. let blockhash = rpc_client.get_latest_blockhash().unwrap();
  737. config_offline.command = CliCommand::DelegateStake {
  738. stake_account_pubkey: stake_keypair.pubkey(),
  739. vote_account_pubkey: test_validator.vote_account_address(),
  740. stake_authority: 0,
  741. force: true,
  742. sign_only: true,
  743. dump_transaction_message: false,
  744. blockhash_query: BlockhashQuery::None(blockhash),
  745. nonce_account: None,
  746. nonce_authority: 0,
  747. memo: None,
  748. fee_payer: 0,
  749. compute_unit_price,
  750. };
  751. config_offline.output_format = OutputFormat::JsonCompact;
  752. let sig_response = process_command(&config_offline).unwrap();
  753. let sign_only = parse_sign_only_reply_string(&sig_response);
  754. assert!(sign_only.has_all_signers());
  755. let offline_presigner = sign_only
  756. .presigner_of(&config_offline.signers[0].pubkey())
  757. .unwrap();
  758. config_payer.signers = vec![&offline_presigner];
  759. config_payer.command = CliCommand::DelegateStake {
  760. stake_account_pubkey: stake_keypair.pubkey(),
  761. vote_account_pubkey: test_validator.vote_account_address(),
  762. stake_authority: 0,
  763. force: true,
  764. sign_only: false,
  765. dump_transaction_message: false,
  766. blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
  767. nonce_account: None,
  768. nonce_authority: 0,
  769. memo: None,
  770. fee_payer: 0,
  771. compute_unit_price,
  772. };
  773. process_command(&config_payer).unwrap();
  774. // Deactivate stake offline
  775. let blockhash = rpc_client.get_latest_blockhash().unwrap();
  776. config_offline.command = CliCommand::DeactivateStake {
  777. stake_account_pubkey: stake_keypair.pubkey(),
  778. stake_authority: 0,
  779. sign_only: true,
  780. deactivate_delinquent: false,
  781. dump_transaction_message: false,
  782. blockhash_query: BlockhashQuery::None(blockhash),
  783. nonce_account: None,
  784. nonce_authority: 0,
  785. memo: None,
  786. seed: None,
  787. fee_payer: 0,
  788. compute_unit_price,
  789. };
  790. let sig_response = process_command(&config_offline).unwrap();
  791. let sign_only = parse_sign_only_reply_string(&sig_response);
  792. assert!(sign_only.has_all_signers());
  793. let offline_presigner = sign_only
  794. .presigner_of(&config_offline.signers[0].pubkey())
  795. .unwrap();
  796. config_payer.signers = vec![&offline_presigner];
  797. config_payer.command = CliCommand::DeactivateStake {
  798. stake_account_pubkey: stake_keypair.pubkey(),
  799. stake_authority: 0,
  800. sign_only: false,
  801. deactivate_delinquent: false,
  802. dump_transaction_message: false,
  803. blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
  804. nonce_account: None,
  805. nonce_authority: 0,
  806. memo: None,
  807. seed: None,
  808. fee_payer: 0,
  809. compute_unit_price,
  810. };
  811. process_command(&config_payer).unwrap();
  812. }
  813. #[test_case(None; "base")]
  814. #[test_case(Some(1_000_000); "with_compute_unit_price")]
  815. fn test_nonced_stake_delegation_and_deactivation(compute_unit_price: Option<u64>) {
  816. solana_logger::setup();
  817. let mint_keypair = Keypair::new();
  818. let mint_pubkey = mint_keypair.pubkey();
  819. let faucet_addr = run_local_faucet(mint_keypair, None);
  820. let test_validator =
  821. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  822. let rpc_client =
  823. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  824. let config_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  825. let mut config = CliConfig::recent_for_tests();
  826. config.signers = vec![&config_keypair];
  827. config.json_rpc_url = test_validator.rpc_url();
  828. let minimum_nonce_balance = rpc_client
  829. .get_minimum_balance_for_rent_exemption(NonceState::size())
  830. .unwrap();
  831. request_and_confirm_airdrop(
  832. &rpc_client,
  833. &config,
  834. &config.signers[0].pubkey(),
  835. 100_000_000_000,
  836. )
  837. .unwrap();
  838. // Create stake account
  839. let stake_keypair = Keypair::new();
  840. config.signers.push(&stake_keypair);
  841. config.command = CliCommand::CreateStakeAccount {
  842. stake_account: 1,
  843. seed: None,
  844. staker: None,
  845. withdrawer: None,
  846. withdrawer_signer: None,
  847. lockup: Lockup::default(),
  848. amount: SpendAmount::Some(50_000_000_000),
  849. sign_only: false,
  850. dump_transaction_message: false,
  851. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  852. nonce_account: None,
  853. nonce_authority: 0,
  854. memo: None,
  855. fee_payer: 0,
  856. from: 0,
  857. compute_unit_price,
  858. };
  859. process_command(&config).unwrap();
  860. // Create nonce account
  861. let nonce_account = Keypair::new();
  862. config.signers[1] = &nonce_account;
  863. config.command = CliCommand::CreateNonceAccount {
  864. nonce_account: 1,
  865. seed: None,
  866. nonce_authority: Some(config.signers[0].pubkey()),
  867. memo: None,
  868. amount: SpendAmount::Some(minimum_nonce_balance),
  869. compute_unit_price,
  870. };
  871. process_command(&config).unwrap();
  872. // Fetch nonce hash
  873. let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  874. &rpc_client,
  875. &nonce_account.pubkey(),
  876. CommitmentConfig::processed(),
  877. )
  878. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  879. .unwrap()
  880. .blockhash();
  881. // Delegate stake
  882. config.signers = vec![&config_keypair];
  883. config.command = CliCommand::DelegateStake {
  884. stake_account_pubkey: stake_keypair.pubkey(),
  885. vote_account_pubkey: test_validator.vote_account_address(),
  886. stake_authority: 0,
  887. force: true,
  888. sign_only: false,
  889. dump_transaction_message: false,
  890. blockhash_query: BlockhashQuery::FeeCalculator(
  891. blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
  892. nonce_hash,
  893. ),
  894. nonce_account: Some(nonce_account.pubkey()),
  895. nonce_authority: 0,
  896. memo: None,
  897. fee_payer: 0,
  898. compute_unit_price,
  899. };
  900. process_command(&config).unwrap();
  901. // Fetch nonce hash
  902. let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  903. &rpc_client,
  904. &nonce_account.pubkey(),
  905. CommitmentConfig::processed(),
  906. )
  907. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  908. .unwrap()
  909. .blockhash();
  910. // Deactivate stake
  911. config.command = CliCommand::DeactivateStake {
  912. stake_account_pubkey: stake_keypair.pubkey(),
  913. stake_authority: 0,
  914. sign_only: false,
  915. deactivate_delinquent: false,
  916. dump_transaction_message: false,
  917. blockhash_query: BlockhashQuery::FeeCalculator(
  918. blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
  919. nonce_hash,
  920. ),
  921. nonce_account: Some(nonce_account.pubkey()),
  922. nonce_authority: 0,
  923. memo: None,
  924. seed: None,
  925. fee_payer: 0,
  926. compute_unit_price,
  927. };
  928. process_command(&config).unwrap();
  929. }
  930. #[test_case(None; "base")]
  931. #[test_case(Some(1_000_000); "with_compute_unit_price")]
  932. fn test_stake_authorize(compute_unit_price: Option<u64>) {
  933. solana_logger::setup();
  934. let mint_keypair = Keypair::new();
  935. let mint_pubkey = mint_keypair.pubkey();
  936. let faucet_addr = run_local_faucet(mint_keypair, None);
  937. let test_validator =
  938. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  939. let rpc_client =
  940. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  941. let default_signer = Keypair::new();
  942. let mut config = CliConfig::recent_for_tests();
  943. config.json_rpc_url = test_validator.rpc_url();
  944. config.signers = vec![&default_signer];
  945. request_and_confirm_airdrop(
  946. &rpc_client,
  947. &config,
  948. &config.signers[0].pubkey(),
  949. 100_000_000_000,
  950. )
  951. .unwrap();
  952. let offline_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  953. let mut config_offline = CliConfig::recent_for_tests();
  954. config_offline.signers = vec![&offline_keypair];
  955. config_offline.json_rpc_url = String::default();
  956. let offline_authority_pubkey = config_offline.signers[0].pubkey();
  957. config_offline.command = CliCommand::ClusterVersion;
  958. // Verify that we cannot reach the cluster
  959. process_command(&config_offline).unwrap_err();
  960. request_and_confirm_airdrop(
  961. &rpc_client,
  962. &config_offline,
  963. &config_offline.signers[0].pubkey(),
  964. 100_000_000_000,
  965. )
  966. .unwrap();
  967. // Create stake account, identity is authority
  968. let stake_keypair = Keypair::new();
  969. let stake_account_pubkey = stake_keypair.pubkey();
  970. config.signers.push(&stake_keypair);
  971. config.command = CliCommand::CreateStakeAccount {
  972. stake_account: 1,
  973. seed: None,
  974. staker: None,
  975. withdrawer: None,
  976. withdrawer_signer: None,
  977. lockup: Lockup::default(),
  978. amount: SpendAmount::Some(50_000_000_000),
  979. sign_only: false,
  980. dump_transaction_message: false,
  981. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  982. nonce_account: None,
  983. nonce_authority: 0,
  984. memo: None,
  985. fee_payer: 0,
  986. from: 0,
  987. compute_unit_price,
  988. };
  989. process_command(&config).unwrap();
  990. // Assign new online stake authority
  991. let online_authority = Keypair::new();
  992. let online_authority_pubkey = online_authority.pubkey();
  993. config.signers.pop();
  994. config.command = CliCommand::StakeAuthorize {
  995. stake_account_pubkey,
  996. new_authorizations: vec![StakeAuthorizationIndexed {
  997. authorization_type: StakeAuthorize::Staker,
  998. new_authority_pubkey: online_authority_pubkey,
  999. authority: 0,
  1000. new_authority_signer: None,
  1001. }],
  1002. sign_only: false,
  1003. dump_transaction_message: false,
  1004. blockhash_query: BlockhashQuery::default(),
  1005. nonce_account: None,
  1006. nonce_authority: 0,
  1007. memo: None,
  1008. fee_payer: 0,
  1009. custodian: None,
  1010. no_wait: false,
  1011. compute_unit_price,
  1012. };
  1013. process_command(&config).unwrap();
  1014. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  1015. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  1016. let current_authority = match stake_state {
  1017. StakeStateV2::Initialized(meta) => meta.authorized.staker,
  1018. _ => panic!("Unexpected stake state!"),
  1019. };
  1020. assert_eq!(current_authority, online_authority_pubkey);
  1021. // Assign new online stake and withdraw authorities
  1022. let online_authority2 = Keypair::new();
  1023. let online_authority2_pubkey = online_authority2.pubkey();
  1024. let withdraw_authority = Keypair::new();
  1025. let withdraw_authority_pubkey = withdraw_authority.pubkey();
  1026. config.signers.push(&online_authority);
  1027. config.command = CliCommand::StakeAuthorize {
  1028. stake_account_pubkey,
  1029. new_authorizations: vec![
  1030. StakeAuthorizationIndexed {
  1031. authorization_type: StakeAuthorize::Staker,
  1032. new_authority_pubkey: online_authority2_pubkey,
  1033. authority: 1,
  1034. new_authority_signer: None,
  1035. },
  1036. StakeAuthorizationIndexed {
  1037. authorization_type: StakeAuthorize::Withdrawer,
  1038. new_authority_pubkey: withdraw_authority_pubkey,
  1039. authority: 0,
  1040. new_authority_signer: None,
  1041. },
  1042. ],
  1043. sign_only: false,
  1044. dump_transaction_message: false,
  1045. blockhash_query: BlockhashQuery::default(),
  1046. nonce_account: None,
  1047. nonce_authority: 0,
  1048. memo: None,
  1049. fee_payer: 0,
  1050. custodian: None,
  1051. no_wait: false,
  1052. compute_unit_price,
  1053. };
  1054. process_command(&config).unwrap();
  1055. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  1056. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  1057. let (current_staker, current_withdrawer) = match stake_state {
  1058. StakeStateV2::Initialized(meta) => (meta.authorized.staker, meta.authorized.withdrawer),
  1059. _ => panic!("Unexpected stake state!"),
  1060. };
  1061. assert_eq!(current_staker, online_authority2_pubkey);
  1062. assert_eq!(current_withdrawer, withdraw_authority_pubkey);
  1063. // Assign new offline stake authority
  1064. config.signers.pop();
  1065. config.signers.push(&online_authority2);
  1066. config.command = CliCommand::StakeAuthorize {
  1067. stake_account_pubkey,
  1068. new_authorizations: vec![StakeAuthorizationIndexed {
  1069. authorization_type: StakeAuthorize::Staker,
  1070. new_authority_pubkey: offline_authority_pubkey,
  1071. authority: 1,
  1072. new_authority_signer: None,
  1073. }],
  1074. sign_only: false,
  1075. dump_transaction_message: false,
  1076. blockhash_query: BlockhashQuery::default(),
  1077. nonce_account: None,
  1078. nonce_authority: 0,
  1079. memo: None,
  1080. fee_payer: 0,
  1081. custodian: None,
  1082. no_wait: false,
  1083. compute_unit_price,
  1084. };
  1085. process_command(&config).unwrap();
  1086. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  1087. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  1088. let current_authority = match stake_state {
  1089. StakeStateV2::Initialized(meta) => meta.authorized.staker,
  1090. _ => panic!("Unexpected stake state!"),
  1091. };
  1092. assert_eq!(current_authority, offline_authority_pubkey);
  1093. // Offline assignment of new nonced stake authority
  1094. let nonced_authority = Keypair::new();
  1095. let nonced_authority_pubkey = nonced_authority.pubkey();
  1096. let blockhash = rpc_client.get_latest_blockhash().unwrap();
  1097. config_offline.command = CliCommand::StakeAuthorize {
  1098. stake_account_pubkey,
  1099. new_authorizations: vec![StakeAuthorizationIndexed {
  1100. authorization_type: StakeAuthorize::Staker,
  1101. new_authority_pubkey: nonced_authority_pubkey,
  1102. authority: 0,
  1103. new_authority_signer: None,
  1104. }],
  1105. sign_only: true,
  1106. dump_transaction_message: false,
  1107. blockhash_query: BlockhashQuery::None(blockhash),
  1108. nonce_account: None,
  1109. nonce_authority: 0,
  1110. memo: None,
  1111. fee_payer: 0,
  1112. custodian: None,
  1113. no_wait: false,
  1114. compute_unit_price,
  1115. };
  1116. config_offline.output_format = OutputFormat::JsonCompact;
  1117. let sign_reply = process_command(&config_offline).unwrap();
  1118. let sign_only = parse_sign_only_reply_string(&sign_reply);
  1119. assert!(sign_only.has_all_signers());
  1120. let offline_presigner = sign_only.presigner_of(&offline_authority_pubkey).unwrap();
  1121. config.signers = vec![&offline_presigner];
  1122. config.command = CliCommand::StakeAuthorize {
  1123. stake_account_pubkey,
  1124. new_authorizations: vec![StakeAuthorizationIndexed {
  1125. authorization_type: StakeAuthorize::Staker,
  1126. new_authority_pubkey: nonced_authority_pubkey,
  1127. authority: 0,
  1128. new_authority_signer: None,
  1129. }],
  1130. sign_only: false,
  1131. dump_transaction_message: false,
  1132. blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
  1133. nonce_account: None,
  1134. nonce_authority: 0,
  1135. memo: None,
  1136. fee_payer: 0,
  1137. custodian: None,
  1138. no_wait: false,
  1139. compute_unit_price,
  1140. };
  1141. process_command(&config).unwrap();
  1142. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  1143. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  1144. let current_authority = match stake_state {
  1145. StakeStateV2::Initialized(meta) => meta.authorized.staker,
  1146. _ => panic!("Unexpected stake state!"),
  1147. };
  1148. assert_eq!(current_authority, nonced_authority_pubkey);
  1149. // Create nonce account
  1150. let minimum_nonce_balance = rpc_client
  1151. .get_minimum_balance_for_rent_exemption(NonceState::size())
  1152. .unwrap();
  1153. let nonce_account = Keypair::new();
  1154. config.signers = vec![&default_signer, &nonce_account];
  1155. config.command = CliCommand::CreateNonceAccount {
  1156. nonce_account: 1,
  1157. seed: None,
  1158. nonce_authority: Some(offline_authority_pubkey),
  1159. memo: None,
  1160. amount: SpendAmount::Some(minimum_nonce_balance),
  1161. compute_unit_price,
  1162. };
  1163. process_command(&config).unwrap();
  1164. // Fetch nonce hash
  1165. let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  1166. &rpc_client,
  1167. &nonce_account.pubkey(),
  1168. CommitmentConfig::processed(),
  1169. )
  1170. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  1171. .unwrap()
  1172. .blockhash();
  1173. // Nonced assignment of new online stake authority
  1174. let online_authority = Keypair::new();
  1175. let online_authority_pubkey = online_authority.pubkey();
  1176. config_offline.signers.push(&nonced_authority);
  1177. config_offline.command = CliCommand::StakeAuthorize {
  1178. stake_account_pubkey,
  1179. new_authorizations: vec![StakeAuthorizationIndexed {
  1180. authorization_type: StakeAuthorize::Staker,
  1181. new_authority_pubkey: online_authority_pubkey,
  1182. authority: 1,
  1183. new_authority_signer: None,
  1184. }],
  1185. sign_only: true,
  1186. dump_transaction_message: false,
  1187. blockhash_query: BlockhashQuery::None(nonce_hash),
  1188. nonce_account: Some(nonce_account.pubkey()),
  1189. nonce_authority: 0,
  1190. memo: None,
  1191. fee_payer: 0,
  1192. custodian: None,
  1193. no_wait: false,
  1194. compute_unit_price,
  1195. };
  1196. let sign_reply = process_command(&config_offline).unwrap();
  1197. let sign_only = parse_sign_only_reply_string(&sign_reply);
  1198. assert!(sign_only.has_all_signers());
  1199. assert_eq!(sign_only.blockhash, nonce_hash);
  1200. let offline_presigner = sign_only.presigner_of(&offline_authority_pubkey).unwrap();
  1201. let nonced_authority_presigner = sign_only.presigner_of(&nonced_authority_pubkey).unwrap();
  1202. config.signers = vec![&offline_presigner, &nonced_authority_presigner];
  1203. config.command = CliCommand::StakeAuthorize {
  1204. stake_account_pubkey,
  1205. new_authorizations: vec![StakeAuthorizationIndexed {
  1206. authorization_type: StakeAuthorize::Staker,
  1207. new_authority_pubkey: online_authority_pubkey,
  1208. authority: 1,
  1209. new_authority_signer: None,
  1210. }],
  1211. sign_only: false,
  1212. dump_transaction_message: false,
  1213. blockhash_query: BlockhashQuery::FeeCalculator(
  1214. blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
  1215. sign_only.blockhash,
  1216. ),
  1217. nonce_account: Some(nonce_account.pubkey()),
  1218. nonce_authority: 0,
  1219. memo: None,
  1220. fee_payer: 0,
  1221. custodian: None,
  1222. no_wait: false,
  1223. compute_unit_price,
  1224. };
  1225. process_command(&config).unwrap();
  1226. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  1227. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  1228. let current_authority = match stake_state {
  1229. StakeStateV2::Initialized(meta) => meta.authorized.staker,
  1230. _ => panic!("Unexpected stake state!"),
  1231. };
  1232. assert_eq!(current_authority, online_authority_pubkey);
  1233. let new_nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  1234. &rpc_client,
  1235. &nonce_account.pubkey(),
  1236. CommitmentConfig::processed(),
  1237. )
  1238. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  1239. .unwrap()
  1240. .blockhash();
  1241. assert_ne!(nonce_hash, new_nonce_hash);
  1242. }
  1243. #[test]
  1244. fn test_stake_authorize_with_fee_payer() {
  1245. solana_logger::setup();
  1246. let fee_one_sig = FeeStructure::default().get_max_fee(1, 0);
  1247. let fee_two_sig = FeeStructure::default().get_max_fee(2, 0);
  1248. let mint_keypair = Keypair::new();
  1249. let mint_pubkey = mint_keypair.pubkey();
  1250. let faucet_addr = run_local_faucet(mint_keypair, None);
  1251. let test_validator = TestValidator::with_custom_fees(
  1252. mint_pubkey,
  1253. fee_one_sig,
  1254. Some(faucet_addr),
  1255. SocketAddrSpace::Unspecified,
  1256. );
  1257. let rpc_client =
  1258. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  1259. let default_signer = Keypair::new();
  1260. let default_pubkey = default_signer.pubkey();
  1261. let mut config = CliConfig::recent_for_tests();
  1262. config.json_rpc_url = test_validator.rpc_url();
  1263. config.signers = vec![&default_signer];
  1264. let payer_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  1265. let mut config_payer = CliConfig::recent_for_tests();
  1266. config_payer.signers = vec![&payer_keypair];
  1267. config_payer.json_rpc_url = test_validator.rpc_url();
  1268. let payer_pubkey = config_payer.signers[0].pubkey();
  1269. let mut config_offline = CliConfig::recent_for_tests();
  1270. let offline_signer = Keypair::new();
  1271. config_offline.signers = vec![&offline_signer];
  1272. config_offline.json_rpc_url = String::new();
  1273. let offline_pubkey = config_offline.signers[0].pubkey();
  1274. // Verify we're offline
  1275. config_offline.command = CliCommand::ClusterVersion;
  1276. process_command(&config_offline).unwrap_err();
  1277. request_and_confirm_airdrop(&rpc_client, &config, &default_pubkey, 5_000_000_000_000).unwrap();
  1278. check_balance!(5_000_000_000_000, &rpc_client, &config.signers[0].pubkey());
  1279. request_and_confirm_airdrop(&rpc_client, &config_payer, &payer_pubkey, 5_000_000_000_000)
  1280. .unwrap();
  1281. check_balance!(5_000_000_000_000, &rpc_client, &payer_pubkey);
  1282. request_and_confirm_airdrop(
  1283. &rpc_client,
  1284. &config_offline,
  1285. &offline_pubkey,
  1286. 5_000_000_000_000,
  1287. )
  1288. .unwrap();
  1289. check_balance!(5_000_000_000_000, &rpc_client, &offline_pubkey);
  1290. check_ready(&rpc_client);
  1291. // Create stake account, identity is authority
  1292. let stake_keypair = Keypair::new();
  1293. let stake_account_pubkey = stake_keypair.pubkey();
  1294. config.signers.push(&stake_keypair);
  1295. config.command = CliCommand::CreateStakeAccount {
  1296. stake_account: 1,
  1297. seed: None,
  1298. staker: None,
  1299. withdrawer: None,
  1300. withdrawer_signer: None,
  1301. lockup: Lockup::default(),
  1302. amount: SpendAmount::Some(1_000_000_000_000),
  1303. sign_only: false,
  1304. dump_transaction_message: false,
  1305. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  1306. nonce_account: None,
  1307. nonce_authority: 0,
  1308. memo: None,
  1309. fee_payer: 0,
  1310. from: 0,
  1311. compute_unit_price: None,
  1312. };
  1313. process_command(&config).unwrap();
  1314. check_balance!(
  1315. 4_000_000_000_000 - fee_two_sig,
  1316. &rpc_client,
  1317. &default_pubkey
  1318. );
  1319. // Assign authority with separate fee payer
  1320. config.signers = vec![&default_signer, &payer_keypair];
  1321. config.command = CliCommand::StakeAuthorize {
  1322. stake_account_pubkey,
  1323. new_authorizations: vec![StakeAuthorizationIndexed {
  1324. authorization_type: StakeAuthorize::Staker,
  1325. new_authority_pubkey: offline_pubkey,
  1326. authority: 0,
  1327. new_authority_signer: None,
  1328. }],
  1329. sign_only: false,
  1330. dump_transaction_message: false,
  1331. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  1332. nonce_account: None,
  1333. nonce_authority: 0,
  1334. memo: None,
  1335. fee_payer: 1,
  1336. custodian: None,
  1337. no_wait: false,
  1338. compute_unit_price: None,
  1339. };
  1340. process_command(&config).unwrap();
  1341. // `config` balance has not changed, despite submitting the TX
  1342. check_balance!(
  1343. 4_000_000_000_000 - fee_two_sig,
  1344. &rpc_client,
  1345. &default_pubkey
  1346. );
  1347. // `config_payer` however has paid `config`'s authority sig
  1348. // and `config_payer`'s fee sig
  1349. check_balance!(5_000_000_000_000 - fee_two_sig, &rpc_client, &payer_pubkey);
  1350. // Assign authority with offline fee payer
  1351. let blockhash = rpc_client.get_latest_blockhash().unwrap();
  1352. config_offline.command = CliCommand::StakeAuthorize {
  1353. stake_account_pubkey,
  1354. new_authorizations: vec![StakeAuthorizationIndexed {
  1355. authorization_type: StakeAuthorize::Staker,
  1356. new_authority_pubkey: payer_pubkey,
  1357. authority: 0,
  1358. new_authority_signer: None,
  1359. }],
  1360. sign_only: true,
  1361. dump_transaction_message: false,
  1362. blockhash_query: BlockhashQuery::None(blockhash),
  1363. nonce_account: None,
  1364. nonce_authority: 0,
  1365. memo: None,
  1366. fee_payer: 0,
  1367. custodian: None,
  1368. no_wait: false,
  1369. compute_unit_price: None,
  1370. };
  1371. config_offline.output_format = OutputFormat::JsonCompact;
  1372. let sign_reply = process_command(&config_offline).unwrap();
  1373. let sign_only = parse_sign_only_reply_string(&sign_reply);
  1374. assert!(sign_only.has_all_signers());
  1375. let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
  1376. config.signers = vec![&offline_presigner];
  1377. config.command = CliCommand::StakeAuthorize {
  1378. stake_account_pubkey,
  1379. new_authorizations: vec![StakeAuthorizationIndexed {
  1380. authorization_type: StakeAuthorize::Staker,
  1381. new_authority_pubkey: payer_pubkey,
  1382. authority: 0,
  1383. new_authority_signer: None,
  1384. }],
  1385. sign_only: false,
  1386. dump_transaction_message: false,
  1387. blockhash_query: BlockhashQuery::FeeCalculator(blockhash_query::Source::Cluster, blockhash),
  1388. nonce_account: None,
  1389. nonce_authority: 0,
  1390. memo: None,
  1391. fee_payer: 0,
  1392. custodian: None,
  1393. no_wait: false,
  1394. compute_unit_price: None,
  1395. };
  1396. process_command(&config).unwrap();
  1397. // `config`'s balance again has not changed
  1398. check_balance!(
  1399. 4_000_000_000_000 - fee_two_sig,
  1400. &rpc_client,
  1401. &default_pubkey
  1402. );
  1403. // `config_offline` however has paid 1 sig due to being both authority
  1404. // and fee payer
  1405. check_balance!(
  1406. 5_000_000_000_000 - fee_one_sig,
  1407. &rpc_client,
  1408. &offline_pubkey
  1409. );
  1410. }
  1411. #[test_case(None; "base")]
  1412. #[test_case(Some(1_000_000); "with_compute_unit_price")]
  1413. fn test_stake_split(compute_unit_price: Option<u64>) {
  1414. solana_logger::setup();
  1415. let mint_keypair = Keypair::new();
  1416. let mint_pubkey = mint_keypair.pubkey();
  1417. let faucet_addr = run_local_faucet(mint_keypair, None);
  1418. let test_validator = TestValidator::with_custom_fees(
  1419. mint_pubkey,
  1420. 1,
  1421. Some(faucet_addr),
  1422. SocketAddrSpace::Unspecified,
  1423. );
  1424. let rpc_client =
  1425. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  1426. let default_signer = Keypair::new();
  1427. let offline_signer = Keypair::new();
  1428. let mut config = CliConfig::recent_for_tests();
  1429. config.json_rpc_url = test_validator.rpc_url();
  1430. config.signers = vec![&default_signer];
  1431. let minimum_balance = rpc_client
  1432. .get_minimum_balance_for_rent_exemption(StakeStateV2::size_of())
  1433. .unwrap();
  1434. let mut config_offline = CliConfig::recent_for_tests();
  1435. config_offline.json_rpc_url = String::default();
  1436. config_offline.signers = vec![&offline_signer];
  1437. let offline_pubkey = config_offline.signers[0].pubkey();
  1438. // Verify we're offline
  1439. config_offline.command = CliCommand::ClusterVersion;
  1440. process_command(&config_offline).unwrap_err();
  1441. request_and_confirm_airdrop(
  1442. &rpc_client,
  1443. &config,
  1444. &config.signers[0].pubkey(),
  1445. 50_000_000_000_000,
  1446. )
  1447. .unwrap();
  1448. check_balance!(50_000_000_000_000, &rpc_client, &config.signers[0].pubkey());
  1449. request_and_confirm_airdrop(
  1450. &rpc_client,
  1451. &config_offline,
  1452. &offline_pubkey,
  1453. 1_000_000_000_000,
  1454. )
  1455. .unwrap();
  1456. check_balance!(1_000_000_000_000, &rpc_client, &offline_pubkey);
  1457. // Create stake account, identity is authority
  1458. let stake_balance = minimum_balance + 10_000_000_000;
  1459. let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  1460. let stake_account_pubkey = stake_keypair.pubkey();
  1461. config.signers.push(&stake_keypair);
  1462. config.command = CliCommand::CreateStakeAccount {
  1463. stake_account: 1,
  1464. seed: None,
  1465. staker: Some(offline_pubkey),
  1466. withdrawer: Some(offline_pubkey),
  1467. withdrawer_signer: None,
  1468. lockup: Lockup::default(),
  1469. amount: SpendAmount::Some(10 * stake_balance),
  1470. sign_only: false,
  1471. dump_transaction_message: false,
  1472. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  1473. nonce_account: None,
  1474. nonce_authority: 0,
  1475. memo: None,
  1476. fee_payer: 0,
  1477. from: 0,
  1478. compute_unit_price,
  1479. };
  1480. process_command(&config).unwrap();
  1481. check_balance!(10 * stake_balance, &rpc_client, &stake_account_pubkey,);
  1482. // Create nonce account
  1483. let minimum_nonce_balance = rpc_client
  1484. .get_minimum_balance_for_rent_exemption(NonceState::size())
  1485. .unwrap();
  1486. let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
  1487. config.signers = vec![&default_signer, &nonce_account];
  1488. config.command = CliCommand::CreateNonceAccount {
  1489. nonce_account: 1,
  1490. seed: None,
  1491. nonce_authority: Some(offline_pubkey),
  1492. memo: None,
  1493. amount: SpendAmount::Some(minimum_nonce_balance),
  1494. compute_unit_price,
  1495. };
  1496. process_command(&config).unwrap();
  1497. check_balance!(minimum_nonce_balance, &rpc_client, &nonce_account.pubkey());
  1498. // Fetch nonce hash
  1499. let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  1500. &rpc_client,
  1501. &nonce_account.pubkey(),
  1502. CommitmentConfig::processed(),
  1503. )
  1504. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  1505. .unwrap()
  1506. .blockhash();
  1507. // Nonced offline split
  1508. let split_account = keypair_from_seed(&[2u8; 32]).unwrap();
  1509. check_balance!(0, &rpc_client, &split_account.pubkey());
  1510. config_offline.signers.push(&split_account);
  1511. config_offline.command = CliCommand::SplitStake {
  1512. stake_account_pubkey,
  1513. stake_authority: 0,
  1514. sign_only: true,
  1515. dump_transaction_message: false,
  1516. blockhash_query: BlockhashQuery::None(nonce_hash),
  1517. nonce_account: Some(nonce_account.pubkey()),
  1518. nonce_authority: 0,
  1519. memo: None,
  1520. split_stake_account: 1,
  1521. seed: None,
  1522. lamports: 2 * stake_balance,
  1523. fee_payer: 0,
  1524. compute_unit_price,
  1525. rent_exempt_reserve: Some(minimum_balance),
  1526. };
  1527. config_offline.output_format = OutputFormat::JsonCompact;
  1528. let sig_response = process_command(&config_offline).unwrap();
  1529. let sign_only = parse_sign_only_reply_string(&sig_response);
  1530. assert!(sign_only.has_all_signers());
  1531. let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
  1532. config.signers = vec![&offline_presigner, &split_account];
  1533. config.command = CliCommand::SplitStake {
  1534. stake_account_pubkey,
  1535. stake_authority: 0,
  1536. sign_only: false,
  1537. dump_transaction_message: false,
  1538. blockhash_query: BlockhashQuery::FeeCalculator(
  1539. blockhash_query::Source::NonceAccount(nonce_account.pubkey()),
  1540. sign_only.blockhash,
  1541. ),
  1542. nonce_account: Some(nonce_account.pubkey()),
  1543. nonce_authority: 0,
  1544. memo: None,
  1545. split_stake_account: 1,
  1546. seed: None,
  1547. lamports: 2 * stake_balance,
  1548. fee_payer: 0,
  1549. compute_unit_price,
  1550. rent_exempt_reserve: None,
  1551. };
  1552. process_command(&config).unwrap();
  1553. check_balance!(8 * stake_balance, &rpc_client, &stake_account_pubkey);
  1554. check_balance!(
  1555. 2 * stake_balance + minimum_balance,
  1556. &rpc_client,
  1557. &split_account.pubkey()
  1558. );
  1559. }
  1560. #[test_case(None; "base")]
  1561. #[test_case(Some(1_000_000); "with_compute_unit_price")]
  1562. fn test_stake_set_lockup(compute_unit_price: Option<u64>) {
  1563. solana_logger::setup();
  1564. let mint_keypair = Keypair::new();
  1565. let mint_pubkey = mint_keypair.pubkey();
  1566. let faucet_addr = run_local_faucet(mint_keypair, None);
  1567. let test_validator = TestValidator::with_custom_fees(
  1568. mint_pubkey,
  1569. 1,
  1570. Some(faucet_addr),
  1571. SocketAddrSpace::Unspecified,
  1572. );
  1573. let rpc_client =
  1574. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  1575. let default_signer = Keypair::new();
  1576. let offline_signer = Keypair::new();
  1577. let mut config = CliConfig::recent_for_tests();
  1578. config.json_rpc_url = test_validator.rpc_url();
  1579. config.signers = vec![&default_signer];
  1580. let mut config_offline = CliConfig::recent_for_tests();
  1581. config_offline.json_rpc_url = String::default();
  1582. config_offline.signers = vec![&offline_signer];
  1583. let offline_pubkey = config_offline.signers[0].pubkey();
  1584. // Verify we're offline
  1585. config_offline.command = CliCommand::ClusterVersion;
  1586. process_command(&config_offline).unwrap_err();
  1587. request_and_confirm_airdrop(
  1588. &rpc_client,
  1589. &config,
  1590. &config.signers[0].pubkey(),
  1591. 5_000_000_000_000,
  1592. )
  1593. .unwrap();
  1594. check_balance!(5_000_000_000_000, &rpc_client, &config.signers[0].pubkey());
  1595. request_and_confirm_airdrop(
  1596. &rpc_client,
  1597. &config_offline,
  1598. &offline_pubkey,
  1599. 1_000_000_000_000,
  1600. )
  1601. .unwrap();
  1602. check_balance!(1_000_000_000_000, &rpc_client, &offline_pubkey);
  1603. // Create stake account, identity is authority
  1604. let stake_balance = rpc_client
  1605. .get_minimum_balance_for_rent_exemption(StakeStateV2::size_of())
  1606. .unwrap()
  1607. + 10_000_000_000;
  1608. let stake_keypair = keypair_from_seed(&[0u8; 32]).unwrap();
  1609. let stake_account_pubkey = stake_keypair.pubkey();
  1610. let lockup = Lockup {
  1611. custodian: config.signers[0].pubkey(),
  1612. ..Lockup::default()
  1613. };
  1614. config.signers.push(&stake_keypair);
  1615. config.command = CliCommand::CreateStakeAccount {
  1616. stake_account: 1,
  1617. seed: None,
  1618. staker: Some(offline_pubkey),
  1619. withdrawer: Some(config.signers[0].pubkey()),
  1620. withdrawer_signer: None,
  1621. lockup,
  1622. amount: SpendAmount::Some(10 * stake_balance),
  1623. sign_only: false,
  1624. dump_transaction_message: false,
  1625. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  1626. nonce_account: None,
  1627. nonce_authority: 0,
  1628. memo: None,
  1629. fee_payer: 0,
  1630. from: 0,
  1631. compute_unit_price,
  1632. };
  1633. process_command(&config).unwrap();
  1634. check_balance!(10 * stake_balance, &rpc_client, &stake_account_pubkey,);
  1635. // Online set lockup
  1636. let lockup = LockupArgs {
  1637. unix_timestamp: Some(1_581_534_570),
  1638. epoch: Some(200),
  1639. custodian: None,
  1640. };
  1641. config.signers.pop();
  1642. config.command = CliCommand::StakeSetLockup {
  1643. stake_account_pubkey,
  1644. lockup,
  1645. new_custodian_signer: None,
  1646. custodian: 0,
  1647. sign_only: false,
  1648. dump_transaction_message: false,
  1649. blockhash_query: BlockhashQuery::default(),
  1650. nonce_account: None,
  1651. nonce_authority: 0,
  1652. memo: None,
  1653. fee_payer: 0,
  1654. compute_unit_price,
  1655. };
  1656. process_command(&config).unwrap();
  1657. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  1658. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  1659. let current_lockup = match stake_state {
  1660. StakeStateV2::Initialized(meta) => meta.lockup,
  1661. _ => panic!("Unexpected stake state!"),
  1662. };
  1663. assert_eq!(
  1664. current_lockup.unix_timestamp,
  1665. lockup.unix_timestamp.unwrap()
  1666. );
  1667. assert_eq!(current_lockup.epoch, lockup.epoch.unwrap());
  1668. assert_eq!(current_lockup.custodian, config.signers[0].pubkey());
  1669. // Set custodian to another pubkey
  1670. let online_custodian = Keypair::new();
  1671. let online_custodian_pubkey = online_custodian.pubkey();
  1672. let lockup = LockupArgs {
  1673. unix_timestamp: Some(1_581_534_571),
  1674. epoch: Some(201),
  1675. custodian: Some(online_custodian_pubkey),
  1676. };
  1677. config.command = CliCommand::StakeSetLockup {
  1678. stake_account_pubkey,
  1679. lockup,
  1680. new_custodian_signer: None,
  1681. custodian: 0,
  1682. sign_only: false,
  1683. dump_transaction_message: false,
  1684. blockhash_query: BlockhashQuery::default(),
  1685. nonce_account: None,
  1686. nonce_authority: 0,
  1687. memo: None,
  1688. fee_payer: 0,
  1689. compute_unit_price,
  1690. };
  1691. process_command(&config).unwrap();
  1692. let lockup = LockupArgs {
  1693. unix_timestamp: Some(1_581_534_572),
  1694. epoch: Some(202),
  1695. custodian: None,
  1696. };
  1697. config.signers = vec![&default_signer, &online_custodian];
  1698. config.command = CliCommand::StakeSetLockup {
  1699. stake_account_pubkey,
  1700. lockup,
  1701. new_custodian_signer: None,
  1702. custodian: 1,
  1703. sign_only: false,
  1704. dump_transaction_message: false,
  1705. blockhash_query: BlockhashQuery::default(),
  1706. nonce_account: None,
  1707. nonce_authority: 0,
  1708. memo: None,
  1709. fee_payer: 0,
  1710. compute_unit_price,
  1711. };
  1712. process_command(&config).unwrap();
  1713. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  1714. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  1715. let current_lockup = match stake_state {
  1716. StakeStateV2::Initialized(meta) => meta.lockup,
  1717. _ => panic!("Unexpected stake state!"),
  1718. };
  1719. assert_eq!(
  1720. current_lockup.unix_timestamp,
  1721. lockup.unix_timestamp.unwrap()
  1722. );
  1723. assert_eq!(current_lockup.epoch, lockup.epoch.unwrap());
  1724. assert_eq!(current_lockup.custodian, online_custodian_pubkey);
  1725. // Set custodian to offline pubkey
  1726. let lockup = LockupArgs {
  1727. unix_timestamp: Some(1_581_534_573),
  1728. epoch: Some(203),
  1729. custodian: Some(offline_pubkey),
  1730. };
  1731. config.command = CliCommand::StakeSetLockup {
  1732. stake_account_pubkey,
  1733. lockup,
  1734. new_custodian_signer: None,
  1735. custodian: 1,
  1736. sign_only: false,
  1737. dump_transaction_message: false,
  1738. blockhash_query: BlockhashQuery::default(),
  1739. nonce_account: None,
  1740. nonce_authority: 0,
  1741. memo: None,
  1742. fee_payer: 0,
  1743. compute_unit_price,
  1744. };
  1745. process_command(&config).unwrap();
  1746. // Create nonce account
  1747. let minimum_nonce_balance = rpc_client
  1748. .get_minimum_balance_for_rent_exemption(NonceState::size())
  1749. .unwrap();
  1750. let nonce_account = keypair_from_seed(&[1u8; 32]).unwrap();
  1751. let nonce_account_pubkey = nonce_account.pubkey();
  1752. config.signers = vec![&default_signer, &nonce_account];
  1753. config.command = CliCommand::CreateNonceAccount {
  1754. nonce_account: 1,
  1755. seed: None,
  1756. nonce_authority: Some(offline_pubkey),
  1757. memo: None,
  1758. amount: SpendAmount::Some(minimum_nonce_balance),
  1759. compute_unit_price,
  1760. };
  1761. process_command(&config).unwrap();
  1762. check_balance!(minimum_nonce_balance, &rpc_client, &nonce_account_pubkey);
  1763. // Fetch nonce hash
  1764. let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  1765. &rpc_client,
  1766. &nonce_account.pubkey(),
  1767. CommitmentConfig::processed(),
  1768. )
  1769. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  1770. .unwrap()
  1771. .blockhash();
  1772. // Nonced offline set lockup
  1773. let lockup = LockupArgs {
  1774. unix_timestamp: Some(1_581_534_576),
  1775. epoch: Some(222),
  1776. custodian: None,
  1777. };
  1778. config_offline.command = CliCommand::StakeSetLockup {
  1779. stake_account_pubkey,
  1780. lockup,
  1781. new_custodian_signer: None,
  1782. custodian: 0,
  1783. sign_only: true,
  1784. dump_transaction_message: false,
  1785. blockhash_query: BlockhashQuery::None(nonce_hash),
  1786. nonce_account: Some(nonce_account_pubkey),
  1787. nonce_authority: 0,
  1788. memo: None,
  1789. fee_payer: 0,
  1790. compute_unit_price,
  1791. };
  1792. config_offline.output_format = OutputFormat::JsonCompact;
  1793. let sig_response = process_command(&config_offline).unwrap();
  1794. let sign_only = parse_sign_only_reply_string(&sig_response);
  1795. assert!(sign_only.has_all_signers());
  1796. let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
  1797. config.signers = vec![&offline_presigner];
  1798. config.command = CliCommand::StakeSetLockup {
  1799. stake_account_pubkey,
  1800. lockup,
  1801. new_custodian_signer: None,
  1802. custodian: 0,
  1803. sign_only: false,
  1804. dump_transaction_message: false,
  1805. blockhash_query: BlockhashQuery::FeeCalculator(
  1806. blockhash_query::Source::NonceAccount(nonce_account_pubkey),
  1807. sign_only.blockhash,
  1808. ),
  1809. nonce_account: Some(nonce_account_pubkey),
  1810. nonce_authority: 0,
  1811. memo: None,
  1812. fee_payer: 0,
  1813. compute_unit_price,
  1814. };
  1815. process_command(&config).unwrap();
  1816. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  1817. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  1818. let current_lockup = match stake_state {
  1819. StakeStateV2::Initialized(meta) => meta.lockup,
  1820. _ => panic!("Unexpected stake state!"),
  1821. };
  1822. assert_eq!(
  1823. current_lockup.unix_timestamp,
  1824. lockup.unix_timestamp.unwrap()
  1825. );
  1826. assert_eq!(current_lockup.epoch, lockup.epoch.unwrap());
  1827. assert_eq!(current_lockup.custodian, offline_pubkey);
  1828. }
  1829. #[test_case(None; "base")]
  1830. #[test_case(Some(1_000_000); "with_compute_unit_price")]
  1831. fn test_offline_nonced_create_stake_account_and_withdraw(compute_unit_price: Option<u64>) {
  1832. solana_logger::setup();
  1833. let mint_keypair = Keypair::new();
  1834. let mint_pubkey = mint_keypair.pubkey();
  1835. let faucet_addr = run_local_faucet(mint_keypair, None);
  1836. let test_validator =
  1837. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  1838. let rpc_client =
  1839. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  1840. let mut config = CliConfig::recent_for_tests();
  1841. let default_signer = keypair_from_seed(&[1u8; 32]).unwrap();
  1842. config.signers = vec![&default_signer];
  1843. config.json_rpc_url = test_validator.rpc_url();
  1844. let mut config_offline = CliConfig::recent_for_tests();
  1845. let offline_signer = keypair_from_seed(&[2u8; 32]).unwrap();
  1846. config_offline.signers = vec![&offline_signer];
  1847. let offline_pubkey = config_offline.signers[0].pubkey();
  1848. config_offline.json_rpc_url = String::default();
  1849. config_offline.command = CliCommand::ClusterVersion;
  1850. // Verify that we cannot reach the cluster
  1851. process_command(&config_offline).unwrap_err();
  1852. request_and_confirm_airdrop(
  1853. &rpc_client,
  1854. &config,
  1855. &config.signers[0].pubkey(),
  1856. 200_000_000_000,
  1857. )
  1858. .unwrap();
  1859. check_balance!(200_000_000_000, &rpc_client, &config.signers[0].pubkey());
  1860. request_and_confirm_airdrop(
  1861. &rpc_client,
  1862. &config_offline,
  1863. &offline_pubkey,
  1864. 100_000_000_000,
  1865. )
  1866. .unwrap();
  1867. check_balance!(100_000_000_000, &rpc_client, &offline_pubkey);
  1868. // Create nonce account
  1869. let minimum_nonce_balance = rpc_client
  1870. .get_minimum_balance_for_rent_exemption(NonceState::size())
  1871. .unwrap();
  1872. let nonce_account = keypair_from_seed(&[3u8; 32]).unwrap();
  1873. let nonce_pubkey = nonce_account.pubkey();
  1874. config.signers.push(&nonce_account);
  1875. config.command = CliCommand::CreateNonceAccount {
  1876. nonce_account: 1,
  1877. seed: None,
  1878. nonce_authority: Some(offline_pubkey),
  1879. memo: None,
  1880. amount: SpendAmount::Some(minimum_nonce_balance),
  1881. compute_unit_price,
  1882. };
  1883. process_command(&config).unwrap();
  1884. // Fetch nonce hash
  1885. let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  1886. &rpc_client,
  1887. &nonce_account.pubkey(),
  1888. CommitmentConfig::processed(),
  1889. )
  1890. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  1891. .unwrap()
  1892. .blockhash();
  1893. // Create stake account offline
  1894. let stake_keypair = keypair_from_seed(&[4u8; 32]).unwrap();
  1895. let stake_pubkey = stake_keypair.pubkey();
  1896. config_offline.signers.push(&stake_keypair);
  1897. config_offline.command = CliCommand::CreateStakeAccount {
  1898. stake_account: 1,
  1899. seed: None,
  1900. staker: None,
  1901. withdrawer: None,
  1902. withdrawer_signer: None,
  1903. lockup: Lockup::default(),
  1904. amount: SpendAmount::Some(50_000_000_000),
  1905. sign_only: true,
  1906. dump_transaction_message: false,
  1907. blockhash_query: BlockhashQuery::None(nonce_hash),
  1908. nonce_account: Some(nonce_pubkey),
  1909. nonce_authority: 0,
  1910. memo: None,
  1911. fee_payer: 0,
  1912. from: 0,
  1913. compute_unit_price,
  1914. };
  1915. config_offline.output_format = OutputFormat::JsonCompact;
  1916. let sig_response = process_command(&config_offline).unwrap();
  1917. let sign_only = parse_sign_only_reply_string(&sig_response);
  1918. assert!(sign_only.has_all_signers());
  1919. let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
  1920. let stake_presigner = sign_only.presigner_of(&stake_pubkey).unwrap();
  1921. config.signers = vec![&offline_presigner, &stake_presigner];
  1922. config.command = CliCommand::CreateStakeAccount {
  1923. stake_account: 1,
  1924. seed: None,
  1925. staker: Some(offline_pubkey),
  1926. withdrawer: None,
  1927. withdrawer_signer: None,
  1928. lockup: Lockup::default(),
  1929. amount: SpendAmount::Some(50_000_000_000),
  1930. sign_only: false,
  1931. dump_transaction_message: false,
  1932. blockhash_query: BlockhashQuery::FeeCalculator(
  1933. blockhash_query::Source::NonceAccount(nonce_pubkey),
  1934. sign_only.blockhash,
  1935. ),
  1936. nonce_account: Some(nonce_pubkey),
  1937. nonce_authority: 0,
  1938. memo: None,
  1939. fee_payer: 0,
  1940. from: 0,
  1941. compute_unit_price,
  1942. };
  1943. process_command(&config).unwrap();
  1944. check_balance!(50_000_000_000, &rpc_client, &stake_pubkey);
  1945. // Fetch nonce hash
  1946. let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  1947. &rpc_client,
  1948. &nonce_account.pubkey(),
  1949. CommitmentConfig::processed(),
  1950. )
  1951. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  1952. .unwrap()
  1953. .blockhash();
  1954. // Offline, nonced stake-withdraw
  1955. let recipient = keypair_from_seed(&[5u8; 32]).unwrap();
  1956. let recipient_pubkey = recipient.pubkey();
  1957. config_offline.signers.pop();
  1958. config_offline.command = CliCommand::WithdrawStake {
  1959. stake_account_pubkey: stake_pubkey,
  1960. destination_account_pubkey: recipient_pubkey,
  1961. amount: SpendAmount::Some(50_000_000_000),
  1962. withdraw_authority: 0,
  1963. custodian: None,
  1964. sign_only: true,
  1965. dump_transaction_message: false,
  1966. blockhash_query: BlockhashQuery::None(nonce_hash),
  1967. nonce_account: Some(nonce_pubkey),
  1968. nonce_authority: 0,
  1969. memo: None,
  1970. seed: None,
  1971. fee_payer: 0,
  1972. compute_unit_price,
  1973. };
  1974. let sig_response = process_command(&config_offline).unwrap();
  1975. let sign_only = parse_sign_only_reply_string(&sig_response);
  1976. let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
  1977. config.signers = vec![&offline_presigner];
  1978. config.command = CliCommand::WithdrawStake {
  1979. stake_account_pubkey: stake_pubkey,
  1980. destination_account_pubkey: recipient_pubkey,
  1981. amount: SpendAmount::Some(50_000_000_000),
  1982. withdraw_authority: 0,
  1983. custodian: None,
  1984. sign_only: false,
  1985. dump_transaction_message: false,
  1986. blockhash_query: BlockhashQuery::FeeCalculator(
  1987. blockhash_query::Source::NonceAccount(nonce_pubkey),
  1988. sign_only.blockhash,
  1989. ),
  1990. nonce_account: Some(nonce_pubkey),
  1991. nonce_authority: 0,
  1992. memo: None,
  1993. seed: None,
  1994. fee_payer: 0,
  1995. compute_unit_price,
  1996. };
  1997. process_command(&config).unwrap();
  1998. check_balance!(50_000_000_000, &rpc_client, &recipient_pubkey);
  1999. // Fetch nonce hash
  2000. let nonce_hash = solana_rpc_client_nonce_utils::get_account_with_commitment(
  2001. &rpc_client,
  2002. &nonce_account.pubkey(),
  2003. CommitmentConfig::processed(),
  2004. )
  2005. .and_then(|ref a| solana_rpc_client_nonce_utils::data_from_account(a))
  2006. .unwrap()
  2007. .blockhash();
  2008. // Create another stake account. This time with seed
  2009. let seed = "seedy";
  2010. config_offline.signers = vec![&offline_signer, &stake_keypair];
  2011. config_offline.command = CliCommand::CreateStakeAccount {
  2012. stake_account: 1,
  2013. seed: Some(seed.to_string()),
  2014. staker: None,
  2015. withdrawer: None,
  2016. withdrawer_signer: None,
  2017. lockup: Lockup::default(),
  2018. amount: SpendAmount::Some(50_000_000_000),
  2019. sign_only: true,
  2020. dump_transaction_message: false,
  2021. blockhash_query: BlockhashQuery::None(nonce_hash),
  2022. nonce_account: Some(nonce_pubkey),
  2023. nonce_authority: 0,
  2024. memo: None,
  2025. fee_payer: 0,
  2026. from: 0,
  2027. compute_unit_price,
  2028. };
  2029. let sig_response = process_command(&config_offline).unwrap();
  2030. let sign_only = parse_sign_only_reply_string(&sig_response);
  2031. let offline_presigner = sign_only.presigner_of(&offline_pubkey).unwrap();
  2032. let stake_presigner = sign_only.presigner_of(&stake_pubkey).unwrap();
  2033. config.signers = vec![&offline_presigner, &stake_presigner];
  2034. config.command = CliCommand::CreateStakeAccount {
  2035. stake_account: 1,
  2036. seed: Some(seed.to_string()),
  2037. staker: Some(offline_pubkey),
  2038. withdrawer: Some(offline_pubkey),
  2039. withdrawer_signer: None,
  2040. lockup: Lockup::default(),
  2041. amount: SpendAmount::Some(50_000_000_000),
  2042. sign_only: false,
  2043. dump_transaction_message: false,
  2044. blockhash_query: BlockhashQuery::FeeCalculator(
  2045. blockhash_query::Source::NonceAccount(nonce_pubkey),
  2046. sign_only.blockhash,
  2047. ),
  2048. nonce_account: Some(nonce_pubkey),
  2049. nonce_authority: 0,
  2050. memo: None,
  2051. fee_payer: 0,
  2052. from: 0,
  2053. compute_unit_price,
  2054. };
  2055. process_command(&config).unwrap();
  2056. let seed_address =
  2057. Pubkey::create_with_seed(&stake_pubkey, seed, &stake::program::id()).unwrap();
  2058. check_balance!(50_000_000_000, &rpc_client, &seed_address);
  2059. }
  2060. #[test]
  2061. fn test_stake_checked_instructions() {
  2062. solana_logger::setup();
  2063. let mint_keypair = Keypair::new();
  2064. let mint_pubkey = mint_keypair.pubkey();
  2065. let faucet_addr = run_local_faucet(mint_keypair, None);
  2066. let test_validator =
  2067. TestValidator::with_no_fees(mint_pubkey, Some(faucet_addr), SocketAddrSpace::Unspecified);
  2068. let rpc_client =
  2069. RpcClient::new_with_commitment(test_validator.rpc_url(), CommitmentConfig::processed());
  2070. let default_signer = Keypair::new();
  2071. let mut config = CliConfig::recent_for_tests();
  2072. config.json_rpc_url = test_validator.rpc_url();
  2073. config.signers = vec![&default_signer];
  2074. request_and_confirm_airdrop(
  2075. &rpc_client,
  2076. &config,
  2077. &config.signers[0].pubkey(),
  2078. 100_000_000_000,
  2079. )
  2080. .unwrap();
  2081. // Create stake account with withdrawer
  2082. let stake_keypair = Keypair::new();
  2083. let stake_account_pubkey = stake_keypair.pubkey();
  2084. let withdrawer_keypair = Keypair::new();
  2085. let withdrawer_pubkey = withdrawer_keypair.pubkey();
  2086. config.signers.push(&stake_keypair);
  2087. config.command = CliCommand::CreateStakeAccount {
  2088. stake_account: 1,
  2089. seed: None,
  2090. staker: None,
  2091. withdrawer: Some(withdrawer_pubkey),
  2092. withdrawer_signer: Some(1),
  2093. lockup: Lockup::default(),
  2094. amount: SpendAmount::Some(50_000_000_000),
  2095. sign_only: false,
  2096. dump_transaction_message: false,
  2097. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  2098. nonce_account: None,
  2099. nonce_authority: 0,
  2100. memo: None,
  2101. fee_payer: 0,
  2102. from: 0,
  2103. compute_unit_price: None,
  2104. };
  2105. process_command(&config).unwrap_err(); // unsigned authority should fail
  2106. config.signers = vec![&default_signer, &stake_keypair, &withdrawer_keypair];
  2107. config.command = CliCommand::CreateStakeAccount {
  2108. stake_account: 1,
  2109. seed: None,
  2110. staker: None,
  2111. withdrawer: Some(withdrawer_pubkey),
  2112. withdrawer_signer: Some(1),
  2113. lockup: Lockup::default(),
  2114. amount: SpendAmount::Some(50_000_000_000),
  2115. sign_only: false,
  2116. dump_transaction_message: false,
  2117. blockhash_query: BlockhashQuery::All(blockhash_query::Source::Cluster),
  2118. nonce_account: None,
  2119. nonce_authority: 0,
  2120. memo: None,
  2121. fee_payer: 0,
  2122. from: 0,
  2123. compute_unit_price: None,
  2124. };
  2125. process_command(&config).unwrap();
  2126. // Re-authorize account, checking new authority
  2127. let staker_keypair = Keypair::new();
  2128. let staker_pubkey = staker_keypair.pubkey();
  2129. config.signers = vec![&default_signer];
  2130. config.command = CliCommand::StakeAuthorize {
  2131. stake_account_pubkey,
  2132. new_authorizations: vec![StakeAuthorizationIndexed {
  2133. authorization_type: StakeAuthorize::Staker,
  2134. new_authority_pubkey: staker_pubkey,
  2135. authority: 0,
  2136. new_authority_signer: Some(0),
  2137. }],
  2138. sign_only: false,
  2139. dump_transaction_message: false,
  2140. blockhash_query: BlockhashQuery::default(),
  2141. nonce_account: None,
  2142. nonce_authority: 0,
  2143. memo: None,
  2144. fee_payer: 0,
  2145. custodian: None,
  2146. no_wait: false,
  2147. compute_unit_price: None,
  2148. };
  2149. process_command(&config).unwrap_err(); // unsigned authority should fail
  2150. config.signers = vec![&default_signer, &staker_keypair];
  2151. config.command = CliCommand::StakeAuthorize {
  2152. stake_account_pubkey,
  2153. new_authorizations: vec![StakeAuthorizationIndexed {
  2154. authorization_type: StakeAuthorize::Staker,
  2155. new_authority_pubkey: staker_pubkey,
  2156. authority: 0,
  2157. new_authority_signer: Some(1),
  2158. }],
  2159. sign_only: false,
  2160. dump_transaction_message: false,
  2161. blockhash_query: BlockhashQuery::default(),
  2162. nonce_account: None,
  2163. nonce_authority: 0,
  2164. memo: None,
  2165. fee_payer: 0,
  2166. custodian: None,
  2167. no_wait: false,
  2168. compute_unit_price: None,
  2169. };
  2170. process_command(&config).unwrap();
  2171. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  2172. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  2173. let current_authority = match stake_state {
  2174. StakeStateV2::Initialized(meta) => meta.authorized.staker,
  2175. _ => panic!("Unexpected stake state!"),
  2176. };
  2177. assert_eq!(current_authority, staker_pubkey);
  2178. let new_withdrawer_keypair = Keypair::new();
  2179. let new_withdrawer_pubkey = new_withdrawer_keypair.pubkey();
  2180. config.signers = vec![&default_signer, &withdrawer_keypair];
  2181. config.command = CliCommand::StakeAuthorize {
  2182. stake_account_pubkey,
  2183. new_authorizations: vec![StakeAuthorizationIndexed {
  2184. authorization_type: StakeAuthorize::Withdrawer,
  2185. new_authority_pubkey: new_withdrawer_pubkey,
  2186. authority: 1,
  2187. new_authority_signer: Some(1),
  2188. }],
  2189. sign_only: false,
  2190. dump_transaction_message: false,
  2191. blockhash_query: BlockhashQuery::default(),
  2192. nonce_account: None,
  2193. nonce_authority: 0,
  2194. memo: None,
  2195. fee_payer: 0,
  2196. custodian: None,
  2197. no_wait: false,
  2198. compute_unit_price: None,
  2199. };
  2200. process_command(&config).unwrap_err(); // unsigned authority should fail
  2201. config.signers = vec![
  2202. &default_signer,
  2203. &withdrawer_keypair,
  2204. &new_withdrawer_keypair,
  2205. ];
  2206. config.command = CliCommand::StakeAuthorize {
  2207. stake_account_pubkey,
  2208. new_authorizations: vec![StakeAuthorizationIndexed {
  2209. authorization_type: StakeAuthorize::Withdrawer,
  2210. new_authority_pubkey: new_withdrawer_pubkey,
  2211. authority: 1,
  2212. new_authority_signer: Some(2),
  2213. }],
  2214. sign_only: false,
  2215. dump_transaction_message: false,
  2216. blockhash_query: BlockhashQuery::default(),
  2217. nonce_account: None,
  2218. nonce_authority: 0,
  2219. memo: None,
  2220. fee_payer: 0,
  2221. custodian: None,
  2222. no_wait: false,
  2223. compute_unit_price: None,
  2224. };
  2225. process_command(&config).unwrap();
  2226. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  2227. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  2228. let current_authority = match stake_state {
  2229. StakeStateV2::Initialized(meta) => meta.authorized.withdrawer,
  2230. _ => panic!("Unexpected stake state!"),
  2231. };
  2232. assert_eq!(current_authority, new_withdrawer_pubkey);
  2233. // Set lockup, checking new custodian
  2234. let custodian = Keypair::new();
  2235. let custodian_pubkey = custodian.pubkey();
  2236. let lockup = LockupArgs {
  2237. unix_timestamp: Some(1_581_534_570),
  2238. epoch: Some(200),
  2239. custodian: Some(custodian_pubkey),
  2240. };
  2241. config.signers = vec![&default_signer, &new_withdrawer_keypair];
  2242. config.command = CliCommand::StakeSetLockup {
  2243. stake_account_pubkey,
  2244. lockup,
  2245. new_custodian_signer: Some(1),
  2246. custodian: 1,
  2247. sign_only: false,
  2248. dump_transaction_message: false,
  2249. blockhash_query: BlockhashQuery::default(),
  2250. nonce_account: None,
  2251. nonce_authority: 0,
  2252. memo: None,
  2253. fee_payer: 0,
  2254. compute_unit_price: None,
  2255. };
  2256. process_command(&config).unwrap_err(); // unsigned new custodian should fail
  2257. config.signers = vec![&default_signer, &new_withdrawer_keypair, &custodian];
  2258. config.command = CliCommand::StakeSetLockup {
  2259. stake_account_pubkey,
  2260. lockup,
  2261. new_custodian_signer: Some(2),
  2262. custodian: 1,
  2263. sign_only: false,
  2264. dump_transaction_message: false,
  2265. blockhash_query: BlockhashQuery::default(),
  2266. nonce_account: None,
  2267. nonce_authority: 0,
  2268. memo: None,
  2269. fee_payer: 0,
  2270. compute_unit_price: None,
  2271. };
  2272. process_command(&config).unwrap();
  2273. let stake_account = rpc_client.get_account(&stake_account_pubkey).unwrap();
  2274. let stake_state: StakeStateV2 = stake_account.state().unwrap();
  2275. let current_lockup = match stake_state {
  2276. StakeStateV2::Initialized(meta) => meta.lockup,
  2277. _ => panic!("Unexpected stake state!"),
  2278. };
  2279. assert_eq!(
  2280. current_lockup.unix_timestamp,
  2281. lockup.unix_timestamp.unwrap()
  2282. );
  2283. assert_eq!(current_lockup.epoch, lockup.epoch.unwrap());
  2284. assert_eq!(current_lockup.custodian, custodian_pubkey);
  2285. }
  2286. #[test]
  2287. fn test_stake_minimum_delegation() {
  2288. let test_validator =
  2289. TestValidator::with_no_fees(Pubkey::new_unique(), None, SocketAddrSpace::Unspecified);
  2290. let mut config = CliConfig::recent_for_tests();
  2291. config.json_rpc_url = test_validator.rpc_url();
  2292. config.command = CliCommand::StakeMinimumDelegation {
  2293. use_lamports_unit: true,
  2294. };
  2295. let result = process_command(&config);
  2296. assert_matches!(result, Ok(..));
  2297. }