Fix offline stakes payer (#7385)

This commit is contained in:
Jack May 2019-12-09 23:11:04 -08:00 committed by GitHub
parent 5e0a69f68b
commit 19ecce1e32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 382 additions and 31 deletions

View File

@ -512,20 +512,20 @@ pub type ProcessResult = Result<String, Box<dyn std::error::Error>>;
pub fn check_account_for_fee(
rpc_client: &RpcClient,
config: &CliConfig,
account_pubkey: &Pubkey,
fee_calculator: &FeeCalculator,
message: &Message,
) -> Result<(), Box<dyn error::Error>> {
check_account_for_multiple_fees(rpc_client, config, fee_calculator, &[message])
check_account_for_multiple_fees(rpc_client, account_pubkey, fee_calculator, &[message])
}
fn check_account_for_multiple_fees(
rpc_client: &RpcClient,
config: &CliConfig,
account_pubkey: &Pubkey,
fee_calculator: &FeeCalculator,
messages: &[&Message],
) -> Result<(), Box<dyn error::Error>> {
let balance = rpc_client.retry_get_balance(&config.keypair.pubkey(), 5)?;
let balance = rpc_client.retry_get_balance(account_pubkey, 5)?;
if let Some(lamports) = balance {
if lamports
>= messages
@ -744,7 +744,12 @@ fn process_deploy(
let mut finalize_tx = Transaction::new(&signers, message, blockhash);
messages.push(&finalize_tx.message);
check_account_for_multiple_fees(rpc_client, config, &fee_calculator, &messages)?;
check_account_for_multiple_fees(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&messages,
)?;
trace!("Creating program account");
let result = rpc_client.send_and_confirm_transaction(&mut create_account_tx, &signers);
@ -804,7 +809,12 @@ fn process_pay(
if sign_only {
return_signers(&tx)
} else {
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<SystemError>(result)
}
@ -838,7 +848,12 @@ fn process_pay(
if sign_only {
return_signers(&tx)
} else {
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &contract_state]);
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
@ -883,7 +898,12 @@ fn process_pay(
} else {
let result = rpc_client
.send_and_confirm_transaction(&mut tx, &[&config.keypair, &contract_state]);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let signature_str = log_instruction_custom_error::<BudgetError>(result)?;
Ok(json!({
@ -905,7 +925,12 @@ fn process_cancel(rpc_client: &RpcClient, config: &CliConfig, pubkey: &Pubkey) -
&config.keypair.pubkey(),
);
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<BudgetError>(result)
}
@ -921,7 +946,12 @@ fn process_time_elapsed(
let ix = budget_instruction::apply_timestamp(&config.keypair.pubkey(), pubkey, to, dt);
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<BudgetError>(result)
}
@ -936,7 +966,12 @@ fn process_witness(
let ix = budget_instruction::apply_signature(&config.keypair.pubkey(), pubkey, to);
let mut tx = Transaction::new_signed_instructions(&[&config.keypair], vec![ix], blockhash);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<BudgetError>(result)
}

View File

@ -419,7 +419,12 @@ pub fn process_ping(
let transaction =
system_transaction::transfer(&config.keypair, &to, lamports, recent_blockhash);
check_account_for_fee(rpc_client, config, &fee_calculator, &transaction.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&transaction.message,
)?;
match rpc_client.send_transaction(&transaction) {
Ok(signature) => {

View File

@ -344,6 +344,7 @@ pub fn parse_stake_delegate_stake(matches: &ArgMatches<'_>) -> Result<CliCommand
let sign_only = matches.is_present("sign_only");
let signers = pubkeys_sigs_of(&matches, "signer");
let blockhash = value_of(matches, "blockhash");
let require_keypair = signers.is_none();
Ok(CliCommandInfo {
command: CliCommand::DelegateStake {
@ -354,7 +355,7 @@ pub fn parse_stake_delegate_stake(matches: &ArgMatches<'_>) -> Result<CliCommand
signers,
blockhash,
},
require_keypair: !sign_only,
require_keypair,
})
}
@ -378,6 +379,7 @@ pub fn parse_stake_authorize(
pub fn parse_redeem_vote_credits(matches: &ArgMatches<'_>) -> Result<CliCommandInfo, CliError> {
let stake_account_pubkey = pubkey_of(matches, "stake_account_pubkey").unwrap();
let vote_account_pubkey = pubkey_of(matches, "vote_account_pubkey").unwrap();
Ok(CliCommandInfo {
command: CliCommand::RedeemVoteCredits(stake_account_pubkey, vote_account_pubkey),
require_keypair: true,
@ -389,6 +391,8 @@ pub fn parse_stake_deactivate_stake(matches: &ArgMatches<'_>) -> Result<CliComma
let sign_only = matches.is_present("sign_only");
let signers = pubkeys_sigs_of(&matches, "signer");
let blockhash = value_of(matches, "blockhash");
let require_keypair = signers.is_none();
Ok(CliCommandInfo {
command: CliCommand::DeactivateStake {
stake_account_pubkey,
@ -396,7 +400,7 @@ pub fn parse_stake_deactivate_stake(matches: &ArgMatches<'_>) -> Result<CliComma
signers,
blockhash,
},
require_keypair: !sign_only,
require_keypair,
})
}
@ -489,7 +493,12 @@ pub fn process_create_stake_account(
&[&config.keypair, stake_account],
recent_blockhash,
);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, stake_account]);
log_instruction_custom_error::<SystemError>(result)
@ -520,7 +529,12 @@ pub fn process_stake_authorize(
&[&config.keypair],
recent_blockhash,
);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<StakeError>(result)
}
@ -551,7 +565,12 @@ pub fn process_deactivate_stake_account(
if sign_only {
return_signers(&tx)
} else {
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&tx.message.account_keys[0],
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<StakeError>(result)
}
@ -579,7 +598,12 @@ pub fn process_withdraw_stake(
&[&config.keypair],
recent_blockhash,
);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<StakeError>(result)
}
@ -601,7 +625,12 @@ pub fn process_redeem_vote_credits(
&[&config.keypair],
recent_blockhash,
);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<StakeError>(result)
}
@ -791,7 +820,12 @@ pub fn process_delegate_stake(
if sign_only {
return_signers(&tx)
} else {
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&tx.message.account_keys[0],
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<StakeError>(result)
}
@ -1008,7 +1042,7 @@ mod tests {
signers: None,
blockhash: None
},
require_keypair: false
require_keypair: true
}
);
@ -1035,7 +1069,7 @@ mod tests {
signers: Some(vec![(key1, sig1)]),
blockhash: None
},
require_keypair: true
require_keypair: false
}
);
@ -1064,7 +1098,7 @@ mod tests {
signers: Some(vec![(key1, sig1), (key2, sig2)]),
blockhash: None
},
require_keypair: true
require_keypair: false
}
);
@ -1143,7 +1177,7 @@ mod tests {
signers: None,
blockhash: None
},
require_keypair: false
require_keypair: true
}
);
@ -1167,7 +1201,7 @@ mod tests {
signers: Some(vec![(key1, sig1)]),
blockhash: None
},
require_keypair: true
require_keypair: false
}
);
@ -1193,7 +1227,7 @@ mod tests {
signers: Some(vec![(key1, sig1), (key2, sig2)]),
blockhash: None
},
require_keypair: true
require_keypair: false
}
);
}

