cli: enforce rent-exemption balance for stake, vote and program accounts in cli (#6118)

* require minimum balance for stake, vote and program accounts
This commit is contained in:
Parth 2019-10-01 01:14:49 +05:30 committed by GitHub
parent 4fbe36d9c6
commit 2c8c2029d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 43 additions and 53 deletions

View File

@ -367,6 +367,16 @@ pub fn process_create_stake_account(
.into()); .into());
} }
let minimum_balance =
rpc_client.get_minimum_balance_for_rent_exemption(std::mem::size_of::<StakeState>())?;
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( let ixs = stake_instruction::create_stake_account_with_lockup(
&config.keypair.pubkey(), &config.keypair.pubkey(),
stake_account_pubkey, stake_account_pubkey,

View File

@ -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_voter = pubkey_of(matches, "authorized_voter").unwrap_or(vote_account_pubkey);
let authorized_withdrawer = pubkey_of(matches, "authorized_withdrawer").unwrap_or(*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( Ok(WalletCommand::CreateVoteAccount(
vote_account_pubkey, vote_account_pubkey,
VoteInit { VoteInit {
@ -36,7 +34,6 @@ pub fn parse_vote_create_account(
authorized_withdrawer, authorized_withdrawer,
commission, commission,
}, },
lamports,
)) ))
} }
@ -70,7 +67,6 @@ pub fn process_create_vote_account(
config: &WalletConfig, config: &WalletConfig,
vote_account_pubkey: &Pubkey, vote_account_pubkey: &Pubkey,
vote_init: &VoteInit, vote_init: &VoteInit,
lamports: u64,
) -> ProcessResult { ) -> ProcessResult {
check_unique_pubkeys( check_unique_pubkeys(
(vote_account_pubkey, "vote_account_pubkey".to_string()), (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()), (&config.keypair.pubkey(), "wallet keypair".to_string()),
(vote_account_pubkey, "vote_account_pubkey".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( let ixs = vote_instruction::create_account(
&config.keypair.pubkey(), &config.keypair.pubkey(),
vote_account_pubkey, vote_account_pubkey,
vote_init, vote_init,
lamports, required_balance,
); );
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash); let mut tx = Transaction::new_signed_instructions(&[&config.keypair], ixs, recent_blockhash);
@ -303,10 +301,8 @@ mod tests {
"create-vote-account", "create-vote-account",
&pubkey_string, &pubkey_string,
&node_pubkey_string, &node_pubkey_string,
"50",
"--commission", "--commission",
"10", "10",
"lamports",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_vote_account).unwrap(), parse_command(&pubkey, &test_create_vote_account).unwrap(),
@ -317,8 +313,7 @@ mod tests {
authorized_voter: pubkey, authorized_voter: pubkey,
authorized_withdrawer: pubkey, authorized_withdrawer: pubkey,
commission: 10 commission: 10
}, }
50
) )
); );
let test_create_vote_account2 = test_commands.clone().get_matches_from(vec![ let test_create_vote_account2 = test_commands.clone().get_matches_from(vec![
@ -326,7 +321,6 @@ mod tests {
"create-vote-account", "create-vote-account",
&pubkey_string, &pubkey_string,
&node_pubkey_string, &node_pubkey_string,
"50",
]); ]);
assert_eq!( assert_eq!(
parse_command(&pubkey, &test_create_vote_account2).unwrap(), parse_command(&pubkey, &test_create_vote_account2).unwrap(),
@ -337,8 +331,7 @@ mod tests {
authorized_voter: pubkey, authorized_voter: pubkey,
authorized_withdrawer: pubkey, authorized_withdrawer: pubkey,
commission: 0 commission: 0
}, }
858993459200
) )
); );
// test init with an authed voter // test init with an authed voter
@ -348,8 +341,6 @@ mod tests {
"create-vote-account", "create-vote-account",
&pubkey_string, &pubkey_string,
&node_pubkey_string, &node_pubkey_string,
"50",
"SOL",
"--authorized-voter", "--authorized-voter",
&authed.to_string(), &authed.to_string(),
]); ]);
@ -362,8 +353,7 @@ mod tests {
authorized_voter: authed, authorized_voter: authed,
authorized_withdrawer: pubkey, authorized_withdrawer: pubkey,
commission: 0 commission: 0
}, }
858993459200
) )
); );
// test init with an authed withdrawer // test init with an authed withdrawer
@ -372,7 +362,6 @@ mod tests {
"create-vote-account", "create-vote-account",
&pubkey_string, &pubkey_string,
&node_pubkey_string, &node_pubkey_string,
"0.5",
"--authorized-withdrawer", "--authorized-withdrawer",
&authed.to_string(), &authed.to_string(),
]); ]);
@ -385,8 +374,7 @@ mod tests {
authorized_voter: pubkey, authorized_voter: pubkey,
authorized_withdrawer: authed, authorized_withdrawer: authed,
commission: 0 commission: 0
}, }
8589934592
) )
); );

View File

