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:
parent
4fbe36d9c6
commit
2c8c2029d8
@ -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,
|
||||||
|
@ -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
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -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());
|
||||||
|
|
||||||
|
@ -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()));
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
@ -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());
|
||||||
|
@ -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"
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user