View File

@ -182,7 +182,12 @@ pub fn process_create_storage_account(
ixs,
recent_blockhash,
);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result =
rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, &storage_account]);
log_instruction_custom_error::<SystemError>(result)
@ -202,7 +207,12 @@ pub fn process_claim_storage_reward(
let message = Message::new_with_payer(vec![instruction], Some(&signers[0].pubkey()));
let mut tx = Transaction::new(&signers, message, recent_blockhash);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &signers)?;
Ok(signature_str.to_string())
}

View File

@ -347,7 +347,12 @@ pub fn process_set_validator_info(
// Submit transaction
let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?;
let mut tx = Transaction::new(&signers, message, recent_blockhash);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let signature_str = rpc_client.send_and_confirm_transaction(&mut tx, &signers)?;
println!("Success! Validator info published at: {:?}", info_pubkey);

View File

@ -266,7 +266,12 @@ pub fn process_create_vote_account(
ixs,
recent_blockhash,
);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair, vote_account]);
log_instruction_custom_error::<SystemError>(result)
}
@ -296,7 +301,12 @@ pub fn process_vote_authorize(
&[&config.keypair],
recent_blockhash,
);
check_account_for_fee(rpc_client, config, &fee_calculator, &tx.message)?;
check_account_for_fee(
rpc_client,
&config.keypair.pubkey(),
&fee_calculator,
&tx.message,
)?;
let result = rpc_client.send_and_confirm_transaction(&mut tx, &[&config.keypair]);
log_instruction_custom_error::<VoteError>(result)
}