@ -65,7 +65,7 @@ pub enum WalletCommand {
Cancel(Pubkey), Cancel(Pubkey),
Confirm(Signature), Confirm(Signature),
VoteAuthorize(Pubkey, Pubkey, VoteAuthorize), VoteAuthorize(Pubkey, Pubkey, VoteAuthorize),
CreateVoteAccount(Pubkey, VoteInit, u64), CreateVoteAccount(Pubkey, VoteInit),
ShowAccount { ShowAccount {
pubkey: Pubkey, pubkey: Pubkey,
output_file: Option<String>, output_file: Option<String>,
@ -655,11 +655,12 @@ fn process_deploy(
// Build transactions to calculate fees // Build transactions to calculate fees
let mut messages: Vec<&Message> = Vec::new(); let mut messages: Vec<&Message> = Vec::new();
let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; 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( let mut create_account_tx = system_transaction::create_account(
&config.keypair, &config.keypair,
&program_id.pubkey(), &program_id.pubkey(),
blockhash, blockhash,
1, minimum_balance,
program_data.len() as u64, program_data.len() as u64,
&bpf_loader::id(), &bpf_loader::id(),
); );
@ -1087,14 +1088,8 @@ pub fn process_command(config: &WalletConfig) -> ProcessResult {
WalletCommand::Confirm(signature) => process_confirm(&rpc_client, signature), WalletCommand::Confirm(signature) => process_confirm(&rpc_client, signature),
// Create vote account // Create vote account
WalletCommand::CreateVoteAccount(vote_account_pubkey, vote_init, lamports) => { WalletCommand::CreateVoteAccount(vote_account_pubkey, vote_init) => {
process_create_vote_account( process_create_vote_account(&rpc_client, config, &vote_account_pubkey, &vote_init)
&rpc_client,
config,
&vote_account_pubkey,
&vote_init,
*lamports,
)
} }
WalletCommand::VoteAuthorize( 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) .validator(is_pubkey_or_keypair)
.help("Validator that will vote with this account"), .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(
Arg::with_name("commission") Arg::with_name("commission")
.long("commission") .long("commission")
@ -2324,7 +2303,6 @@ mod tests {
authorized_withdrawer: bob_pubkey, authorized_withdrawer: bob_pubkey,
commission: 0, commission: 0,
}, },
10,
); );
let signature = process_command(&config); let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());
@ -2344,7 +2322,7 @@ mod tests {
withdrawer: config.keypair.pubkey(), withdrawer: config.keypair.pubkey(),
}, },
Lockup { slot: 0, custodian }, Lockup { slot: 0, custodian },
10, 1234,
); );
let signature = process_command(&config); let signature = process_command(&config);
assert_eq!(signature.unwrap(), SIGNATURE.to_string()); assert_eq!(signature.unwrap(), SIGNATURE.to_string());
@ -2493,7 +2471,6 @@ mod tests {
authorized_withdrawer: bob_pubkey, authorized_withdrawer: bob_pubkey,
commission: 0, commission: 0,
}, },
10,
); );
assert!(process_command(&config).is_err()); assert!(process_command(&config).is_err());

View File

@ -28,12 +28,19 @@ fn test_wallet_deploy_program() {
let rpc_client = RpcClient::new_socket(leader_data.rpc); 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(); let mut config = WalletConfig::default();
config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port()); config.json_rpc_url = format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
config.command = WalletCommand::Airdrop { config.command = WalletCommand::Airdrop {
drone_host: None, drone_host: None,
drone_port: drone_addr.port(), 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, use_lamports_unit: true,
}; };
process_command(&config).unwrap(); process_command(&config).unwrap();
@ -57,7 +64,7 @@ fn test_wallet_deploy_program() {
let account_info_obj = account_info.as_object().unwrap(); let account_info_obj = account_info.as_object().unwrap();
assert_eq!( assert_eq!(
account_info_obj.get("lamports").unwrap().as_u64().unwrap(), account_info_obj.get("lamports").unwrap().as_u64().unwrap(),
1 minimum_balance_for_rent_exemption
); );
let owner_array = account_info.get("owner").unwrap(); let owner_array = account_info.get("owner").unwrap();
assert_eq!(owner_array, &json!(bpf_loader::id())); assert_eq!(owner_array, &json!(bpf_loader::id()));

View File

@ -62,6 +62,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest {
RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)), RpcRequest::GetTransactionCount => Value::Number(Number::from(1234)),
RpcRequest::GetSlot => Value::Number(Number::from(0)), RpcRequest::GetSlot => Value::Number(Number::from(0)),
RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()), RpcRequest::SendTransaction => Value::String(SIGNATURE.to_string()),
RpcRequest::GetMinimumBalanceForRentExemption => Value::Number(Number::from(1234)),
_ => Value::Null, _ => Value::Null,
}; };
Ok(val) Ok(val)

View File

@ -346,8 +346,12 @@ impl RpcClient {
) )
})?; })?;
let minimum_balance: u64 = let minimum_balance: u64 = serde_json::from_value(minimum_balance_json).map_err(|err| {
serde_json::from_value(minimum_balance_json).expect("deserialize minimum_balance"); io::Error::new(
io::ErrorKind::Other,
format!("GetMinimumBalanceForRentExemption parse failure: {:?}", err),
)
})?;
trace!( trace!(
"Response minimum balance {:?} {:?}", "Response minimum balance {:?} {:?}",
data_len, data_len,

View File

@ -583,6 +583,9 @@ pub fn new_validator_for_tests() -> (Validator, ContactInfo, Keypair, PathBuf) {
.native_instruction_processors .native_instruction_processors
.push(solana_budget_program!()); .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 (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block);
let voting_keypair = Arc::new(Keypair::new()); let voting_keypair = Arc::new(Keypair::new());

View File

@ -282,7 +282,7 @@ setup_validator_accounts() {
fi fi
echo "Creating validator vote account" 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 fi
echo "Validator vote account configured" echo "Validator vote account configured"