send_and_confirm_transaction() no longer needs a keypair (#9950) (#9962)

automerge
This commit is contained in:
mergify[bot]
2020-05-10 10:14:31 -07:00
committed by GitHub
parent b213004157
commit a08235da9a
10 changed files with 300 additions and 76 deletions

View File

@@ -1343,8 +1343,7 @@ fn process_deploy(
)?;
trace!("Creating program account");
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut create_account_tx, &signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&create_account_tx);
log_instruction_custom_error::<SystemError>(result, &config).map_err(|_| {
CliError::DynamicProgramError("Program account allocation failed".to_string())
})?;
@@ -1354,7 +1353,7 @@ fn process_deploy(
trace!("Finalizing program account");
rpc_client
.send_and_confirm_transaction_with_spinner(&mut finalize_tx, &signers)
.send_and_confirm_transaction_with_spinner(&finalize_tx)
.map_err(|e| {
CliError::DynamicProgramError(format!("Program finalize transaction failed: {}", e))
})?;
@@ -1419,8 +1418,7 @@ fn process_pay(
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
}
} else if *witnesses == None {
@@ -1455,10 +1453,7 @@ fn process_pay(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(
&mut tx,
&[config.signers[0], &contract_state],
);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
let signature = log_instruction_custom_error::<BudgetError>(result, &config)?;
Ok(json!({
"signature": signature,
@@ -1494,10 +1489,7 @@ fn process_pay(
return_signers(&tx, &config)
} else {
tx.try_sign(&[config.signers[0], &contract_state], blockhash)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(
&mut tx,
&[config.signers[0], &contract_state],
);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
check_account_for_fee(
rpc_client,
&config.signers[0].pubkey(),
@@ -1532,8 +1524,7 @@ fn process_cancel(rpc_client: &RpcClient, config: &CliConfig, pubkey: &Pubkey) -
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &[config.signers[0]]);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<BudgetError>(result, &config)
}
@@ -1556,8 +1547,7 @@ fn process_time_elapsed(
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &[config.signers[0]]);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<BudgetError>(result, &config)
}
@@ -1619,7 +1609,7 @@ fn process_transfer(
let result = if no_wait {
rpc_client.send_transaction(&tx)
} else {
rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers)
rpc_client.send_and_confirm_transaction_with_spinner(&tx)
};
log_instruction_custom_error::<SystemError>(result, &config)
}
@@ -1643,8 +1633,7 @@ fn process_witness(
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &[config.signers[0]]);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<BudgetError>(result, &config)
}
@@ -2275,8 +2264,8 @@ pub fn request_and_confirm_airdrop(
sleep(Duration::from_secs(1));
}
}?;
let mut tx = keypair.airdrop_transaction();
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &[&keypair]);
let tx = keypair.airdrop_transaction();
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
}

View File

@@ -462,7 +462,7 @@ pub fn process_authorize_nonce_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<NonceError>(result, &config)
}
@@ -539,7 +539,7 @@ pub fn process_create_nonce_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
}
@@ -580,8 +580,7 @@ pub fn process_new_nonce(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client
.send_and_confirm_transaction_with_spinner(&mut tx, &[config.signers[0], nonce_authority]);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
}
@@ -640,7 +639,7 @@ pub fn process_withdraw_from_nonce_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<NonceError>(result, &config)
}

View File

@@ -894,7 +894,7 @@ pub fn process_create_stake_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
}
}
@@ -959,7 +959,7 @@ pub fn process_stake_authorize(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
}
}
@@ -1013,7 +1013,7 @@ pub fn process_deactivate_stake_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
}
}
@@ -1076,7 +1076,7 @@ pub fn process_withdraw_stake(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
}
}
@@ -1210,7 +1210,7 @@ pub fn process_split_stake(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
}
}
@@ -1267,7 +1267,7 @@ pub fn process_stake_set_lockup(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
}
}
@@ -1475,7 +1475,7 @@ pub fn process_delegate_stake(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<StakeError>(result, &config)
}
}

