From 2c8c2029d83162c65befc59d91f80a82b016793c Mon Sep 17 00:00:00 2001 From: Parth Date: Tue, 1 Oct 2019 01:14:49 +0530 Subject: [PATCH] cli: enforce rent-exemption balance for stake, vote and program accounts in cli (#6118) * require minimum balance for stake, vote and program accounts --- cli/src/stake.rs | 10 ++++++++ cli/src/vote.rs | 26 ++++++-------------- cli/src/wallet.rs | 35 +++++---------------------- cli/tests/deploy.rs | 11 +++++++-- client/src/mock_rpc_client_request.rs | 1 + client/src/rpc_client.rs | 8 ++++-- core/src/validator.rs | 3 +++ multinode-demo/validator.sh | 2 +- 8 files changed, 43 insertions(+), 53 deletions(-) diff --git a/cli/src/stake.rs b/cli/src/stake.rs index 34258d812..933108d6f 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -367,6 +367,16 @@ pub fn process_create_stake_account( .into()); } + let minimum_balance = + rpc_client.get_minimum_balance_for_rent_exemption(std::mem::size_of::())?; + + if lamports < minimum_balance { + Err(WalletError::BadParameter(format!( + "need atleast {} lamports for stake account to be rent exempt, provided lamports: {}", + minimum_balance, lamports + )))?; + } + let ixs = stake_instruction::create_stake_account_with_lockup( &config.keypair.pubkey(), stake_account_pubkey, diff --git a/cli/src/vote.rs b/cli/src/vote.rs index 2640cdd18..48a99a88a 100644 --- a/cli/src/vote.rs +++ b/cli/src/vote.rs @@ -26,8 +26,6 @@ pub fn parse_vote_create_account( let authorized_voter = pubkey_of(matches, "authorized_voter").unwrap_or(vote_account_pubkey); let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer").unwrap_or(*pubkey); - let lamports = amount_of(matches, "amount", "unit").expect("Invalid amount"); - Ok(WalletCommand::CreateVoteAccount( vote_account_pubkey, VoteInit { @@ -36,7 +34,6 @@ pub fn parse_vote_create_account( authorized_withdrawer, commission, }, - lamports, )) } @@ -70,7 +67,6 @@ pub fn process_create_vote_account( config: &WalletConfig, vote_account_pubkey: &Pubkey, vote_init: &VoteInit, - lamports: u64, ) -> ProcessResult { check_unique_pubkeys( (vote_account_pubkey, "vote_account_pubkey".to_string()), @@ -80,11 +76,13 @@ pub fn process_create_vote_account( (&config.keypair.pubkey(), "wallet keypair".to_string()), (vote_account_pubkey, "vote_account_pubkey".to_string()), )?; + let required_balance = + rpc_client.get_minimum_balance_for_rent_exemption(VoteState::size_of())?; let ixs = vote_instruction::create_account( &config.keypair.pubkey(), vote_account_pubkey, vote_init, - lamports, + required_balance, ); let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash); @@ -303,10 +301,8 @@ mod tests { "create-vote-account", &pubkey_string, &node_pubkey_string, - "50", "--commission", "10", - "lamports", ]); assert_eq!( parse_command(&pubkey, &test_create_vote_account).unwrap(), @@ -317,8 +313,7 @@ mod tests { authorized_voter: pubkey, authorized_withdrawer: pubkey, commission: 10 - }, - 50 + } ) ); let test_create_vote_account2 = test_commands.clone().get_matches_from(vec![ @@ -326,7 +321,6 @@ mod tests { "create-vote-account", &pubkey_string, &node_pubkey_string, - "50", ]); assert_eq!( parse_command(&pubkey, &test_create_vote_account2).unwrap(), @@ -337,8 +331,7 @@ mod tests { authorized_voter: pubkey, authorized_withdrawer: pubkey, commission: 0 - }, - 858993459200 + } ) ); // test init with an authed voter @@ -348,8 +341,6 @@ mod tests { "create-vote-account", &pubkey_string, &node_pubkey_string, - "50", - "SOL", "--authorized-voter", &authed.to_string(), ]); @@ -362,8 +353,7 @@ mod tests { authorized_voter: authed, authorized_withdrawer: pubkey, commission: 0 - }, - 858993459200 + } ) ); // test init with an authed withdrawer @@ -372,7 +362,6 @@ mod tests { "create-vote-account", &pubkey_string, &node_pubkey_string, - "0.5", "--authorized-withdrawer", &authed.to_string(), ]); @@ -385,8 +374,7 @@ mod tests { authorized_voter: pubkey, authorized_withdrawer: authed, commission: 0 - }, - 8589934592 + } ) ); diff --git a/cli/src/wallet.rs b/cli/src/wallet.rs index dc7fa1906..6c83a7eb5 100644 --- a/cli/src/wallet.rs +++ b/cli/src/wallet.rs @@ -65,7 +65,7 @@ pub enum WalletCommand { Cancel(Pubkey), Confirm(Signature), VoteAuthorize(Pubkey, Pubkey, VoteAuthorize), - CreateVoteAccount(Pubkey, VoteInit, u64), + CreateVoteAccount(Pubkey, VoteInit), ShowAccount { pubkey: Pubkey, output_file: Option, @@ -655,11 +655,12 @@ fn process_deploy( // Build transactions to calculate fees let mut messages: Vec<&Message> = Vec::new(); let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let minimum_balance = rpc_client.get_minimum_balance_for_rent_exemption(program_data.len())?; let mut create_account_tx = system_transaction::create_account( &config.keypair, &program_id.pubkey(), blockhash, - 1, + minimum_balance, program_data.len() as u64, &bpf_loader::id(), ); @@ -1087,14 +1088,8 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult { WalletCommand::Confirm(signature) => process_confirm(&rpc_client, signature), // Create vote account - WalletCommand::CreateVoteAccount(vote_account_pubkey, vote_init, lamports) => { - process_create_vote_account( - &rpc_client, - config, - &vote_account_pubkey, - &vote_init, - *lamports, - ) + WalletCommand::CreateVoteAccount(vote_account_pubkey, vote_init) => { + process_create_vote_account(&rpc_client, config, &vote_account_pubkey, &vote_init) } WalletCommand::VoteAuthorize( @@ -1546,22 +1541,6 @@ pub fn app<'ab, 'v>(name: &str, about: &'ab str, version: &'v str) -> App<'ab, ' .validator(is_pubkey_or_keypair) .help("Validator that will vote with this account"), ) - .arg( - Arg::with_name("amount") - .index(3) - .value_name("AMOUNT") - .takes_value(true) - .required(true) - .help("The amount of send to the vote account (default unit SOL)"), - ) - .arg( - Arg::with_name("unit") - .index(4) - .value_name("UNIT") - .takes_value(true) - .possible_values(&["SOL", "lamports"]) - .help("Specify unit to use for request"), - ) .arg( Arg::with_name("commission") .long("commission") @@ -2324,7 +2303,6 @@ mod tests { authorized_withdrawer: bob_pubkey, commission: 0, }, - 10, ); let signature = process_command(&config); assert_eq!(signature.unwrap(), SIGNATURE.to_string()); @@ -2344,7 +2322,7 @@ mod tests { withdrawer: config.keypair.pubkey(), }, Lockup { slot: 0, custodian }, - 10, + 1234, ); let signature = process_command(&config); assert_eq!(signature.unwrap(), SIGNATURE.to_string()); @@ -2493,7 +2471,6 @@ mod tests { authorized_withdrawer: bob_pubkey, commission: 0, }, - 10, ); assert!(process_command(&config).is_err()); diff --git a/cli/tests/deploy.rs b/cli/tests/deploy.rs index c96b3f8dd..fcf43ff0c 100644 --- a/cli/tests/deploy.rs +++ b/cli/tests/deploy.rs @@ -28,12 +28,19 @@ fn test_wallet_deploy_program() { let rpc_client = RpcClient::new_socket(leader_data.rpc); + let mut file = File::open(pathbuf.to_str().unwrap()).unwrap(); + let mut program_data = Vec::new(); + file.read_to_end(&mut program_data).unwrap(); + let minimum_balance_for_rent_exemption = rpc_client + .get_minimum_balance_for_rent_exemption(program_data.len()) + .unwrap(); + let mut config = WalletConfig::default(); config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); config.command = WalletCommand::Airdrop { drone_host: None, drone_port: drone_addr.port(), - lamports: 50, + lamports: minimum_balance_for_rent_exemption + 1, // min balance for rent exemption + leftover for tx processing use_lamports_unit: true, }; process_command(&config).unwrap(); @@ -57,7 +64,7 @@ fn test_wallet_deploy_program() { let account_info_obj = account_info.as_object().unwrap(); assert_eq!( account_info_obj.get("lamports").unwrap().as_u64().unwrap(), - 1 + minimum_balance_for_rent_exemption ); let owner_array = account_info.get("owner").unwrap(); assert_eq!(owner_array, &json!(bpf_loader::id())); diff --git a/client/src/mock_rpc_client_request.rs b/client/src/mock_rpc_client_request.rs index 6b3eb2e86..d17ef7c9e 100644 --- a/client/src/mock_rpc_client_request.rs +++ b/client/src/mock_rpc_client_request.rs @@ -62,6 +62,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest { RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)), RpcRequest::GetSlot => Value::Number(Number::from(0)), RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()), + RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(1234)), _ => Value::Null, }; Ok(val) diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index c9f28d09d..195c6331f 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -346,8 +346,12 @@ impl RpcClient { ) })?; - let minimum_balance: u64 = - serde_json::from_value(minimum_balance_json).expect("deserialize minimum_balance"); + let minimum_balance: u64 = serde_json::from_value(minimum_balance_json).map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("GetMinimumBalanceForRentExemption parse failure: {:?}", err), + ) + })?; trace!( "Response minimum balance {:?} {:?}", data_len, diff --git a/core/src/validator.rs b/core/src/validator.rs index bd6498fcb..27f570226 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -583,6 +583,9 @@ pub fn new_validator_for_tests() -> (Validator, ContactInfo, Keypair, PathBuf) { .native_instruction_processors .push(solana_budget_program!()); + genesis_block.rent_calculator.lamports_per_byte_year = 1; + genesis_block.rent_calculator.exemption_threshold = 1.0; + let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block); let voting_keypair = Arc::new(Keypair::new()); diff --git a/multinode-demo/validator.sh b/multinode-demo/validator.sh index e2fefbc01..e3480a132 100755 --- a/multinode-demo/validator.sh +++ b/multinode-demo/validator.sh @@ -282,7 +282,7 @@ setup_validator_accounts() { fi echo "Creating validator vote account" - wallet create-vote-account "$voting_keypair_path" "$identity_keypair_path" 1 lamports --commission 127 || return $? + wallet create-vote-account "$voting_keypair_path" "$identity_keypair_path" --commission 127 || return $? fi echo "Validator vote account configured"