252
cli/tests/stake.rs Normal file
View File

@ -0,0 +1,252 @@
use serde_json::Value;
use solana_cli::cli::{process_command, request_and_confirm_airdrop, CliCommand, CliConfig};
use solana_client::rpc_client::RpcClient;
use solana_drone::drone::run_local_drone;
use solana_sdk::{
hash::Hash,
pubkey::Pubkey,
signature::{read_keypair_file, write_keypair, KeypairUtil, Signature},
};
use solana_stake_program::stake_state::Lockup;
use std::fs::remove_dir_all;
use std::str::FromStr;
use std::sync::mpsc::channel;
#[cfg(test)]
use solana_core::validator::new_validator_for_tests;
use std::thread::sleep;
use std::time::Duration;
use tempfile::NamedTempFile;
fn make_tmp_file() -> (String, NamedTempFile) {
let tmp_file = NamedTempFile::new().unwrap();
(String::from(tmp_file.path().to_str().unwrap()), tmp_file)
}
fn check_balance(expected_balance: u64, client: &RpcClient, pubkey: &Pubkey) {
(0..5).for_each(|tries| {
let balance = client.retry_get_balance(pubkey, 1).unwrap().unwrap();
if balance == expected_balance {
return;
}
if tries == 4 {
assert_eq!(balance, expected_balance);
}
sleep(Duration::from_millis(500));
});
}
#[test]
fn test_stake_delegation_and_deactivation() {
solana_logger::setup();
let (server, leader_data, alice, ledger_path) = new_validator_for_tests();
let (sender, receiver) = channel();
run_local_drone(alice, sender, None);
let drone_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let mut config_validator = CliConfig::default();
config_validator.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_vote = CliConfig::default();
config_vote.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let (vote_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&config_vote.keypair, tmp_file.as_file_mut()).unwrap();
let mut config_stake = CliConfig::default();
config_stake.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&config_stake.keypair, tmp_file.as_file_mut()).unwrap();
request_and_confirm_airdrop(
&rpc_client,
&drone_addr,
&config_validator.keypair.pubkey(),
100_000,
)
.unwrap();
check_balance(100_000, &rpc_client, &config_validator.keypair.pubkey());
// Create vote account
config_validator.command = CliCommand::CreateVoteAccount {
vote_account: read_keypair_file(&vote_keypair_file).unwrap().into(),
node_pubkey: config_validator.keypair.pubkey(),
authorized_voter: None,
authorized_withdrawer: None,
commission: 0,
};
process_command(&config_validator).unwrap();
// Create stake account
config_validator.command = CliCommand::CreateStakeAccount {
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
staker: None,
withdrawer: None,
lockup: Lockup {
custodian: Pubkey::default(),
epoch: 0,
},
lamports: 50_000,
};
process_command(&config_validator).unwrap();
// Delegate stake
config_validator.command = CliCommand::DelegateStake {
stake_account_pubkey: config_stake.keypair.pubkey(),
vote_account_pubkey: config_vote.keypair.pubkey(),
force: true,
sign_only: false,
signers: None,
blockhash: None,
};
process_command(&config_validator).unwrap();
// Deactivate stake
config_validator.command = CliCommand::DeactivateStake {
stake_account_pubkey: config_stake.keypair.pubkey(),
sign_only: false,
signers: None,
blockhash: None,
};
process_command(&config_validator).unwrap();
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}
#[test]
fn test_stake_delegation_and_deactivation_offline() {
solana_logger::setup();
let (server, leader_data, alice, ledger_path) = new_validator_for_tests();
let (sender, receiver) = channel();
run_local_drone(alice, sender, None);
let drone_addr = receiver.recv().unwrap();
let rpc_client = RpcClient::new_socket(leader_data.rpc);
let mut config_validator = CliConfig::default();
config_validator.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_payer = CliConfig::default();
config_payer.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let mut config_vote = CliConfig::default();
config_vote.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let (vote_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&config_vote.keypair, tmp_file.as_file_mut()).unwrap();
let mut config_stake = CliConfig::default();
config_stake.json_rpc_url =
format!("http://{}:{}", leader_data.rpc.ip(), leader_data.rpc.port());
let (stake_keypair_file, mut tmp_file) = make_tmp_file();
write_keypair(&config_stake.keypair, tmp_file.as_file_mut()).unwrap();
request_and_confirm_airdrop(
&rpc_client,
&drone_addr,
&config_validator.keypair.pubkey(),
100_000,
)
.unwrap();
check_balance(100_000, &rpc_client, &config_validator.keypair.pubkey());
// Create vote account
config_validator.command = CliCommand::CreateVoteAccount {
vote_account: read_keypair_file(&vote_keypair_file).unwrap().into(),
node_pubkey: config_validator.keypair.pubkey(),
authorized_voter: None,
authorized_withdrawer: None,
commission: 0,
};
process_command(&config_validator).unwrap();
// Create stake account
config_validator.command = CliCommand::CreateStakeAccount {
stake_account: read_keypair_file(&stake_keypair_file).unwrap().into(),
staker: None,
withdrawer: None,
lockup: Lockup {
custodian: Pubkey::default(),
epoch: 0,
},
lamports: 50_000,
};
process_command(&config_validator).unwrap();
// Delegate stake offline
config_validator.command = CliCommand::DelegateStake {
stake_account_pubkey: config_stake.keypair.pubkey(),
vote_account_pubkey: config_vote.keypair.pubkey(),
force: true,
sign_only: true,
signers: None,
blockhash: None,
};
let sig_response = process_command(&config_validator).unwrap();
let object: Value = serde_json::from_str(&sig_response).unwrap();
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
let signer_strings = object.get("signers").unwrap().as_array().unwrap();
let signers: Vec<_> = signer_strings
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
// Delegate stake online
config_payer.command = CliCommand::DelegateStake {
stake_account_pubkey: config_stake.keypair.pubkey(),
vote_account_pubkey: config_vote.keypair.pubkey(),
force: true,
sign_only: false,
signers: Some(signers),
blockhash: Some(blockhash_str.parse::<Hash>().unwrap()),
};
process_command(&config_payer).unwrap();
// Deactivate stake offline
config_validator.command = CliCommand::DeactivateStake {
stake_account_pubkey: config_stake.keypair.pubkey(),
sign_only: true,
signers: None,
blockhash: None,
};
let sig_response = process_command(&config_validator).unwrap();
let object: Value = serde_json::from_str(&sig_response).unwrap();
let blockhash_str = object.get("blockhash").unwrap().as_str().unwrap();
let signer_strings = object.get("signers").unwrap().as_array().unwrap();
let signers: Vec<_> = signer_strings
.iter()
.map(|signer_string| {
let mut signer = signer_string.as_str().unwrap().split('=');
let key = Pubkey::from_str(signer.next().unwrap()).unwrap();
let sig = Signature::from_str(signer.next().unwrap()).unwrap();
(key, sig)
})
.collect();
// Deactivate stake online
config_payer.command = CliCommand::DeactivateStake {
stake_account_pubkey: config_stake.keypair.pubkey(),
sign_only: false,
signers: Some(signers),
blockhash: Some(blockhash_str.parse::<Hash>().unwrap()),
};
process_command(&config_payer).unwrap();
server.close().unwrap();
remove_dir_all(ledger_path).unwrap();
}