View File

@@ -242,7 +242,7 @@ pub fn process_create_storage_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
}
@@ -266,7 +266,7 @@ pub fn process_claim_storage_reward(
&fee_calculator,
&tx.message,
)?;
let signature = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &signers)?;
let signature = rpc_client.send_and_confirm_transaction_with_spinner(&tx)?;
Ok(signature.to_string())
}

View File

@@ -367,7 +367,7 @@ pub fn process_set_validator_info(
&fee_calculator,
&tx.message,
)?;
let signature_str = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &signers)?;
let signature_str = rpc_client.send_and_confirm_transaction_with_spinner(&tx)?;
println!("Success! Validator info published at: {:?}", info_pubkey);
println!("{}", signature_str);

View File

@@ -457,7 +457,7 @@ pub fn process_create_vote_account(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<SystemError>(result, &config)
}
@@ -497,8 +497,7 @@ pub fn process_vote_authorize(
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &[config.signers[0]]);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, &config)
}
@@ -531,7 +530,7 @@ pub fn process_vote_update_validator(
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction_with_spinner(&mut tx, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx);
log_instruction_custom_error::<VoteError>(result, &config)
}
@@ -636,8 +635,7 @@ pub fn process_withdraw_from_vote_account(
&fee_calculator,
&transaction.message,
)?;
let result =
rpc_client.send_and_confirm_transaction_with_spinner(&mut transaction, &config.signers);
let result = rpc_client.send_and_confirm_transaction_with_spinner(&transaction);
log_instruction_custom_error::<VoteError>(result, &config)
}

View File

@@ -24,7 +24,7 @@ use solana_sdk::{
pubkey::Pubkey,
signature::Signature,
signers::Signers,
transaction::{self, Transaction, TransactionError},
transaction::{self, Transaction},
};
use solana_transaction_status::{
ConfirmedBlock, ConfirmedTransaction, TransactionEncoding, TransactionStatus,
@@ -449,10 +449,9 @@ impl RpcClient {
.map_err(|err| ClientError::new_with_command(err.into(), "MinimumLedgerSlot"))
}
pub fn send_and_confirm_transaction<T: Signers>(
pub fn send_and_confirm_transaction(
&self,
transaction: &mut Transaction,
signer_keys: &T,
transaction: &Transaction,
) -> ClientResult<Signature> {
let mut send_retries = 20;
loop {
@@ -476,11 +475,6 @@ impl RpcClient {
send_retries = if let Some(result) = status.clone() {
match result {
Ok(_) => return Ok(signature),
Err(TransactionError::AccountInUse) => {
// Fetch a new blockhash and re-sign the transaction before sending it again
self.resign_transaction(transaction, signer_keys)?;
send_retries - 1
}
Err(_) => 0,
}
} else {
@@ -491,7 +485,9 @@ impl RpcClient {
return Err(err.unwrap_err().into());
} else {
return Err(
RpcError::ForUser("unable to confirm transaction. This can happen in situations such as transaction expiration and insufficient fee-payer funds".to_string()).into(),
RpcError::ForUser("unable to confirm transaction. \
This can happen in situations such as transaction expiration \
and insufficient fee-payer funds".to_string()).into(),
);
}
}
@@ -1066,10 +1062,9 @@ impl RpcClient {
Ok(confirmations)
}
pub fn send_and_confirm_transaction_with_spinner<T: Signers>(
pub fn send_and_confirm_transaction_with_spinner(
&self,
transaction: &mut Transaction,
signer_keys: &T,
transaction: &Transaction,
) -> ClientResult<Signature> {
let mut confirmations = 0;
@@ -1106,11 +1101,6 @@ impl RpcClient {
send_retries = if let Some(result) = status.clone() {
match result {
Ok(_) => 0,
Err(TransactionError::AccountInUse) => {
// Fetch a new blockhash and re-sign the transaction before sending it again
self.resign_transaction(transaction, signer_keys)?;
send_retries - 1
}
// If transaction errors, return right away; no point in counting confirmations
Err(_) => 0,
}
@@ -1128,9 +1118,13 @@ impl RpcClient {
}
}
} else {
return Err(
RpcError::ForUser("unable to confirm transaction. This can happen in situations such as transaction expiration and insufficient fee-payer funds".to_string()).into(),
);
return Err(RpcError::ForUser(
"unable to confirm transaction. \
This can happen in situations such as transaction \
expiration and insufficient fee-payer funds"
.to_string(),
)
.into());
}
}
};
@@ -1155,7 +1149,9 @@ impl RpcClient {
.unwrap_or(confirmations);
if now.elapsed().as_secs() >= MAX_HASH_AGE_IN_SECONDS as u64 {
return Err(
RpcError::ForUser("transaction not finalized. This can happen when a transaction lands in an abandoned fork. Please retry.".to_string()).into(),
RpcError::ForUser("transaction not finalized. \
This can happen when a transaction lands in an abandoned fork. \
Please retry.".to_string()).into(),
);
}
}
@@ -1358,17 +1354,16 @@ mod tests {
let key = Keypair::new();
let to = Pubkey::new_rand();
let blockhash = Hash::default();
let mut tx = system_transaction::transfer(&key, &to, 50, blockhash);
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&key]);
let tx = system_transaction::transfer(&key, &to, 50, blockhash);
let result = rpc_client.send_and_confirm_transaction(&tx);
result.unwrap();
let rpc_client = RpcClient::new_mock("account_in_use".to_string());
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&key]);
let result = rpc_client.send_and_confirm_transaction(&tx);
assert!(result.is_err());
let rpc_client = RpcClient::new_mock("instruction_error".to_string());
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&key]);
let result = rpc_client.send_and_confirm_transaction(&tx);
assert_matches!(
result.unwrap_err().kind(),
ClientErrorKind::TransactionError(TransactionError::InstructionError(
@@ -1378,7 +1373,7 @@ mod tests {
);
let rpc_client = RpcClient::new_mock("sig_not_found".to_string());
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&key]);
let result = rpc_client.send_and_confirm_transaction(&tx);
if let ClientErrorKind::Io(err) = result.unwrap_err().kind() {
assert_eq!(err.kind(), io::ErrorKind::Other);
}

View File

@@ -221,8 +221,7 @@ fn new_update_manifest(
let mut transaction = Transaction::new_unsigned_instructions(new_account);
let signers = [from_keypair, update_manifest_keypair];
transaction.sign(&signers, recent_blockhash);
rpc_client.send_and_confirm_transaction(&mut transaction, &[from_keypair])?;
rpc_client.send_and_confirm_transaction(&transaction)?;
}
Ok(())
}
@@ -245,8 +244,8 @@ fn store_update_manifest(
);
let message = Message::new_with_payer(&[instruction], Some(&from_keypair.pubkey()));
let mut transaction = Transaction::new(&signers, message, recent_blockhash);
rpc_client.send_and_confirm_transaction(&mut transaction, &[from_keypair])?;
let transaction = Transaction::new(&signers, message, recent_blockhash);
rpc_client.send_and_confirm_transaction(&transaction)?;
Ok(())
}

244
ramp-tps/src/voters.rs Normal file
View File

@@ -0,0 +1,244 @@
use crate::notifier::Notifier;
use crate::utils;
use log::*;
use solana_client::{client_error::Result as ClientResult, rpc_client::RpcClient};
use solana_sdk::{
clock::Slot,
epoch_schedule::EpochSchedule,
native_token::sol_to_lamports,
pubkey::Pubkey,
signature::{Keypair, Signer},
transaction::Transaction,
};
use solana_stake_program::{
stake_instruction,
stake_state::{Authorized as StakeAuthorized, Lockup},
};
use std::{
collections::{HashMap, HashSet},
rc::Rc,
str::FromStr,
thread::sleep,
time::Duration,
};
// The percentage of leader slots that validators complete in order to receive the stake
// reward at the end of a TPS round.
const MIN_LEADER_SLOT_PCT: f64 = 80.0;
#[derive(Default)]
pub struct LeaderRecord {
total_slots: u64,
missed_slots: u64,
}
impl LeaderRecord {
pub fn completed_slot_pct(&self) -> f64 {
if self.total_slots == 0 {
0f64
} else {
let completed_slots = self.total_slots - self.missed_slots;
100f64 * completed_slots as f64 / self.total_slots as f64
}
}
pub fn healthy(&self) -> bool {
self.completed_slot_pct() >= MIN_LEADER_SLOT_PCT
}
}
/// Calculate the leader record for each active validator
pub fn calculate_leader_records(
rpc_client: &RpcClient,
epoch_schedule: &EpochSchedule,
start_slot: Slot,
end_slot: Slot,
notifier: &Notifier,
) -> ClientResult<HashMap<Pubkey, LeaderRecord>> {
let start_epoch = epoch_schedule.get_epoch(start_slot);
let end_epoch = epoch_schedule.get_epoch(end_slot);
let confirmed_blocks: HashSet<_> = rpc_client
.get_confirmed_blocks(start_slot, Some(end_slot))?
.into_iter()
.collect();
let mut leader_records = HashMap::<Pubkey, LeaderRecord>::new();
for epoch in start_epoch..=end_epoch {
let first_slot_in_epoch = epoch_schedule.get_first_slot_in_epoch(epoch);
let start_slot = std::cmp::max(start_slot, first_slot_in_epoch);
let last_slot_in_epoch = epoch_schedule.get_last_slot_in_epoch(epoch);
let end_slot = std::cmp::min(end_slot, last_slot_in_epoch);
rpc_client
.get_leader_schedule(Some(start_slot))?
.unwrap_or_else(|| utils::bail(notifier, "Error: Leader schedule was not found"))
.into_iter()
.map(|(pk, s)| (Pubkey::from_str(&pk).unwrap(), s))
.for_each(|(pubkey, leader_slots)| {
let mut record = leader_records.entry(pubkey).or_default();
for slot_index in leader_slots.iter() {
let slot = (*slot_index as u64) + first_slot_in_epoch;
if slot >= start_slot && slot <= end_slot {
record.total_slots += 1;
if !confirmed_blocks.contains(&slot) {
record.missed_slots += 1;
}
}
}
});
}
Ok(leader_records)
}
pub fn fetch_active_validators(rpc_client: &RpcClient) -> HashMap<Pubkey, Pubkey> {
match rpc_client.get_vote_accounts() {
Err(err) => {
warn!("Failed to get_vote_accounts(): {}", err);
HashMap::new()
}
Ok(vote_accounts) => vote_accounts
.current
.into_iter()
.filter_map(|info| {
if let (Ok(node_pubkey), Ok(vote_pubkey)) = (
Pubkey::from_str(&info.node_pubkey),
Pubkey::from_str(&info.vote_pubkey),
) {
Some((node_pubkey, vote_pubkey))
} else {
None
}
})
.collect(),
}
}
/// Endlessly retry stake delegation until success
fn delegate_stake(
rpc_client: &RpcClient,
faucet_keypair: &Keypair,
vote_account_pubkey: &Pubkey,
sol_gift: u64,
) {
let stake_account_keypair = Keypair::new();
info!(
"delegate_stake: stake pubkey: {}",
stake_account_keypair.pubkey()
);
let mut retry_count = 0;
loop {
let recent_blockhash = loop {
match rpc_client.get_recent_blockhash() {
Ok(response) => break response.0,
Err(err) => {
error!("Failed to get recent blockhash: {}", err);
sleep(Duration::from_secs(5));
}
}
};
let transaction = Transaction::new_signed_instructions(
&[faucet_keypair, &stake_account_keypair],
&stake_instruction::create_account_and_delegate_stake(
&faucet_keypair.pubkey(),
&stake_account_keypair.pubkey(),
&vote_account_pubkey,
&StakeAuthorized::auto(&faucet_keypair.pubkey()),
&Lockup::default(),
sol_to_lamports(sol_gift as f64),
),
recent_blockhash,
);
// Check if stake was delegated but just failed to confirm on an earlier attempt
if retry_count > 0 {
if let Ok(stake_account) = rpc_client.get_account(&stake_account_keypair.pubkey()) {
if stake_account.owner == solana_stake_program::id() {
break;
}
}
}
if let Err(err) = rpc_client.send_and_confirm_transaction(&transaction) {
error!(
"Failed to delegate stake (retries: {}): {}",
retry_count, err
);
retry_count += 1;
sleep(Duration::from_secs(5));
} else {
break;
}
}
}
/// Announce validator status leader slot performance
pub fn announce_results(
starting_validators: &HashMap<Pubkey, Pubkey>,
remaining_validators: &HashMap<Pubkey, Pubkey>,
pubkey_to_keybase: Rc<dyn Fn(&Pubkey) -> String>,
leader_records: &HashMap<Pubkey, LeaderRecord>,
notifier: &mut Notifier,
) {
let buffer_records = |keys: Vec<&Pubkey>, notifier: &mut Notifier| {
if keys.is_empty() {
notifier.buffer("* None".to_string());
return;
}
let mut validators = vec![];
for pubkey in keys {
let name = pubkey_to_keybase(pubkey);
if let Some(record) = leader_records.get(pubkey) {
validators.push(format!(
"* {} ({:.1}% leader efficiency)",
name,
record.completed_slot_pct()
));
}
}
validators.sort();
notifier.buffer_vec(validators);
};
let healthy: Vec<_> = remaining_validators
.keys()
.filter(|k| leader_records.get(k).map(|r| r.healthy()).unwrap_or(false))
.collect();
let unhealthy: Vec<_> = remaining_validators
.keys()
.filter(|k| leader_records.get(k).map(|r| !r.healthy()).unwrap_or(true))
.collect();
let inactive: Vec<_> = starting_validators
.keys()
.filter(|k| !remaining_validators.contains_key(k))
.collect();
notifier.buffer("Healthy Validators:".to_string());
buffer_records(healthy, notifier);
notifier.buffer("Unhealthy Validators:".to_string());
buffer_records(unhealthy, notifier);
notifier.buffer("Inactive Validators:".to_string());
buffer_records(inactive, notifier);
notifier.flush();
}
/// Award stake to the surviving validators by delegating stake to their vote account
pub fn award_stake(
rpc_client: &RpcClient,
faucet_keypair: &Keypair,
voters: Vec<(String, &Pubkey)>,
sol_gift: u64,
notifier: &mut Notifier,
) {
for (node_pubkey, vote_account_pubkey) in voters {
info!("Delegate {} SOL to {}", sol_gift, node_pubkey);
delegate_stake(rpc_client, faucet_keypair, vote_account_pubkey, sol_gift);
notifier.buffer(format!("Delegated {} SOL to {}", sol_gift, node_pubkey));
}
notifier.flush();
}

View File

@@ -162,7 +162,7 @@ fn send_message<S: Signers>(
) -> Result<Signature, ClientError> {
let mut transaction = Transaction::new_unsigned(message);
client.resign_transaction(&mut transaction, signers)?;
client.send_and_confirm_transaction_with_spinner(&mut transaction, signers)
client.send_and_confirm_transaction_with_spinner(&transaction)
}
fn main() -> Result<(), Box<dyn Error>> {