From 0b50bb2b2025b43c27bc755a95fbf114f2655d30 Mon Sep 17 00:00:00 2001 From: Jack May Date: Fri, 13 Aug 2021 09:08:20 -0700 Subject: [PATCH] Deprecate FeeCalculator returning APIs (#19120) --- accounts-cluster-bench/src/main.rs | 45 ++- banks-server/src/banks_server.rs | 1 + bench-tps/src/bench.rs | 40 +- cli/src/checks.rs | 112 ++++-- cli/src/cli.rs | 2 +- cli/src/cluster_query.rs | 12 +- cli/src/feature.rs | 4 +- cli/src/nonce.rs | 24 +- cli/src/program.rs | 29 +- cli/src/spend_utils.rs | 59 +-- cli/src/stake.rs | 42 +- cli/src/validator_info.rs | 6 +- cli/src/vote.rs | 30 +- cli/src/wallet.rs | 5 +- cli/tests/stake.rs | 8 +- cli/tests/transfer.rs | 4 +- client/src/blockhash_query.rs | 65 ++- client/src/mock_sender.rs | 17 +- client/src/rpc_client.rs | 186 +++++++-- client/src/rpc_request.rs | 36 +- client/src/rpc_response.rs | 7 + client/src/thin_client.rs | 55 ++- core/src/test_validator.rs | 46 ++- core/src/vote_simulator.rs | 4 +- core/tests/client.rs | 2 +- core/tests/rpc.rs | 8 +- .../simple-payment-and-state-verification.md | 2 +- install/src/command.rs | 4 +- local-cluster/src/cluster_tests.rs | 12 +- local-cluster/src/local_cluster.rs | 8 +- local-cluster/tests/local_cluster.rs | 12 +- program-test/src/lib.rs | 12 +- programs/bpf/tests/programs.rs | 2 + rpc/src/rpc.rs | 375 ++++++++++++------ rpc/src/rpc_service.rs | 5 +- rpc/src/transaction_status_service.rs | 2 + runtime/benches/bank.rs | 4 +- runtime/src/accounts.rs | 6 +- runtime/src/bank.rs | 47 ++- runtime/src/bank_client.rs | 58 ++- runtime/src/blockhash_queue.rs | 17 +- sdk/program/src/fee_calculator.rs | 8 +- sdk/src/client.rs | 35 ++ stake-accounts/src/main.rs | 3 +- tokens/src/commands.rs | 102 +++-- tokens/src/spl_token.rs | 17 +- watchtower/src/main.rs | 2 +- 47 files changed, 1119 insertions(+), 463 deletions(-) diff --git a/accounts-cluster-bench/src/main.rs b/accounts-cluster-bench/src/main.rs index 75a6942ae..14a673058 100644 --- a/accounts-cluster-bench/src/main.rs +++ b/accounts-cluster-bench/src/main.rs @@ -12,6 +12,7 @@ use solana_measure::measure::Measure; use solana_runtime::inline_spl_token_v2_0; use solana_sdk::{ commitment_config::CommitmentConfig, + instruction::{AccountMeta, Instruction}, message::Message, pubkey::Pubkey, rpc_port::DEFAULT_RPC_PORT, @@ -33,10 +34,6 @@ use std::{ time::{Duration, Instant}, }; -// Create and close messages both require 2 signatures; if transaction construction changes, update -// this magic number -const NUM_SIGNATURES: u64 = 2; - pub fn airdrop_lamports( client: &RpcClient, faucet_addr: &SocketAddr, @@ -55,7 +52,7 @@ pub fn airdrop_lamports( id.pubkey(), ); - let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap(); + let blockhash = client.get_latest_blockhash().unwrap(); match request_airdrop_transaction(faucet_addr, &id.pubkey(), airdrop_amount, blockhash) { Ok(transaction) => { let mut tries = 0; @@ -375,10 +372,10 @@ fn run_accounts_bench( info!("Targeting {}", entrypoint_addr); - let mut last_blockhash = Instant::now(); + let mut latest_blockhash = Instant::now(); let mut last_log = Instant::now(); let mut count = 0; - let mut recent_blockhash = client.get_recent_blockhash().expect("blockhash"); + let mut blockhash = client.get_latest_blockhash().expect("blockhash"); let mut tx_sent_count = 0; let mut total_accounts_created = 0; let mut total_accounts_closed = 0; @@ -406,16 +403,32 @@ fn run_accounts_bench( let executor = TransactionExecutor::new(entrypoint_addr); + // Create and close messages both require 2 signatures, fake a 2 signature message to calculate fees + let message = Message::new( + &[ + Instruction::new_with_bytes( + Pubkey::new_unique(), + &[], + vec![AccountMeta::new(Pubkey::new_unique(), true)], + ), + Instruction::new_with_bytes( + Pubkey::new_unique(), + &[], + vec![AccountMeta::new(Pubkey::new_unique(), true)], + ), + ], + None, + ); + loop { - if last_blockhash.elapsed().as_millis() > 10_000 { - recent_blockhash = client.get_recent_blockhash().expect("blockhash"); - last_blockhash = Instant::now(); + if latest_blockhash.elapsed().as_millis() > 10_000 { + blockhash = client.get_latest_blockhash().expect("blockhash"); + latest_blockhash = Instant::now(); } - let fee = recent_blockhash - .1 - .lamports_per_signature - .saturating_mul(NUM_SIGNATURES); + let fee = client + .get_fee_for_message(&blockhash, &message) + .expect("get_fee_for_message"); let lamports = min_balance + fee; for (i, balance) in balances.iter_mut().enumerate() { @@ -464,7 +477,7 @@ fn run_accounts_bench( mint, ); let signers: Vec<&Keypair> = vec![keypair, &base_keypair]; - Transaction::new(&signers, message, recent_blockhash.0) + Transaction::new(&signers, message, blockhash) }) .collect(); balances[i] = balances[i].saturating_sub(lamports * txs.len() as u64); @@ -496,7 +509,7 @@ fn run_accounts_bench( mint.is_some(), ); let signers: Vec<&Keypair> = vec![payer_keypairs[0], &base_keypair]; - Transaction::new(&signers, message, recent_blockhash.0) + Transaction::new(&signers, message, blockhash) }) .collect(); balances[0] = balances[0].saturating_sub(fee * txs.len() as u64); diff --git a/banks-server/src/banks_server.rs b/banks-server/src/banks_server.rs index 368eeafe6..4dace6464 100644 --- a/banks-server/src/banks_server.rs +++ b/banks-server/src/banks_server.rs @@ -167,6 +167,7 @@ impl Banks for BanksServer { commitment: CommitmentLevel, ) -> (FeeCalculator, Hash, u64) { let bank = self.bank(commitment); + #[allow(deprecated)] let (blockhash, fee_calculator) = bank.last_blockhash_with_fee_calculator(); let last_valid_block_height = bank .get_blockhash_last_valid_block_height(&blockhash) diff --git a/bench-tps/src/bench.rs b/bench-tps/src/bench.rs index d2b22e643..01c16a668 100644 --- a/bench-tps/src/bench.rs +++ b/bench-tps/src/bench.rs @@ -10,8 +10,8 @@ use solana_sdk::{ client::Client, clock::{DEFAULT_S_PER_SLOT, MAX_PROCESSING_AGE}, commitment_config::CommitmentConfig, - fee_calculator::FeeCalculator, hash::Hash, + instruction::{AccountMeta, Instruction}, message::Message, pubkey::Pubkey, signature::{Keypair, Signer}, @@ -45,14 +45,12 @@ pub type Result = std::result::Result; pub type SharedTransactions = Arc>>>; -fn get_recent_blockhash(client: &T) -> (Hash, FeeCalculator) { +fn get_latest_blockhash(client: &T) -> Hash { loop { - match client.get_recent_blockhash_with_commitment(CommitmentConfig::processed()) { - Ok((blockhash, fee_calculator, _last_valid_slot)) => { - return (blockhash, fee_calculator) - } + match client.get_latest_blockhash_with_commitment(CommitmentConfig::processed()) { + Ok((blockhash, _)) => return blockhash, Err(err) => { - info!("Couldn't get recent blockhash: {:?}", err); + info!("Couldn't get last blockhash: {:?}", err); sleep(Duration::from_secs(1)); } }; @@ -239,19 +237,19 @@ where let shared_txs: SharedTransactions = Arc::new(RwLock::new(VecDeque::new())); - let recent_blockhash = Arc::new(RwLock::new(get_recent_blockhash(client.as_ref()).0)); + let blockhash = Arc::new(RwLock::new(get_latest_blockhash(client.as_ref()))); let shared_tx_active_thread_count = Arc::new(AtomicIsize::new(0)); let total_tx_sent_count = Arc::new(AtomicUsize::new(0)); let blockhash_thread = { let exit_signal = exit_signal.clone(); - let recent_blockhash = recent_blockhash.clone(); + let blockhash = blockhash.clone(); let client = client.clone(); let id = id.pubkey(); Builder::new() .name("solana-blockhash-poller".to_string()) .spawn(move || { - poll_blockhash(&exit_signal, &recent_blockhash, &client, &id); + poll_blockhash(&exit_signal, &blockhash, &client, &id); }) .unwrap() }; @@ -271,7 +269,7 @@ where let start = Instant::now(); generate_chunked_transfers( - recent_blockhash, + blockhash, &shared_txs, shared_tx_active_thread_count, source_keypair_chunks, @@ -402,7 +400,7 @@ fn poll_blockhash( loop { let blockhash_updated = { let old_blockhash = *blockhash.read().unwrap(); - if let Ok((new_blockhash, _fee)) = client.get_new_blockhash(&old_blockhash) { + if let Ok(new_blockhash) = client.get_new_latest_blockhash(&old_blockhash) { *blockhash.write().unwrap() = new_blockhash; blockhash_last_updated = Instant::now(); true @@ -540,7 +538,7 @@ impl<'a> FundingTransactions<'a> for Vec<(&'a Keypair, Transaction)> { self.len(), ); - let (blockhash, _fee_calculator) = get_recent_blockhash(client.as_ref()); + let blockhash = get_latest_blockhash(client.as_ref()); // re-sign retained to_fund_txes with updated blockhash self.sign(blockhash); @@ -732,7 +730,7 @@ pub fn airdrop_lamports( id.pubkey(), ); - let (blockhash, _fee_calculator) = get_recent_blockhash(client); + let blockhash = get_latest_blockhash(client); match request_airdrop_transaction(faucet_addr, &id.pubkey(), airdrop_amount, blockhash) { Ok(transaction) => { let mut tries = 0; @@ -890,8 +888,18 @@ pub fn generate_and_fund_keypairs( // pay for the transaction fees in a new run. let enough_lamports = 8 * lamports_per_account / 10; if first_keypair_balance < enough_lamports || last_keypair_balance < enough_lamports { - let fee_rate_governor = client.get_fee_rate_governor().unwrap(); - let max_fee = fee_rate_governor.max_lamports_per_signature; + let single_sig_message = Message::new( + &[Instruction::new_with_bytes( + Pubkey::new_unique(), + &[], + vec![AccountMeta::new(Pubkey::new_unique(), true)], + )], + None, + ); + let blockhash = client.get_latest_blockhash().unwrap(); + let max_fee = client + .get_fee_for_message(&blockhash, &single_sig_message) + .unwrap(); let extra_fees = extra * max_fee; let total_keypairs = keypairs.len() as u64 + 1; // Add one for funding keypair let total = lamports_per_account * total_keypairs + extra_fees; diff --git a/cli/src/checks.rs b/cli/src/checks.rs index 92a365c76..660b9a4b7 100644 --- a/cli/src/checks.rs +++ b/cli/src/checks.rs @@ -4,30 +4,30 @@ use solana_client::{ rpc_client::RpcClient, }; use solana_sdk::{ - commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, message::Message, + commitment_config::CommitmentConfig, hash::Hash, message::Message, native_token::lamports_to_sol, pubkey::Pubkey, }; pub fn check_account_for_fee( rpc_client: &RpcClient, account_pubkey: &Pubkey, - fee_calculator: &FeeCalculator, + blockhash: &Hash, message: &Message, ) -> Result<(), CliError> { - check_account_for_multiple_fees(rpc_client, account_pubkey, fee_calculator, &[message]) + check_account_for_multiple_fees(rpc_client, account_pubkey, blockhash, &[message]) } pub fn check_account_for_fee_with_commitment( rpc_client: &RpcClient, account_pubkey: &Pubkey, - fee_calculator: &FeeCalculator, + blockhash: &Hash, message: &Message, commitment: CommitmentConfig, ) -> Result<(), CliError> { check_account_for_multiple_fees_with_commitment( rpc_client, account_pubkey, - fee_calculator, + blockhash, &[message], commitment, ) @@ -36,13 +36,13 @@ pub fn check_account_for_fee_with_commitment( pub fn check_account_for_multiple_fees( rpc_client: &RpcClient, account_pubkey: &Pubkey, - fee_calculator: &FeeCalculator, + blockhash: &Hash, messages: &[&Message], ) -> Result<(), CliError> { check_account_for_multiple_fees_with_commitment( rpc_client, account_pubkey, - fee_calculator, + blockhash, messages, CommitmentConfig::default(), ) @@ -51,7 +51,7 @@ pub fn check_account_for_multiple_fees( pub fn check_account_for_multiple_fees_with_commitment( rpc_client: &RpcClient, account_pubkey: &Pubkey, - fee_calculator: &FeeCalculator, + blockhash: &Hash, messages: &[&Message], commitment: CommitmentConfig, ) -> Result<(), CliError> { @@ -59,7 +59,7 @@ pub fn check_account_for_multiple_fees_with_commitment( rpc_client, account_pubkey, 0, - fee_calculator, + blockhash, messages, commitment, ) @@ -69,11 +69,11 @@ pub fn check_account_for_spend_multiple_fees_with_commitment( rpc_client: &RpcClient, account_pubkey: &Pubkey, balance: u64, - fee_calculator: &FeeCalculator, + blockhash: &Hash, messages: &[&Message], commitment: CommitmentConfig, ) -> Result<(), CliError> { - let fee = calculate_fee(fee_calculator, messages); + let fee = get_fee_for_message(rpc_client, blockhash, messages)?; if !check_account_for_balance_with_commitment( rpc_client, account_pubkey, @@ -98,11 +98,17 @@ pub fn check_account_for_spend_multiple_fees_with_commitment( Ok(()) } -pub fn calculate_fee(fee_calculator: &FeeCalculator, messages: &[&Message]) -> u64 { - messages +pub fn get_fee_for_message( + rpc_client: &RpcClient, + blockhash: &Hash, + messages: &[&Message], +) -> Result { + Ok(messages .iter() - .map(|message| fee_calculator.calculate_fee(message)) - .sum() + .map(|message| rpc_client.get_fee_for_message(blockhash, message)) + .collect::, _>>()? + .iter() + .sum()) } pub fn check_account_for_balance( @@ -166,7 +172,6 @@ mod tests { value: json!(account_balance), }); let pubkey = solana_sdk::pubkey::new_rand(); - let fee_calculator = FeeCalculator::new(1); let pubkey0 = Pubkey::new(&[0; 32]); let pubkey1 = Pubkey::new(&[1; 32]); @@ -180,21 +185,32 @@ mod tests { let mut mocks = HashMap::new(); mocks.insert(RpcRequest::GetBalance, account_balance_response.clone()); let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); - check_account_for_fee(&rpc_client, &pubkey, &fee_calculator, &message0) + let blockhash = rpc_client.get_latest_blockhash().unwrap(); + check_account_for_fee(&rpc_client, &pubkey, &blockhash, &message0) .expect("unexpected result"); + let check_fee_response = json!(Response { + context: RpcResponseContext { slot: 1 }, + value: json!(2), + }); let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::GetFeeForMessage, check_fee_response); mocks.insert(RpcRequest::GetBalance, account_balance_response.clone()); let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); - assert!(check_account_for_fee(&rpc_client, &pubkey, &fee_calculator, &message1).is_err()); + assert!(check_account_for_fee(&rpc_client, &pubkey, &blockhash, &message1).is_err()); + let check_fee_response = json!(Response { + context: RpcResponseContext { slot: 1 }, + value: json!(2), + }); let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::GetFeeForMessage, check_fee_response); mocks.insert(RpcRequest::GetBalance, account_balance_response); let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); assert!(check_account_for_multiple_fees( &rpc_client, &pubkey, - &fee_calculator, + &blockhash, &[&message0, &message0] ) .is_err()); @@ -204,18 +220,18 @@ mod tests { context: RpcResponseContext { slot: 1 }, value: json!(account_balance), }); + let check_fee_response = json!(Response { + context: RpcResponseContext { slot: 1 }, + value: json!(1), + }); let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::GetFeeForMessage, check_fee_response); mocks.insert(RpcRequest::GetBalance, account_balance_response); let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); - check_account_for_multiple_fees( - &rpc_client, - &pubkey, - &fee_calculator, - &[&message0, &message0], - ) - .expect("unexpected result"); + check_account_for_multiple_fees(&rpc_client, &pubkey, &blockhash, &[&message0, &message0]) + .expect("unexpected result"); } #[test] @@ -237,27 +253,45 @@ mod tests { } #[test] - fn test_calculate_fee() { - let fee_calculator = FeeCalculator::new(1); - // No messages, no fee. - assert_eq!(calculate_fee(&fee_calculator, &[]), 0); + fn test_get_fee_for_message() { + let check_fee_response = json!(Response { + context: RpcResponseContext { slot: 1 }, + value: json!(1), + }); + let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::GetFeeForMessage, check_fee_response); + let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); + let blockhash = rpc_client.get_latest_blockhash().unwrap(); - // No signatures, no fee. - let message = Message::default(); - assert_eq!(calculate_fee(&fee_calculator, &[&message, &message]), 0); + // No messages, no fee. + assert_eq!( + get_fee_for_message(&rpc_client, &blockhash, &[]).unwrap(), + 0 + ); // One message w/ one signature, a fee. let pubkey0 = Pubkey::new(&[0; 32]); let pubkey1 = Pubkey::new(&[1; 32]); let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1); let message0 = Message::new(&[ix0], Some(&pubkey0)); - assert_eq!(calculate_fee(&fee_calculator, &[&message0]), 1); + assert_eq!( + get_fee_for_message(&rpc_client, &blockhash, &[&message0]).unwrap(), + 1 + ); - // Two messages, additive fees. - let ix0 = system_instruction::transfer(&pubkey0, &pubkey1, 1); - let ix1 = system_instruction::transfer(&pubkey1, &pubkey0, 1); - let message1 = Message::new(&[ix0, ix1], Some(&pubkey0)); - assert_eq!(calculate_fee(&fee_calculator, &[&message0, &message1]), 3); + // No signatures, no fee. + let check_fee_response = json!(Response { + context: RpcResponseContext { slot: 1 }, + value: json!(0), + }); + let mut mocks = HashMap::new(); + mocks.insert(RpcRequest::GetFeeForMessage, check_fee_response); + let rpc_client = RpcClient::new_mock_with_mocks("".to_string(), mocks); + let message = Message::default(); + assert_eq!( + get_fee_for_message(&rpc_client, &blockhash, &[&message, &message]).unwrap(), + 0 + ); } #[test] diff --git a/cli/src/cli.rs b/cli/src/cli.rs index 8a0b58928..3f32fbafd 100644 --- a/cli/src/cli.rs +++ b/cli/src/cli.rs @@ -1523,7 +1523,7 @@ pub fn request_and_confirm_airdrop( to_pubkey: &Pubkey, lamports: u64, ) -> ClientResult { - let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?; + let recent_blockhash = rpc_client.get_latest_blockhash()?; let signature = rpc_client.request_airdrop_with_blockhash(to_pubkey, lamports, &recent_blockhash)?; rpc_client.confirm_transaction_with_spinner( diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index 59efb36da..375f64d04 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -142,9 +142,10 @@ impl ClusterQuerySubCommands for App<'_, '_> { SubCommand::with_name("cluster-version") .about("Get the version of the cluster entrypoint"), ) + // Deprecated in v1.8.0 .subcommand( SubCommand::with_name("fees") - .about("Display current cluster fees") + .about("Display current cluster fees (Deprecated in v1.8.0)") .arg( Arg::with_name("blockhash") .long("blockhash") @@ -950,6 +951,7 @@ pub fn process_fees( blockhash: Option<&Hash>, ) -> ProcessResult { let fees = if let Some(recent_blockhash) = blockhash { + #[allow(deprecated)] let result = rpc_client.get_fee_calculator_for_blockhash_with_commitment( recent_blockhash, config.commitment, @@ -966,6 +968,7 @@ pub fn process_fees( CliFees::none() } } else { + #[allow(deprecated)] let result = rpc_client.get_fees_with_commitment(config.commitment)?; CliFees::some( result.context.slot, @@ -1374,7 +1377,7 @@ pub fn process_ping( let mut confirmed_count = 0; let mut confirmation_time: VecDeque = VecDeque::with_capacity(1024); - let (mut blockhash, mut fee_calculator) = rpc_client.get_recent_blockhash()?; + let mut blockhash = rpc_client.get_latest_blockhash()?; let mut blockhash_transaction_count = 0; let mut blockhash_acquired = Instant::now(); if let Some(fixed_blockhash) = fixed_blockhash { @@ -1393,9 +1396,8 @@ pub fn process_ping( let now = Instant::now(); if fixed_blockhash.is_none() && now.duration_since(blockhash_acquired).as_secs() > 60 { // Fetch a new blockhash every minute - let (new_blockhash, new_fee_calculator) = rpc_client.get_new_blockhash(&blockhash)?; + let new_blockhash = rpc_client.get_new_latest_blockhash(&blockhash)?; blockhash = new_blockhash; - fee_calculator = new_fee_calculator; blockhash_transaction_count = 0; blockhash_acquired = Instant::now(); } @@ -1414,7 +1416,7 @@ pub fn process_ping( rpc_client, false, SpendAmount::Some(lamports), - &fee_calculator, + &blockhash, &config.signers[0].pubkey(), build_message, config.commitment, diff --git a/cli/src/feature.rs b/cli/src/feature.rs index 5e64b338d..1950211fe 100644 --- a/cli/src/feature.rs +++ b/cli/src/feature.rs @@ -409,12 +409,12 @@ fn process_activate( let rent = rpc_client.get_minimum_balance_for_rent_exemption(Feature::size_of())?; - let (blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let blockhash = rpc_client.get_latest_blockhash()?; let (message, _) = resolve_spend_tx_and_check_account_balance( rpc_client, false, SpendAmount::Some(rent), - &fee_calculator, + &blockhash, &config.signers[0].pubkey(), |lamports| { Message::new( diff --git a/cli/src/nonce.rs b/cli/src/nonce.rs index 90e53be79..80aff5004 100644 --- a/cli/src/nonce.rs +++ b/cli/src/nonce.rs @@ -351,7 +351,7 @@ pub fn process_authorize_nonce_account( memo: Option<&String>, new_authority: &Pubkey, ) -> ProcessResult { - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let nonce_authority = config.signers[nonce_authority]; let ixs = vec![authorize_nonce_account( @@ -362,12 +362,12 @@ pub fn process_authorize_nonce_account( .with_memo(memo); let message = Message::new(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, recent_blockhash)?; + tx.try_sign(&config.signers, latest_blockhash)?; check_account_for_fee_with_commitment( rpc_client, &config.signers[0].pubkey(), - &fee_calculator, + &latest_blockhash, &tx.message, config.commitment, )?; @@ -434,13 +434,13 @@ pub fn process_create_nonce_account( Message::new(&ixs, Some(&config.signers[0].pubkey())) }; - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let (message, lamports) = resolve_spend_tx_and_check_account_balance( rpc_client, false, amount, - &fee_calculator, + &latest_blockhash, &config.signers[0].pubkey(), build_message, config.commitment, @@ -468,7 +468,7 @@ pub fn process_create_nonce_account( } let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, recent_blockhash)?; + tx.try_sign(&config.signers, latest_blockhash)?; let merge_errors = get_feature_is_active(rpc_client, &merge_nonce_error_into_system_error::id())?; let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx); @@ -544,14 +544,14 @@ pub fn process_new_nonce( &nonce_authority.pubkey(), )] .with_memo(memo); - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let message = Message::new(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, recent_blockhash)?; + tx.try_sign(&config.signers, latest_blockhash)?; check_account_for_fee_with_commitment( rpc_client, &config.signers[0].pubkey(), - &fee_calculator, + &latest_blockhash, &tx.message, config.commitment, )?; @@ -611,7 +611,7 @@ pub fn process_withdraw_from_nonce_account( destination_account_pubkey: &Pubkey, lamports: u64, ) -> ProcessResult { - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let nonce_authority = config.signers[nonce_authority]; let ixs = vec![withdraw_nonce_account( @@ -623,11 +623,11 @@ pub fn process_withdraw_from_nonce_account( .with_memo(memo); let message = Message::new(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, recent_blockhash)?; + tx.try_sign(&config.signers, latest_blockhash)?; check_account_for_fee_with_commitment( rpc_client, &config.signers[0].pubkey(), - &fee_calculator, + &latest_blockhash, &tx.message, config.commitment, )?; diff --git a/cli/src/program.rs b/cli/src/program.rs index d467c0b4c..3518ce48b 100644 --- a/cli/src/program.rs +++ b/cli/src/program.rs @@ -23,7 +23,6 @@ use solana_client::{ rpc_config::{RpcAccountInfoConfig, RpcProgramAccountsConfig}, rpc_filter::{Memcmp, MemcmpEncodedBytes, RpcFilterType}, rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS, - rpc_response::Fees, tpu_client::{TpuClient, TpuClientConfig}, }; use solana_rbpf::{ @@ -1067,7 +1066,7 @@ fn process_set_authority( }; trace!("Set a new authority"); - let (blockhash, _) = rpc_client.get_recent_blockhash()?; + let blockhash = rpc_client.get_latest_blockhash()?; let mut tx = if let Some(ref pubkey) = program_pubkey { Transaction::new_unsigned(Message::new( @@ -1343,7 +1342,7 @@ fn close( recipient_pubkey: &Pubkey, authority_signer: &dyn Signer, ) -> Result<(), Box> { - let (blockhash, _) = rpc_client.get_recent_blockhash()?; + let blockhash = rpc_client.get_latest_blockhash()?; let mut tx = Transaction::new_unsigned(Message::new( &[bpf_loader_upgradeable::close( @@ -1891,14 +1890,14 @@ fn check_payer( balance_needed: u64, messages: &[&Message], ) -> Result<(), Box> { - let (_, fee_calculator) = rpc_client.get_recent_blockhash()?; + let blockhash = rpc_client.get_latest_blockhash()?; // Does the payer have enough? check_account_for_spend_multiple_fees_with_commitment( rpc_client, &config.signers[0].pubkey(), balance_needed, - &fee_calculator, + &blockhash, messages, config.commitment, )?; @@ -1920,7 +1919,7 @@ fn send_deploy_messages( if let Some(message) = initial_message { if let Some(initial_signer) = initial_signer { trace!("Preparing the required accounts"); - let (blockhash, _) = rpc_client.get_recent_blockhash()?; + let blockhash = rpc_client.get_latest_blockhash()?; let mut initial_transaction = Transaction::new_unsigned(message.clone()); // Most of the initial_transaction combinations require both the fee-payer and new program @@ -1943,13 +1942,8 @@ fn send_deploy_messages( if let Some(write_messages) = write_messages { if let Some(write_signer) = write_signer { trace!("Writing program data"); - let Fees { - blockhash, - last_valid_block_height, - .. - } = rpc_client - .get_fees_with_commitment(config.commitment)? - .value; + let (blockhash, last_valid_block_height) = + rpc_client.get_latest_blockhash_with_commitment(config.commitment)?; let mut write_transactions = vec![]; for message in write_messages.iter() { let mut tx = Transaction::new_unsigned(message.clone()); @@ -1972,7 +1966,7 @@ fn send_deploy_messages( if let Some(message) = final_message { if let Some(final_signers) = final_signers { trace!("Deploying program"); - let (blockhash, _) = rpc_client.get_recent_blockhash()?; + let blockhash = rpc_client.get_latest_blockhash()?; let mut final_tx = Transaction::new_unsigned(message.clone()); let mut signers = final_signers.to_vec(); @@ -2141,11 +2135,8 @@ fn send_and_confirm_transactions_with_spinner( send_retries -= 1; // Re-sign any failed transactions with a new blockhash and retry - let Fees { - blockhash, - last_valid_block_height: new_last_valid_block_height, - .. - } = rpc_client.get_fees_with_commitment(commitment)?.value; + let (blockhash, new_last_valid_block_height) = + rpc_client.get_latest_blockhash_with_commitment(commitment)?; last_valid_block_height = new_last_valid_block_height; transactions = vec![]; for (_, mut transaction) in pending_transactions.into_iter() { diff --git a/cli/src/spend_utils.rs b/cli/src/spend_utils.rs index df785e457..f525b803f 100644 --- a/cli/src/spend_utils.rs +++ b/cli/src/spend_utils.rs @@ -1,12 +1,12 @@ use crate::{ - checks::{calculate_fee, check_account_for_balance_with_commitment}, + checks::{check_account_for_balance_with_commitment, get_fee_for_message}, cli::CliError, }; use clap::ArgMatches; use solana_clap_utils::{input_parsers::lamports_of_sol, offline::SIGN_ONLY_ARG}; use solana_client::rpc_client::RpcClient; use solana_sdk::{ - commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, message::Message, + commitment_config::CommitmentConfig, hash::Hash, message::Message, native_token::lamports_to_sol, pubkey::Pubkey, }; @@ -47,7 +47,7 @@ pub fn resolve_spend_tx_and_check_account_balance( rpc_client: &RpcClient, sign_only: bool, amount: SpendAmount, - fee_calculator: &FeeCalculator, + blockhash: &Hash, from_pubkey: &Pubkey, build_message: F, commitment: CommitmentConfig, @@ -59,7 +59,7 @@ where rpc_client, sign_only, amount, - fee_calculator, + blockhash, from_pubkey, from_pubkey, build_message, @@ -71,7 +71,7 @@ pub fn resolve_spend_tx_and_check_account_balances( rpc_client: &RpcClient, sign_only: bool, amount: SpendAmount, - fee_calculator: &FeeCalculator, + blockhash: &Hash, from_pubkey: &Pubkey, fee_pubkey: &Pubkey, build_message: F, @@ -82,26 +82,28 @@ where { if sign_only { let (message, SpendAndFee { spend, fee: _ }) = resolve_spend_message( + rpc_client, amount, - fee_calculator, + None, 0, from_pubkey, fee_pubkey, build_message, - ); + )?; Ok((message, spend)) } else { let from_balance = rpc_client .get_balance_with_commitment(from_pubkey, commitment)? .value; let (message, SpendAndFee { spend, fee }) = resolve_spend_message( + rpc_client, amount, - fee_calculator, + Some(blockhash), from_balance, from_pubkey, fee_pubkey, build_message, - ); + )?; if from_pubkey == fee_pubkey { if from_balance == 0 || from_balance < spend + fee { return Err(CliError::InsufficientFundsForSpendAndFee( @@ -130,43 +132,46 @@ where } fn resolve_spend_message( + rpc_client: &RpcClient, amount: SpendAmount, - fee_calculator: &FeeCalculator, + blockhash: Option<&Hash>, from_balance: u64, from_pubkey: &Pubkey, fee_pubkey: &Pubkey, build_message: F, -) -> (Message, SpendAndFee) +) -> Result<(Message, SpendAndFee), CliError> where F: Fn(u64) -> Message, { - match amount { - SpendAmount::Some(lamports) => { - let message = build_message(lamports); - let fee = calculate_fee(fee_calculator, &[&message]); - ( - message, - SpendAndFee { - spend: lamports, - fee, - }, - ) - } - SpendAmount::All => { + let fee = match blockhash { + Some(blockhash) => { let dummy_message = build_message(0); - let fee = calculate_fee(fee_calculator, &[&dummy_message]); + get_fee_for_message(rpc_client, blockhash, &[&dummy_message])? + } + None => 0, // Offline, cannot calulate fee + }; + + match amount { + SpendAmount::Some(lamports) => Ok(( + build_message(lamports), + SpendAndFee { + spend: lamports, + fee, + }, + )), + SpendAmount::All => { let lamports = if from_pubkey == fee_pubkey { from_balance.saturating_sub(fee) } else { from_balance }; - ( + Ok(( build_message(lamports), SpendAndFee { spend: lamports, fee, }, - ) + )) } } } diff --git a/cli/src/stake.rs b/cli/src/stake.rs index 04e8d1d01..63aff4a77 100644 --- a/cli/src/stake.rs +++ b/cli/src/stake.rs @@ -1272,14 +1272,13 @@ pub fn process_create_stake_account( } }; - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; let (message, lamports) = resolve_spend_tx_and_check_account_balances( rpc_client, sign_only, amount, - &fee_calculator, + &recent_blockhash, &from.pubkey(), &fee_payer.pubkey(), build_message, @@ -1387,8 +1386,7 @@ pub fn process_stake_authorize( } ixs = ixs.with_memo(memo); - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; let nonce_authority = config.signers[nonce_authority]; let fee_payer = config.signers[fee_payer]; @@ -1427,7 +1425,7 @@ pub fn process_stake_authorize( check_account_for_fee_with_commitment( rpc_client, &tx.message.account_keys[0], - &fee_calculator, + &recent_blockhash, &tx.message, config.commitment, )?; @@ -1455,8 +1453,7 @@ pub fn process_deactivate_stake_account( seed: Option<&String>, fee_payer: SignerIndex, ) -> ProcessResult { - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; let stake_authority = config.signers[stake_authority]; let stake_account_address = if let Some(seed) = seed { @@ -1507,7 +1504,7 @@ pub fn process_deactivate_stake_account( check_account_for_fee_with_commitment( rpc_client, &tx.message.account_keys[0], - &fee_calculator, + &recent_blockhash, &tx.message, config.commitment, )?; @@ -1543,8 +1540,7 @@ pub fn process_withdraw_stake( *stake_account_pubkey }; - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; let fee_payer = config.signers[fee_payer]; let nonce_authority = config.signers[nonce_authority]; @@ -1575,7 +1571,7 @@ pub fn process_withdraw_stake( rpc_client, sign_only, amount, - &fee_calculator, + &recent_blockhash, &stake_account_address, &fee_payer.pubkey(), build_message, @@ -1606,7 +1602,7 @@ pub fn process_withdraw_stake( check_account_for_fee_with_commitment( rpc_client, &tx.message.account_keys[0], - &fee_calculator, + &recent_blockhash, &tx.message, config.commitment, )?; @@ -1692,8 +1688,7 @@ pub fn process_split_stake( } } - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; let ixs = if let Some(seed) = split_stake_account_seed { stake_instruction::split_with_seed( @@ -1751,7 +1746,7 @@ pub fn process_split_stake( check_account_for_fee_with_commitment( rpc_client, &tx.message.account_keys[0], - &fee_calculator, + &recent_blockhash, &tx.message, config.commitment, )?; @@ -1812,8 +1807,7 @@ pub fn process_merge_stake( } } - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; let ixs = stake_instruction::merge( stake_account_pubkey, @@ -1858,7 +1852,7 @@ pub fn process_merge_stake( check_account_for_fee_with_commitment( rpc_client, &tx.message.account_keys[0], - &fee_calculator, + &recent_blockhash, &tx.message, config.commitment, )?; @@ -1887,8 +1881,7 @@ pub fn process_stake_set_lockup( memo: Option<&String>, fee_payer: SignerIndex, ) -> ProcessResult { - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; let custodian = config.signers[custodian]; let ixs = vec![if new_custodian_signer.is_some() { @@ -1934,7 +1927,7 @@ pub fn process_stake_set_lockup( check_account_for_fee_with_commitment( rpc_client, &tx.message.account_keys[0], - &fee_calculator, + &recent_blockhash, &tx.message, config.commitment, )?; @@ -2291,8 +2284,7 @@ pub fn process_delegate_stake( } } - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; let ixs = vec![stake_instruction::delegate_stake( stake_account_pubkey, @@ -2337,7 +2329,7 @@ pub fn process_delegate_stake( check_account_for_fee_with_commitment( rpc_client, &tx.message.account_keys[0], - &fee_calculator, + &recent_blockhash, &tx.message, config.commitment, )?; diff --git a/cli/src/validator_info.rs b/cli/src/validator_info.rs index 61054a3e9..227c149eb 100644 --- a/cli/src/validator_info.rs +++ b/cli/src/validator_info.rs @@ -345,18 +345,18 @@ pub fn process_set_validator_info( }; // Submit transaction - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let (message, _) = resolve_spend_tx_and_check_account_balance( rpc_client, false, SpendAmount::Some(lamports), - &fee_calculator, + &latest_blockhash, &config.signers[0].pubkey(), build_message, config.commitment, )?; let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&signers, recent_blockhash)?; + tx.try_sign(&signers, latest_blockhash)?; let signature_str = rpc_client.send_and_confirm_transaction_with_spinner(&tx)?; println!("Success! Validator info published at: {:?}", info_pubkey); diff --git a/cli/src/vote.rs b/cli/src/vote.rs index ee81929d0..20fe1e552 100644 --- a/cli/src/vote.rs +++ b/cli/src/vote.rs @@ -605,19 +605,19 @@ pub fn process_create_vote_account( } } - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let (message, _) = resolve_spend_tx_and_check_account_balance( rpc_client, false, amount, - &fee_calculator, + &latest_blockhash, &config.signers[0].pubkey(), build_message, config.commitment, )?; let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, recent_blockhash)?; + tx.try_sign(&config.signers, latest_blockhash)?; let result = rpc_client.send_and_confirm_transaction_with_spinner(&tx); log_instruction_custom_error::(result, config) } @@ -639,7 +639,7 @@ pub fn process_vote_authorize( (&authorized.pubkey(), "authorized_account".to_string()), (new_authorized_pubkey, "new_authorized_pubkey".to_string()), )?; - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let vote_ix = if new_authorized_signer.is_some() { vote_instruction::authorize_checked( vote_account_pubkey, // vote account to update @@ -659,11 +659,11 @@ pub fn process_vote_authorize( let message = Message::new(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, recent_blockhash)?; + tx.try_sign(&config.signers, latest_blockhash)?; check_account_for_fee_with_commitment( rpc_client, &config.signers[0].pubkey(), - &fee_calculator, + &latest_blockhash, &tx.message, config.commitment, )?; @@ -686,7 +686,7 @@ pub fn process_vote_update_validator( (vote_account_pubkey, "vote_account_pubkey".to_string()), (&new_identity_pubkey, "new_identity_account".to_string()), )?; - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let ixs = vec![vote_instruction::update_validator_identity( vote_account_pubkey, &authorized_withdrawer.pubkey(), @@ -696,11 +696,11 @@ pub fn process_vote_update_validator( let message = Message::new(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, recent_blockhash)?; + tx.try_sign(&config.signers, latest_blockhash)?; check_account_for_fee_with_commitment( rpc_client, &config.signers[0].pubkey(), - &fee_calculator, + &latest_blockhash, &tx.message, config.commitment, )?; @@ -717,7 +717,7 @@ pub fn process_vote_update_commission( memo: Option<&String>, ) -> ProcessResult { let authorized_withdrawer = config.signers[withdraw_authority]; - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let ixs = vec![vote_instruction::update_commission( vote_account_pubkey, &authorized_withdrawer.pubkey(), @@ -727,11 +727,11 @@ pub fn process_vote_update_commission( let message = Message::new(&ixs, Some(&config.signers[0].pubkey())); let mut tx = Transaction::new_unsigned(message); - tx.try_sign(&config.signers, recent_blockhash)?; + tx.try_sign(&config.signers, latest_blockhash)?; check_account_for_fee_with_commitment( rpc_client, &config.signers[0].pubkey(), - &fee_calculator, + &latest_blockhash, &tx.message, config.commitment, )?; @@ -836,7 +836,7 @@ pub fn process_withdraw_from_vote_account( destination_account_pubkey: &Pubkey, memo: Option<&String>, ) -> ProcessResult { - let (recent_blockhash, fee_calculator) = rpc_client.get_recent_blockhash()?; + let latest_blockhash = rpc_client.get_latest_blockhash()?; let withdraw_authority = config.signers[withdraw_authority]; let current_balance = rpc_client.get_balance(vote_account_pubkey)?; @@ -865,11 +865,11 @@ pub fn process_withdraw_from_vote_account( let message = Message::new(&ixs, Some(&config.signers[0].pubkey())); let mut transaction = Transaction::new_unsigned(message); - transaction.try_sign(&config.signers, recent_blockhash)?; + transaction.try_sign(&config.signers, latest_blockhash)?; check_account_for_fee_with_commitment( rpc_client, &config.signers[0].pubkey(), - &fee_calculator, + &latest_blockhash, &transaction.message, config.commitment, )?; diff --git a/cli/src/wallet.rs b/cli/src/wallet.rs index f37556258..baa9f44bf 100644 --- a/cli/src/wallet.rs +++ b/cli/src/wallet.rs @@ -645,8 +645,7 @@ pub fn process_transfer( let from = config.signers[from]; let mut from_pubkey = from.pubkey(); - let (recent_blockhash, fee_calculator) = - blockhash_query.get_blockhash_and_fee_calculator(rpc_client, config.commitment)?; + let recent_blockhash = blockhash_query.get_blockhash(rpc_client, config.commitment)?; if !sign_only && !allow_unfunded_recipient { let recipient_balance = rpc_client @@ -706,7 +705,7 @@ pub fn process_transfer( rpc_client, sign_only, amount, - &fee_calculator, + &recent_blockhash, &from_pubkey, &fee_payer.pubkey(), build_message, diff --git a/cli/tests/stake.rs b/cli/tests/stake.rs index 5e41030a5..d42a349bd 100644 --- a/cli/tests/stake.rs +++ b/cli/tests/stake.rs @@ -356,7 +356,7 @@ fn test_offline_stake_delegation_and_deactivation() { process_command(&config_validator).unwrap(); // Delegate stake offline - let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); + let blockhash = rpc_client.get_latest_blockhash().unwrap(); config_offline.command = CliCommand::DelegateStake { stake_account_pubkey: stake_keypair.pubkey(), vote_account_pubkey: test_validator.vote_account_address(), @@ -394,7 +394,7 @@ fn test_offline_stake_delegation_and_deactivation() { process_command(&config_payer).unwrap(); // Deactivate stake offline - let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); + let blockhash = rpc_client.get_latest_blockhash().unwrap(); config_offline.command = CliCommand::DeactivateStake { stake_account_pubkey: stake_keypair.pubkey(), stake_authority: 0, @@ -714,7 +714,7 @@ fn test_stake_authorize() { // Offline assignment of new nonced stake authority let nonced_authority = Keypair::new(); let nonced_authority_pubkey = nonced_authority.pubkey(); - let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); + let blockhash = rpc_client.get_latest_blockhash().unwrap(); config_offline.command = CliCommand::StakeAuthorize { stake_account_pubkey, new_authorizations: vec![StakeAuthorizationIndexed { @@ -964,7 +964,7 @@ fn test_stake_authorize_with_fee_payer() { check_recent_balance(100_000 - SIG_FEE - SIG_FEE, &rpc_client, &payer_pubkey); // Assign authority with offline fee payer - let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); + let blockhash = rpc_client.get_latest_blockhash().unwrap(); config_offline.command = CliCommand::StakeAuthorize { stake_account_pubkey, new_authorizations: vec![StakeAuthorizationIndexed { diff --git a/cli/tests/transfer.rs b/cli/tests/transfer.rs index ad4578461..59ffca3e4 100644 --- a/cli/tests/transfer.rs +++ b/cli/tests/transfer.rs @@ -106,7 +106,7 @@ fn test_transfer() { check_recent_balance(50, &rpc_client, &offline_pubkey); // Offline transfer - let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); + let blockhash = rpc_client.get_latest_blockhash().unwrap(); offline.command = CliCommand::Transfer { amount: SpendAmount::Some(10), to: recipient_pubkey, @@ -318,7 +318,7 @@ fn test_transfer_multisession_signing() { check_ready(&rpc_client); - let (blockhash, _) = rpc_client.get_recent_blockhash().unwrap(); + let blockhash = rpc_client.get_latest_blockhash().unwrap(); // Offline fee-payer signs first let mut fee_payer_config = CliConfig::recent_for_tests(); diff --git a/client/src/blockhash_query.rs b/client/src/blockhash_query.rs index 78dd26e39..616bd0f28 100644 --- a/client/src/blockhash_query.rs +++ b/client/src/blockhash_query.rs @@ -19,6 +19,7 @@ pub enum Source { } impl Source { + #[deprecated(since = "1.8.0", note = "Please use `get_blockhash` instead")] pub fn get_blockhash_and_fee_calculator( &self, rpc_client: &RpcClient, @@ -26,6 +27,7 @@ impl Source { ) -> Result<(Hash, FeeCalculator), Box> { match self { Self::Cluster => { + #[allow(deprecated)] let res = rpc_client .get_recent_blockhash_with_commitment(commitment)? .value; @@ -39,6 +41,10 @@ impl Source { } } + #[deprecated( + since = "1.8.0", + note = "Please do not use, will no longer be available in the future" + )] pub fn get_fee_calculator( &self, rpc_client: &RpcClient, @@ -47,6 +53,7 @@ impl Source { ) -> Result, Box> { match self { Self::Cluster => { + #[allow(deprecated)] let res = rpc_client .get_fee_calculator_for_blockhash_with_commitment(blockhash, commitment)? .value; @@ -61,6 +68,40 @@ impl Source { } } } + + pub fn get_blockhash( + &self, + rpc_client: &RpcClient, + commitment: CommitmentConfig, + ) -> Result> { + match self { + Self::Cluster => { + let (blockhash, _) = rpc_client.get_latest_blockhash_with_commitment(commitment)?; + Ok(blockhash) + } + Self::NonceAccount(ref pubkey) => { + let data = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment) + .and_then(|ref a| nonce_utils::data_from_account(a))?; + Ok(data.blockhash) + } + } + } + + pub fn is_blockhash_valid( + &self, + rpc_client: &RpcClient, + blockhash: &Hash, + commitment: CommitmentConfig, + ) -> Result> { + Ok(match self { + Self::Cluster => rpc_client.is_blockhash_valid(blockhash, commitment)?, + Self::NonceAccount(ref pubkey) => { + let _ = nonce_utils::get_account_with_commitment(rpc_client, pubkey, commitment) + .and_then(|ref a| nonce_utils::data_from_account(a))?; + true + } + }) + } } #[derive(Debug, PartialEq)] @@ -90,6 +131,7 @@ impl BlockhashQuery { BlockhashQuery::new(blockhash, sign_only, nonce_account) } + #[deprecated(since = "1.8.0", note = "Please use `get_blockhash` instead")] pub fn get_blockhash_and_fee_calculator( &self, rpc_client: &RpcClient, @@ -98,16 +140,36 @@ impl BlockhashQuery { match self { BlockhashQuery::None(hash) => Ok((*hash, FeeCalculator::default())), BlockhashQuery::FeeCalculator(source, hash) => { + #[allow(deprecated)] let fee_calculator = source .get_fee_calculator(rpc_client, hash, commitment)? .ok_or(format!("Hash has expired {:?}", hash))?; Ok((*hash, fee_calculator)) } - BlockhashQuery::All(source) => { + BlockhashQuery::All(source) => + { + #[allow(deprecated)] source.get_blockhash_and_fee_calculator(rpc_client, commitment) } } } + + pub fn get_blockhash( + &self, + rpc_client: &RpcClient, + commitment: CommitmentConfig, + ) -> Result> { + match self { + BlockhashQuery::None(hash) => Ok(*hash), + BlockhashQuery::FeeCalculator(source, hash) => { + if !source.is_blockhash_valid(rpc_client, hash, commitment)? { + return Err(format!("Hash has expired {:?}", hash).into()); + } + Ok(*hash) + } + BlockhashQuery::All(source) => source.get_blockhash(rpc_client, commitment), + } + } } impl Default for BlockhashQuery { @@ -282,6 +344,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_blockhash_query_get_blockhash_fee_calc() { let test_blockhash = hash(&[0u8]); let rpc_blockhash = hash(&[1u8]); diff --git a/client/src/mock_sender.rs b/client/src/mock_sender.rs index 3cd31623a..53db4f329 100644 --- a/client/src/mock_sender.rs +++ b/client/src/mock_sender.rs @@ -6,9 +6,9 @@ use { rpc_config::RpcBlockProductionConfig, rpc_request::RpcRequest, rpc_response::{ - Response, RpcAccountBalance, RpcBlockProduction, RpcBlockProductionRange, RpcFees, - RpcResponseContext, RpcSimulateTransactionResult, RpcStakeActivation, RpcSupply, - RpcVersionInfo, RpcVoteAccountStatus, StakeActivationState, + Response, RpcAccountBalance, RpcBlockProduction, RpcBlockProductionRange, RpcBlockhash, + RpcFees, RpcResponseContext, RpcSimulateTransactionResult, RpcStakeActivation, + RpcSupply, RpcVersionInfo, RpcVoteAccountStatus, StakeActivationState, }, rpc_sender::RpcSender, }, @@ -271,6 +271,17 @@ impl RpcSender for MockSender { feature_set: Some(version.feature_set), }) } + "getLatestBlockhash" => serde_json::to_value(Response { + context: RpcResponseContext { slot: 1 }, + value: RpcBlockhash { + blockhash: PUBKEY.to_string(), + last_valid_block_height: 0, + }, + })?, + "getFeeForMessage" => serde_json::to_value(Response { + context: RpcResponseContext { slot: 1 }, + value: json!(Some(0)), + })?, _ => Value::Null, }; Ok(val) diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index f76bef1c5..e27c7fde5 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -38,6 +38,7 @@ use { epoch_schedule::EpochSchedule, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, + message::Message, pubkey::Pubkey, signature::Signature, transaction::{self, uses_durable_nonce, Transaction}, @@ -722,7 +723,7 @@ impl RpcClient { preflight_commitment: Some(preflight_commitment.commitment), ..config }; - let serialized_encoded = serialize_encode_transaction(transaction, encoding)?; + let serialized_encoded = serialize_and_encode::(transaction, encoding)?; let signature_base58_str: String = match self.send( RpcRequest::SendTransaction, json!([serialized_encoded, config]), @@ -859,7 +860,7 @@ impl RpcClient { commitment: Some(commitment), ..config }; - let serialized_encoded = serialize_encode_transaction(transaction, encoding)?; + let serialized_encoded = serialize_and_encode::(transaction, encoding)?; self.send( RpcRequest::SimulateTransaction, json!([serialized_encoded, config]), @@ -1981,9 +1982,8 @@ impl RpcClient { let signature = self.send_transaction(transaction)?; let recent_blockhash = if uses_durable_nonce(transaction).is_some() { - let (recent_blockhash, ..) = self - .get_recent_blockhash_with_commitment(CommitmentConfig::processed())? - .value; + let (recent_blockhash, ..) = + self.get_latest_blockhash_with_commitment(CommitmentConfig::processed())?; recent_blockhash } else { transaction.message.recent_blockhash @@ -1994,13 +1994,9 @@ impl RpcClient { Some(Ok(_)) => return Ok(signature), Some(Err(e)) => return Err(e.into()), None => { - let fee_calculator = self - .get_fee_calculator_for_blockhash_with_commitment( - &recent_blockhash, - CommitmentConfig::processed(), - )? - .value; - if fee_calculator.is_none() { + if !self + .is_blockhash_valid(&recent_blockhash, CommitmentConfig::processed())? + { // Block hash is not found by some reason break 'sending; } else if cfg!(not(test)) @@ -2209,10 +2205,21 @@ impl RpcClient { ) } + #[deprecated( + since = "1.8.0", + note = "Please use `get_latest_blockhash` and `get_fee_for_message` instead" + )] + #[allow(deprecated)] pub fn get_fees(&self) -> ClientResult { + #[allow(deprecated)] Ok(self.get_fees_with_commitment(self.commitment())?.value) } + #[deprecated( + since = "1.8.0", + note = "Please use `get_latest_blockhash_with_commitment` and `get_fee_for_message` instead" + )] + #[allow(deprecated)] pub fn get_fees_with_commitment(&self, commitment_config: CommitmentConfig) -> RpcResult { let Response { context, @@ -2237,13 +2244,21 @@ impl RpcClient { }) } + #[deprecated(since = "1.8.0", note = "Please use `get_latest_blockhash` instead")] + #[allow(deprecated)] pub fn get_recent_blockhash(&self) -> ClientResult<(Hash, FeeCalculator)> { + #[allow(deprecated)] let (blockhash, fee_calculator, _last_valid_slot) = self .get_recent_blockhash_with_commitment(self.commitment())? .value; Ok((blockhash, fee_calculator)) } + #[deprecated( + since = "1.8.0", + note = "Please use `get_latest_blockhash_with_commitment` instead" + )] + #[allow(deprecated)] pub fn get_recent_blockhash_with_commitment( &self, commitment_config: CommitmentConfig, @@ -2307,15 +2322,23 @@ impl RpcClient { }) } + #[deprecated(since = "1.8.0", note = "Please `get_fee_for_message` instead")] + #[allow(deprecated)] pub fn get_fee_calculator_for_blockhash( &self, blockhash: &Hash, ) -> ClientResult> { + #[allow(deprecated)] Ok(self .get_fee_calculator_for_blockhash_with_commitment(blockhash, self.commitment())? .value) } + #[deprecated( + since = "1.8.0", + note = "Please `get_latest_blockhash_with_commitment` and `get_fee_for_message` instead" + )] + #[allow(deprecated)] pub fn get_fee_calculator_for_blockhash_with_commitment( &self, blockhash: &Hash, @@ -2335,6 +2358,11 @@ impl RpcClient { }) } + #[deprecated( + since = "1.8.0", + note = "Please do not use, will no longer be available in the future" + )] + #[allow(deprecated)] pub fn get_fee_rate_governor(&self) -> RpcResult { let Response { context, @@ -2348,10 +2376,16 @@ impl RpcClient { }) } + #[deprecated( + since = "1.8.0", + note = "Please use `get_new_latest_blockhash` instead" + )] + #[allow(deprecated)] pub fn get_new_blockhash(&self, blockhash: &Hash) -> ClientResult<(Hash, FeeCalculator)> { let mut num_retries = 0; let start = Instant::now(); while start.elapsed().as_secs() < 5 { + #[allow(deprecated)] if let Ok((new_blockhash, fee_calculator)) = self.get_recent_blockhash() { if new_blockhash != *blockhash { return Ok((new_blockhash, fee_calculator)); @@ -2831,8 +2865,7 @@ impl RpcClient { config: RpcSendTransactionConfig, ) -> ClientResult { let recent_blockhash = if uses_durable_nonce(transaction).is_some() { - self.get_recent_blockhash_with_commitment(CommitmentConfig::processed())? - .value + self.get_latest_blockhash_with_commitment(CommitmentConfig::processed())? .0 } else { transaction.message.recent_blockhash @@ -2872,13 +2905,8 @@ impl RpcClient { let status = self .get_signature_status_with_commitment(signature, CommitmentConfig::processed())?; if status.is_none() { - let blockhash_not_found = self - .get_fee_calculator_for_blockhash_with_commitment( - recent_blockhash, - CommitmentConfig::processed(), - )? - .value - .is_none(); + let blockhash_not_found = + !self.is_blockhash_valid(recent_blockhash, CommitmentConfig::processed())?; if blockhash_not_found && now.elapsed() >= confirm_transaction_initial_timeout { break (signature, status); } @@ -2936,6 +2964,84 @@ impl RpcClient { } } + pub fn get_latest_blockhash(&self) -> ClientResult { + let (blockhash, _) = self.get_latest_blockhash_with_commitment(self.commitment())?; + Ok(blockhash) + } + + pub fn get_latest_blockhash_with_commitment( + &self, + commitment: CommitmentConfig, + ) -> ClientResult<(Hash, u64)> { + let latest_blockhash = self + .send::>( + RpcRequest::GetLatestBlockhash, + json!([self.maybe_map_commitment(commitment)?]), + )? + .value; + + let blockhash = latest_blockhash.blockhash.parse().map_err(|_| { + ClientError::new_with_request( + RpcError::ParseError("Hash".to_string()).into(), + RpcRequest::GetLatestBlockhash, + ) + })?; + Ok((blockhash, latest_blockhash.last_valid_block_height)) + } + + pub fn is_blockhash_valid( + &self, + blockhash: &Hash, + commitment: CommitmentConfig, + ) -> ClientResult { + let result = self.send::>( + RpcRequest::IsBlockhashValid, + json!([blockhash.to_string(), commitment,]), + )?; + Ok(result.value) + } + + pub fn get_fee_for_message(&self, blockhash: &Hash, message: &Message) -> ClientResult { + let serialized_encoded = + serialize_and_encode::(message, UiTransactionEncoding::Base64)?; + let result = self.send::>>( + RpcRequest::GetFeeForMessage, + json!([ + blockhash.to_string(), + serialized_encoded, + UiTransactionEncoding::Base64, + self.commitment(), + ]), + )?; + result + .value + .ok_or_else(|| ClientErrorKind::Custom("Invalid blockhash".to_string()).into()) + } + + pub fn get_new_latest_blockhash(&self, blockhash: &Hash) -> ClientResult { + let mut num_retries = 0; + let start = Instant::now(); + while start.elapsed().as_secs() < 5 { + if let Ok(new_blockhash) = self.get_latest_blockhash() { + if new_blockhash != *blockhash { + return Ok(new_blockhash); + } + } + debug!("Got same blockhash ({:?}), will retry...", blockhash); + + // Retry ~twice during a slot + sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT / 2)); + num_retries += 1; + } + Err(RpcError::ForUser(format!( + "Unable to get new blockhash after {}ms (retried {} times), stuck at {}", + start.elapsed().as_millis(), + num_retries, + blockhash + )) + .into()) + } + pub fn send(&self, request: RpcRequest, params: Value) -> ClientResult where T: serde::de::DeserializeOwned, @@ -2951,18 +3057,18 @@ impl RpcClient { } } -fn serialize_encode_transaction( - transaction: &Transaction, - encoding: UiTransactionEncoding, -) -> ClientResult { - let serialized = serialize(transaction) - .map_err(|e| ClientErrorKind::Custom(format!("transaction serialization failed: {}", e)))?; +pub fn serialize_and_encode(input: &T, encoding: UiTransactionEncoding) -> ClientResult +where + T: serde::ser::Serialize, +{ + let serialized = serialize(input) + .map_err(|e| ClientErrorKind::Custom(format!("Serialization failed: {}", e)))?; let encoded = match encoding { UiTransactionEncoding::Base58 => bs58::encode(serialized).into_string(), UiTransactionEncoding::Base64 => base64::encode(serialized), _ => { return Err(ClientErrorKind::Custom(format!( - "unsupported transaction encoding: {}. Supported encodings: base58, base64", + "unsupported encoding: {}. Supported encodings: base58, base64", encoding )) .into()) @@ -3094,12 +3200,14 @@ mod tests { .unwrap(); assert_eq!(balance, 50); + #[allow(deprecated)] let blockhash: String = rpc_client .send(RpcRequest::GetRecentBlockhash, Value::Null) .unwrap(); assert_eq!(blockhash, "deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"); // Send erroneous parameter + #[allow(deprecated)] let blockhash: ClientResult = rpc_client.send(RpcRequest::GetRecentBlockhash, json!(["parameter"])); assert!(blockhash.is_err()); @@ -3134,12 +3242,14 @@ mod tests { let expected_blockhash: Hash = PUBKEY.parse().unwrap(); - let (blockhash, _fee_calculator) = rpc_client.get_recent_blockhash().expect("blockhash ok"); + let blockhash = rpc_client.get_latest_blockhash().expect("blockhash ok"); assert_eq!(blockhash, expected_blockhash); let rpc_client = RpcClient::new_mock("fails".to_string()); - assert!(rpc_client.get_recent_blockhash().is_err()); + #[allow(deprecated)] + let result = rpc_client.get_recent_blockhash(); + assert!(result.is_err()); } #[test] @@ -3229,4 +3339,20 @@ mod tests { Ok(()) } + + #[test] + fn test_get_latest_blockhash() { + let rpc_client = RpcClient::new_mock("succeeds".to_string()); + + let expected_blockhash: Hash = PUBKEY.parse().unwrap(); + + let blockhash = rpc_client.get_latest_blockhash().expect("blockhash ok"); + assert_eq!(blockhash, expected_blockhash); + + let rpc_client = RpcClient::new_mock("fails".to_string()); + + #[allow(deprecated)] + let is_err = rpc_client.get_latest_blockhash().is_err(); + assert!(is_err); + } } diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 52bae09c1..059900550 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -8,6 +8,9 @@ use { #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] pub enum RpcRequest { + Custom { + method: &'static str, + }, DeregisterNode, GetAccountInfo, GetBalance, @@ -18,7 +21,6 @@ pub enum RpcRequest { GetBlocksWithLimit, GetBlockTime, GetClusterNodes, - #[deprecated(since = "1.7.0", note = "Please use RpcRequest::GetBlock instead")] GetConfirmedBlock, #[deprecated(since = "1.7.0", note = "Please use RpcRequest::GetBlocks instead")] @@ -38,11 +40,23 @@ pub enum RpcRequest { note = "Please use RpcRequest::GetTransaction instead" )] GetConfirmedTransaction, - GetEpochInfo, GetEpochSchedule, + #[deprecated( + since = "1.8.0", + note = "Please use RpcRequest::GetFeeForMessage instead" + )] GetFeeCalculatorForBlockhash, + GetFeeForMessage, + #[deprecated( + since = "1.8.0", + note = "Please do not use, will no longer be available in the future" + )] GetFeeRateGovernor, + #[deprecated( + since = "1.8.0", + note = "Please use RpcRequest::GetFeeForMessage instead" + )] GetFees, GetFirstAvailableBlock, GetGenesisHash, @@ -52,12 +66,17 @@ pub enum RpcRequest { GetInflationRate, GetInflationReward, GetLargestAccounts, + GetLatestBlockhash, GetLeaderSchedule, GetMaxRetransmitSlot, GetMaxShredInsertSlot, GetMinimumBalanceForRentExemption, GetMultipleAccounts, GetProgramAccounts, + #[deprecated( + since = "1.8.0", + note = "Please use RpcRequest::GetLatestBlockhash instead" + )] GetRecentBlockhash, GetRecentPerformanceSamples, GetSnapshotSlot, @@ -80,21 +99,20 @@ pub enum RpcRequest { GetTransactionCount, GetVersion, GetVoteAccounts, + IsBlockhashValid, MinimumLedgerSlot, RegisterNode, RequestAirdrop, SendTransaction, SimulateTransaction, SignVote, - Custom { - method: &'static str, - }, } #[allow(deprecated)] impl fmt::Display for RpcRequest { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let method = match self { + RpcRequest::Custom { method } => method, RpcRequest::DeregisterNode => "deregisterNode", RpcRequest::GetAccountInfo => "getAccountInfo", RpcRequest::GetBalance => "getBalance", @@ -113,6 +131,7 @@ impl fmt::Display for RpcRequest { RpcRequest::GetEpochInfo => "getEpochInfo", RpcRequest::GetEpochSchedule => "getEpochSchedule", RpcRequest::GetFeeCalculatorForBlockhash => "getFeeCalculatorForBlockhash", + RpcRequest::GetFeeForMessage => "getFeeForMessage", RpcRequest::GetFeeRateGovernor => "getFeeRateGovernor", RpcRequest::GetFees => "getFees", RpcRequest::GetFirstAvailableBlock => "getFirstAvailableBlock", @@ -123,6 +142,7 @@ impl fmt::Display for RpcRequest { RpcRequest::GetInflationRate => "getInflationRate", RpcRequest::GetInflationReward => "getInflationReward", RpcRequest::GetLargestAccounts => "getLargestAccounts", + RpcRequest::GetLatestBlockhash => "getLatestBlockhash", RpcRequest::GetLeaderSchedule => "getLeaderSchedule", RpcRequest::GetMaxRetransmitSlot => "getMaxRetransmitSlot", RpcRequest::GetMaxShredInsertSlot => "getMaxShredInsertSlot", @@ -151,13 +171,13 @@ impl fmt::Display for RpcRequest { RpcRequest::GetTransactionCount => "getTransactionCount", RpcRequest::GetVersion => "getVersion", RpcRequest::GetVoteAccounts => "getVoteAccounts", + RpcRequest::IsBlockhashValid => "isBlockhashValid", RpcRequest::MinimumLedgerSlot => "minimumLedgerSlot", RpcRequest::RegisterNode => "registerNode", RpcRequest::RequestAirdrop => "requestAirdrop", RpcRequest::SendTransaction => "sendTransaction", RpcRequest::SimulateTransaction => "simulateTransaction", RpcRequest::SignVote => "signVote", - RpcRequest::Custom { method } => method, }; write!(f, "{}", method) @@ -261,14 +281,17 @@ mod tests { let request = test_request.build_request_json(1, Value::Null); assert_eq!(request["method"], "getEpochInfo"); + #[allow(deprecated)] let test_request = RpcRequest::GetRecentBlockhash; let request = test_request.build_request_json(1, Value::Null); assert_eq!(request["method"], "getRecentBlockhash"); + #[allow(deprecated)] let test_request = RpcRequest::GetFeeCalculatorForBlockhash; let request = test_request.build_request_json(1, json!([addr])); assert_eq!(request["method"], "getFeeCalculatorForBlockhash"); + #[allow(deprecated)] let test_request = RpcRequest::GetFeeRateGovernor; let request = test_request.build_request_json(1, Value::Null); assert_eq!(request["method"], "getFeeRateGovernor"); @@ -298,6 +321,7 @@ mod tests { let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"); // Test request with CommitmentConfig and no params + #[allow(deprecated)] let test_request = RpcRequest::GetRecentBlockhash; let request = test_request.build_request_json(1, json!([commitment_config])); assert_eq!(request["params"], json!([commitment_config.clone()])); diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index 4ea68e0b6..83fe6979d 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -41,6 +41,13 @@ pub struct RpcBlockhashFeeCalculator { pub fee_calculator: FeeCalculator, } +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RpcBlockhash { + pub blockhash: String, + pub last_valid_block_height: u64, +} + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct RpcFees { diff --git a/client/src/thin_client.rs b/client/src/thin_client.rs index 3988e8e5d..0e372e427 100644 --- a/client/src/thin_client.rs +++ b/client/src/thin_client.rs @@ -246,7 +246,7 @@ impl ThinClient { } } info!("{} tries failed transfer to {}", x, self.tpu_addr()); - let (blockhash, _fee_calculator) = self.get_recent_blockhash()?; + let blockhash = self.get_latest_blockhash()?; transaction.sign(keypairs, blockhash); } Err(io::Error::new( @@ -333,7 +333,7 @@ impl SyncClient for ThinClient { keypairs: &T, message: Message, ) -> TransportResult { - let (blockhash, _fee_calculator) = self.get_recent_blockhash()?; + let blockhash = self.get_latest_blockhash()?; let mut transaction = Transaction::new(keypairs, message, blockhash); let signature = self.send_and_confirm_transaction(keypairs, &mut transaction, 5, 0)?; Ok(signature) @@ -404,6 +404,7 @@ impl SyncClient for ThinClient { } fn get_recent_blockhash(&self) -> TransportResult<(Hash, FeeCalculator)> { + #[allow(deprecated)] let (blockhash, fee_calculator, _last_valid_slot) = self.get_recent_blockhash_with_commitment(CommitmentConfig::default())?; Ok((blockhash, fee_calculator)) @@ -415,6 +416,7 @@ impl SyncClient for ThinClient { ) -> TransportResult<(Hash, FeeCalculator, Slot)> { let index = self.optimizer.experiment(); let now = Instant::now(); + #[allow(deprecated)] let recent_blockhash = self.rpc_clients[index].get_recent_blockhash_with_commitment(commitment_config); match recent_blockhash { @@ -433,12 +435,14 @@ impl SyncClient for ThinClient { &self, blockhash: &Hash, ) -> TransportResult> { + #[allow(deprecated)] self.rpc_client() .get_fee_calculator_for_blockhash(blockhash) .map_err(|e| e.into()) } fn get_fee_rate_governor(&self) -> TransportResult { + #[allow(deprecated)] self.rpc_client() .get_fee_rate_governor() .map_err(|e| e.into()) @@ -556,10 +560,57 @@ impl SyncClient for ThinClient { } fn get_new_blockhash(&self, blockhash: &Hash) -> TransportResult<(Hash, FeeCalculator)> { + #[allow(deprecated)] self.rpc_client() .get_new_blockhash(blockhash) .map_err(|e| e.into()) } + + fn get_latest_blockhash(&self) -> TransportResult { + let (blockhash, _) = + self.get_latest_blockhash_with_commitment(CommitmentConfig::default())?; + Ok(blockhash) + } + + fn get_latest_blockhash_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> TransportResult<(Hash, u64)> { + let index = self.optimizer.experiment(); + let now = Instant::now(); + match self.rpc_clients[index].get_latest_blockhash_with_commitment(commitment_config) { + Ok((blockhash, last_valid_block_height)) => { + self.optimizer.report(index, duration_as_ms(&now.elapsed())); + Ok((blockhash, last_valid_block_height)) + } + Err(e) => { + self.optimizer.report(index, std::u64::MAX); + Err(e.into()) + } + } + } + + fn is_blockhash_valid( + &self, + blockhash: &Hash, + commitment_config: CommitmentConfig, + ) -> TransportResult { + self.rpc_client() + .is_blockhash_valid(blockhash, commitment_config) + .map_err(|e| e.into()) + } + + fn get_fee_for_message(&self, blockhash: &Hash, message: &Message) -> TransportResult { + self.rpc_client() + .get_fee_for_message(blockhash, message) + .map_err(|e| e.into()) + } + + fn get_new_latest_blockhash(&self, blockhash: &Hash) -> TransportResult { + self.rpc_client() + .get_new_latest_blockhash(blockhash) + .map_err(|e| e.into()) + } } impl AsyncClient for ThinClient { diff --git a/core/src/test_validator.rs b/core/src/test_validator.rs index 83ba53dca..9718aa87e 100644 --- a/core/src/test_validator.rs +++ b/core/src/test_validator.rs @@ -28,6 +28,8 @@ use { exit::Exit, fee_calculator::{FeeCalculator, FeeRateGovernor}, hash::Hash, + instruction::{AccountMeta, Instruction}, + message::Message, native_token::sol_to_lamports, pubkey::Pubkey, rent::Rent, @@ -551,25 +553,40 @@ impl TestValidator { { let rpc_client = RpcClient::new_with_commitment(rpc_url.clone(), CommitmentConfig::processed()); - - if let Ok(result) = rpc_client.get_fee_rate_governor() { - let fee_rate_governor = result.value; - if fee_rate_governor.target_lamports_per_signature > 0 { - loop { - match rpc_client.get_recent_blockhash() { - Ok((_blockhash, fee_calculator)) => { - if fee_calculator.lamports_per_signature != 0 { - break; - } - } - Err(err) => { - warn!("get_recent_blockhash() failed: {:?}", err); + let message = Message::new( + &[Instruction::new_with_bytes( + Pubkey::new_unique(), + &[], + vec![AccountMeta::new(Pubkey::new_unique(), true)], + )], + None, + ); + const MAX_TRIES: u64 = 10; + let mut num_tries = 0; + loop { + num_tries += 1; + if num_tries > MAX_TRIES { + break; + } + println!("Waiting for fees to stabilize {:?}...", num_tries); + match rpc_client.get_latest_blockhash() { + Ok(blockhash) => match rpc_client.get_fee_for_message(&blockhash, &message) { + Ok(fee) => { + if fee != 0 { break; } } - sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT)); + Err(err) => { + warn!("get_fee_for_message() failed: {:?}", err); + break; + } + }, + Err(err) => { + warn!("get_latest_blockhash() failed: {:?}", err); + break; } } + sleep(Duration::from_millis(DEFAULT_MS_PER_SLOT)); } } @@ -615,6 +632,7 @@ impl TestValidator { pub fn rpc_client(&self) -> (RpcClient, Hash, FeeCalculator) { let rpc_client = RpcClient::new_with_commitment(self.rpc_url.clone(), CommitmentConfig::processed()); + #[allow(deprecated)] let (recent_blockhash, fee_calculator) = rpc_client .get_recent_blockhash() .expect("get_recent_blockhash"); diff --git a/core/src/vote_simulator.rs b/core/src/vote_simulator.rs index c912d0518..2b1f46b96 100644 --- a/core/src/vote_simulator.rs +++ b/core/src/vote_simulator.rs @@ -82,12 +82,12 @@ impl VoteSimulator { for (pubkey, vote) in cluster_votes.iter() { if vote.contains(&parent) { let keypairs = self.validator_keypairs.get(pubkey).unwrap(); - let last_blockhash = parent_bank.last_blockhash(); + let latest_blockhash = parent_bank.last_blockhash(); let vote_tx = vote_transaction::new_vote_transaction( // Must vote > root to be processed vec![parent], parent_bank.hash(), - last_blockhash, + latest_blockhash, &keypairs.node_keypair, &keypairs.vote_keypair, &keypairs.vote_keypair, diff --git a/core/tests/client.rs b/core/tests/client.rs index 8618e5dd1..75ea3d75c 100644 --- a/core/tests/client.rs +++ b/core/tests/client.rs @@ -53,7 +53,7 @@ fn test_rpc_client() { let original_alice_balance = client.get_balance(&alice.pubkey()).unwrap(); - let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap(); + let blockhash = client.get_latest_blockhash().unwrap(); let tx = system_transaction::transfer(&alice, &bob_pubkey, sol_to_lamports(20.0), blockhash); let signature = client.send_transaction(&tx).unwrap(); diff --git a/core/tests/rpc.rs b/core/tests/rpc.rs index 45d5f9f5a..34fee2df0 100644 --- a/core/tests/rpc.rs +++ b/core/tests/rpc.rs @@ -231,7 +231,7 @@ fn test_rpc_subscriptions() { transactions_socket.connect(test_validator.tpu()).unwrap(); let rpc_client = RpcClient::new(test_validator.rpc_url()); - let recent_blockhash = rpc_client.get_recent_blockhash().unwrap().0; + let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); // Create transaction signatures to subscribe to let transactions: Vec = (0..1000) @@ -406,7 +406,7 @@ fn test_tpu_send_transaction() { ) .unwrap(); - let recent_blockhash = rpc_client.get_recent_blockhash().unwrap().0; + let recent_blockhash = rpc_client.get_latest_blockhash().unwrap(); let tx = system_transaction::transfer(&mint_keypair, &Pubkey::new_unique(), 42, recent_blockhash); assert!(tpu_client.send_transaction(&tx)); @@ -433,8 +433,8 @@ fn deserialize_rpc_error() -> ClientResult<()> { let bob = Keypair::new(); let lamports = 50; - let (recent_blockhash, _) = rpc_client.get_recent_blockhash()?; - let mut tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, recent_blockhash); + let blockhash = rpc_client.get_latest_blockhash()?; + let mut tx = system_transaction::transfer(&alice, &bob.pubkey(), lamports, blockhash); // This will cause an error tx.signatures.clear(); diff --git a/docs/src/proposals/simple-payment-and-state-verification.md b/docs/src/proposals/simple-payment-and-state-verification.md index 701e3a843..5d3fa1f87 100644 --- a/docs/src/proposals/simple-payment-and-state-verification.md +++ b/docs/src/proposals/simple-payment-and-state-verification.md @@ -122,7 +122,7 @@ https://github.com/solana-labs/solana/blob/b6bfed64cb159ee67bb6bdbaefc7f833bbed3 // Number of signatures processed in this block &signature_count_buf, // Last PoH hash in this block - self.last_blockhash().as_ref(), + self.latest_blockhash().as_ref(), ]); ``` diff --git a/install/src/command.rs b/install/src/command.rs index 68be9b506..dd317cab5 100644 --- a/install/src/command.rs +++ b/install/src/command.rs @@ -218,7 +218,7 @@ fn new_update_manifest( .get_account_data(&update_manifest_keypair.pubkey()) .is_err() { - let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?; + let recent_blockhash = rpc_client.get_latest_blockhash()?; let lamports = rpc_client .get_minimum_balance_for_rent_exemption(SignedUpdateManifest::max_space() as usize)?; @@ -244,7 +244,7 @@ fn store_update_manifest( update_manifest_keypair: &Keypair, update_manifest: &SignedUpdateManifest, ) -> Result<(), Box> { - let (recent_blockhash, _fee_calculator) = rpc_client.get_recent_blockhash()?; + let recent_blockhash = rpc_client.get_latest_blockhash()?; let signers = [from_keypair, update_manifest_keypair]; let instruction = config_instruction::store::( diff --git a/local-cluster/src/cluster_tests.rs b/local-cluster/src/cluster_tests.rs index 2312231c4..d0d354cdf 100644 --- a/local-cluster/src/cluster_tests.rs +++ b/local-cluster/src/cluster_tests.rs @@ -66,8 +66,8 @@ pub fn spend_and_verify_all_nodes( ) .expect("balance in source"); assert!(bal > 0); - let (blockhash, _fee_calculator, _last_valid_slot) = client - .get_recent_blockhash_with_commitment(CommitmentConfig::confirmed()) + let (blockhash, _) = client + .get_latest_blockhash_with_commitment(CommitmentConfig::confirmed()) .unwrap(); let mut transaction = system_transaction::transfer(funding_keypair, &random_keypair.pubkey(), 1, blockhash); @@ -115,8 +115,8 @@ pub fn send_many_transactions( ) .expect("balance in source"); assert!(bal > 0); - let (blockhash, _fee_calculator, _last_valid_slot) = client - .get_recent_blockhash_with_commitment(CommitmentConfig::processed()) + let (blockhash, _) = client + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .unwrap(); let transfer_amount = thread_rng().gen_range(1, max_tokens_per_transfer); @@ -241,8 +241,8 @@ pub fn kill_entry_and_spend_and_verify_rest( } let random_keypair = Keypair::new(); - let (blockhash, _fee_calculator, _last_valid_slot) = client - .get_recent_blockhash_with_commitment(CommitmentConfig::processed()) + let (blockhash, _) = client + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .unwrap(); let mut transaction = system_transaction::transfer( funding_keypair, diff --git a/local-cluster/src/local_cluster.rs b/local-cluster/src/local_cluster.rs index cae0f5e2a..fb209611b 100644 --- a/local-cluster/src/local_cluster.rs +++ b/local-cluster/src/local_cluster.rs @@ -481,8 +481,8 @@ impl LocalCluster { lamports: u64, ) -> u64 { trace!("getting leader blockhash"); - let (blockhash, _fee_calculator, _last_valid_slot) = client - .get_recent_blockhash_with_commitment(CommitmentConfig::processed()) + let (blockhash, _) = client + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .unwrap(); let mut tx = system_transaction::transfer(source_keypair, dest_pubkey, lamports, blockhash); info!( @@ -542,7 +542,7 @@ impl LocalCluster { &[from_account.as_ref(), vote_account], message, client - .get_recent_blockhash_with_commitment(CommitmentConfig::processed()) + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .unwrap() .0, ); @@ -570,7 +570,7 @@ impl LocalCluster { &[from_account.as_ref(), &stake_account_keypair], message, client - .get_recent_blockhash_with_commitment(CommitmentConfig::processed()) + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .unwrap() .0, ); diff --git a/local-cluster/tests/local_cluster.rs b/local-cluster/tests/local_cluster.rs index ab3f0967a..31a6133ba 100644 --- a/local-cluster/tests/local_cluster.rs +++ b/local-cluster/tests/local_cluster.rs @@ -191,8 +191,8 @@ fn test_local_cluster_signature_subscribe() { non_bootstrap_info.client_facing_addr(), VALIDATOR_PORT_RANGE, ); - let (blockhash, _fee_calculator, _last_valid_slot) = tx_client - .get_recent_blockhash_with_commitment(CommitmentConfig::processed()) + let (blockhash, _) = tx_client + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .unwrap(); let mut transaction = system_transaction::transfer( @@ -1475,8 +1475,8 @@ fn generate_frozen_account_panic(mut cluster: LocalCluster, frozen_account: Arc< let mut i = 0; while !solana_runtime::accounts_db::FROZEN_ACCOUNT_PANIC.load(Ordering::Relaxed) { // Transfer from frozen account - let (blockhash, _fee_calculator, _last_valid_slot) = client - .get_recent_blockhash_with_commitment(CommitmentConfig::processed()) + let (blockhash, _) = client + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .unwrap(); client .async_transfer( @@ -3246,8 +3246,8 @@ fn setup_transfer_scan_threads( if exit_.load(Ordering::Relaxed) { return; } - let (blockhash, _fee_calculator, _last_valid_slot) = client - .get_recent_blockhash_with_commitment(CommitmentConfig::processed()) + let (blockhash, _) = client + .get_latest_blockhash_with_commitment(CommitmentConfig::processed()) .unwrap(); for i in 0..starting_keypairs_.len() { client diff --git a/program-test/src/lib.rs b/program-test/src/lib.rs index f206780d1..77087ef5b 100644 --- a/program-test/src/lib.rs +++ b/program-test/src/lib.rs @@ -451,12 +451,12 @@ fn setup_fee_calculator(bank: Bank) -> Bank { } let last_blockhash = bank.last_blockhash(); // Make sure the new last_blockhash now requires a fee - assert_ne!( - bank.get_fee_calculator(&last_blockhash) - .expect("fee_calculator") - .lamports_per_signature, - 0 - ); + #[allow(deprecated)] + let lamports_per_signature = bank + .get_fee_calculator(&last_blockhash) + .expect("fee_calculator") + .lamports_per_signature; + assert_ne!(lamports_per_signature, 0); bank } diff --git a/programs/bpf/tests/programs.rs b/programs/bpf/tests/programs.rs index 781bd1fe6..6492ecc97 100644 --- a/programs/bpf/tests/programs.rs +++ b/programs/bpf/tests/programs.rs @@ -362,10 +362,12 @@ fn execute_transactions(bank: &Bank, txs: &[Transaction]) -> Vec, ) -> RpcResponse { let bank = self.bank(commitment); - let (blockhash, fee_calculator) = bank.confirmed_last_blockhash(); + #[allow(deprecated)] + let (blockhash, fee_calculator) = bank.confirmed_last_blockhash_with_fee_calculator(); new_response( &bank, RpcBlockhashFeeCalculator { @@ -543,7 +546,8 @@ impl JsonRpcRequestProcessor { fn get_fees(&self, commitment: Option) -> RpcResponse { let bank = self.bank(commitment); - let (blockhash, fee_calculator) = bank.confirmed_last_blockhash(); + #[allow(deprecated)] + let (blockhash, fee_calculator) = bank.confirmed_last_blockhash_with_fee_calculator(); #[allow(deprecated)] let last_valid_slot = bank .get_blockhash_last_valid_slot(&blockhash) @@ -568,6 +572,7 @@ impl JsonRpcRequestProcessor { commitment: Option, ) -> RpcResponse> { let bank = self.bank(commitment); + #[allow(deprecated)] let fee_calculator = bank.get_fee_calculator(blockhash); new_response( &bank, @@ -577,6 +582,7 @@ impl JsonRpcRequestProcessor { fn get_fee_rate_governor(&self) -> RpcResponse { let bank = self.bank(None); + #[allow(deprecated)] let fee_rate_governor = bank.get_fee_rate_governor(); new_response( &bank, @@ -1896,6 +1902,45 @@ impl JsonRpcRequestProcessor { self.get_filtered_program_accounts(bank, &spl_token_id_v2_0(), filters) } } + + fn get_latest_blockhash( + &self, + commitment: Option, + ) -> RpcResponse { + let bank = self.bank(commitment); + let blockhash = bank.last_blockhash(); + let last_valid_block_height = bank + .get_blockhash_last_valid_block_height(&blockhash) + .expect("bank blockhash queue should contain blockhash"); + new_response( + &bank, + RpcBlockhash { + blockhash: blockhash.to_string(), + last_valid_block_height, + }, + ) + } + + fn is_blockhash_valid( + &self, + blockhash: &Hash, + commitment: Option, + ) -> RpcResponse { + let bank = self.bank(commitment); + let is_valid = bank.is_blockhash_valid(blockhash); + new_response(&bank, is_valid) + } + + fn get_fee_for_message( + &self, + blockhash: &Hash, + message: &Message, + commitment: Option, + ) -> Result>> { + let bank = self.bank(commitment); + let fee = bank.get_fee_for_message(blockhash, message); + Ok(new_response(&bank, fee)) + } } fn verify_transaction( @@ -2410,34 +2455,6 @@ pub mod rpc_bank { #[rpc(meta, name = "getEpochSchedule")] fn get_epoch_schedule(&self, meta: Self::Metadata) -> Result; - #[rpc(meta, name = "getRecentBlockhash")] - fn get_recent_blockhash( - &self, - meta: Self::Metadata, - commitment: Option, - ) -> Result>; - - #[rpc(meta, name = "getFees")] - fn get_fees( - &self, - meta: Self::Metadata, - commitment: Option, - ) -> Result>; - - #[rpc(meta, name = "getFeeCalculatorForBlockhash")] - fn get_fee_calculator_for_blockhash( - &self, - meta: Self::Metadata, - blockhash: String, - commitment: Option, - ) -> Result>>; - - #[rpc(meta, name = "getFeeRateGovernor")] - fn get_fee_rate_governor( - &self, - meta: Self::Metadata, - ) -> Result>; - #[rpc(meta, name = "getSlotLeader")] fn get_slot_leader( &self, @@ -2500,44 +2517,6 @@ pub mod rpc_bank { Ok(meta.get_epoch_schedule()) } - fn get_recent_blockhash( - &self, - meta: Self::Metadata, - commitment: Option, - ) -> Result> { - debug!("get_recent_blockhash rpc request received"); - Ok(meta.get_recent_blockhash(commitment)) - } - - fn get_fees( - &self, - meta: Self::Metadata, - commitment: Option, - ) -> Result> { - debug!("get_fees rpc request received"); - Ok(meta.get_fees(commitment)) - } - - fn get_fee_calculator_for_blockhash( - &self, - meta: Self::Metadata, - blockhash: String, - commitment: Option, - ) -> Result>> { - debug!("get_fee_calculator_for_blockhash rpc request received"); - let blockhash = Hash::from_str(&blockhash) - .map_err(|e| Error::invalid_params(format!("{:?}", e)))?; - Ok(meta.get_fee_calculator_for_blockhash(&blockhash, commitment)) - } - - fn get_fee_rate_governor( - &self, - meta: Self::Metadata, - ) -> Result> { - debug!("get_fee_rate_governor rpc request received"); - Ok(meta.get_fee_rate_governor()) - } - fn get_slot_leader( &self, meta: Self::Metadata, @@ -3084,6 +3063,31 @@ pub mod rpc_full { #[rpc(meta, name = "getFirstAvailableBlock")] fn get_first_available_block(&self, meta: Self::Metadata) -> BoxFuture>; + + #[rpc(meta, name = "getLatestBlockhash")] + fn get_latest_blockhash( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result>; + + #[rpc(meta, name = "isBlockhashValid")] + fn is_blockhash_valid( + &self, + meta: Self::Metadata, + blockhash: String, + commitment: Option, + ) -> Result>; + + #[rpc(meta, name = "getFeeForMessage")] + fn get_fee_for_message( + &self, + meta: Self::Metadata, + blockhash: String, + data: String, + encoding: UiTransactionEncoding, + commitment: Option, + ) -> Result>>; } pub struct FullImpl; @@ -3232,7 +3236,7 @@ pub mod rpc_full { let blockhash = if let Some(blockhash) = config.recent_blockhash { verify_hash(&blockhash)? } else { - bank.confirmed_last_blockhash().0 + bank.confirmed_last_blockhash() }; let last_valid_block_height = bank .get_blockhash_last_valid_block_height(&blockhash) @@ -3269,7 +3273,8 @@ pub mod rpc_full { debug!("send_transaction rpc request received"); let config = config.unwrap_or_default(); let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58); - let (wire_transaction, transaction) = deserialize_transaction(data, encoding)?; + let (wire_transaction, transaction) = + decode_and_deserialize::(data, encoding)?; let preflight_commitment = config .preflight_commitment @@ -3369,7 +3374,7 @@ pub mod rpc_full { debug!("simulate_transaction rpc request received"); let config = config.unwrap_or_default(); let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Base58); - let (_, mut transaction) = deserialize_transaction(data, encoding)?; + let (_, mut transaction) = decode_and_deserialize::(data, encoding)?; let bank = &*meta.bank(config.commitment); if config.sig_verify { @@ -3578,10 +3583,126 @@ pub mod rpc_full { Box::pin(async move { meta.get_inflation_reward(addresses, config).await }) } + + fn get_latest_blockhash( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result> { + debug!("get_latest_blockhash rpc request received"); + Ok(meta.get_latest_blockhash(commitment)) + } + + fn is_blockhash_valid( + &self, + meta: Self::Metadata, + blockhash: String, + commitment: Option, + ) -> Result> { + let blockhash = Hash::from_str(&blockhash) + .map_err(|e| Error::invalid_params(format!("{:?}", e)))?; + Ok(meta.is_blockhash_valid(&blockhash, commitment)) + } + + fn get_fee_for_message( + &self, + meta: Self::Metadata, + blockhash: String, + data: String, + encoding: UiTransactionEncoding, + commitment: Option, + ) -> Result>> { + debug!("get_fee_for_message rpc request received"); + let blockhash = Hash::from_str(&blockhash) + .map_err(|e| Error::invalid_params(format!("{:?}", e)))?; + let (_, message) = decode_and_deserialize::(data, encoding)?; + meta.get_fee_for_message(&blockhash, &message, commitment) + } } } -// Deprecated RPC methods, collected for easy deactivation and removal in v1.8 +// RPC methods deprecated in v1.8 +pub mod rpc_deprecated_v1_8 { + #![allow(deprecated)] + use super::*; + #[rpc] + pub trait DeprecatedV1_8 { + type Metadata; + + #[rpc(meta, name = "getRecentBlockhash")] + fn get_recent_blockhash( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result>; + + #[rpc(meta, name = "getFees")] + fn get_fees( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result>; + + #[rpc(meta, name = "getFeeCalculatorForBlockhash")] + fn get_fee_calculator_for_blockhash( + &self, + meta: Self::Metadata, + blockhash: String, + commitment: Option, + ) -> Result>>; + + #[rpc(meta, name = "getFeeRateGovernor")] + fn get_fee_rate_governor( + &self, + meta: Self::Metadata, + ) -> Result>; + } + + pub struct DeprecatedV1_8Impl; + impl DeprecatedV1_8 for DeprecatedV1_8Impl { + type Metadata = JsonRpcRequestProcessor; + + fn get_recent_blockhash( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result> { + debug!("get_recent_blockhash rpc request received"); + Ok(meta.get_recent_blockhash(commitment)) + } + + fn get_fees( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result> { + debug!("get_fees rpc request received"); + Ok(meta.get_fees(commitment)) + } + + fn get_fee_calculator_for_blockhash( + &self, + meta: Self::Metadata, + blockhash: String, + commitment: Option, + ) -> Result>> { + debug!("get_fee_calculator_for_blockhash rpc request received"); + let blockhash = Hash::from_str(&blockhash) + .map_err(|e| Error::invalid_params(format!("{:?}", e)))?; + Ok(meta.get_fee_calculator_for_blockhash(&blockhash, commitment)) + } + + fn get_fee_rate_governor( + &self, + meta: Self::Metadata, + ) -> Result> { + debug!("get_fee_rate_governor rpc request received"); + Ok(meta.get_fee_rate_governor()) + } + } +} + +// RPC methods deprecated in v1.7 pub mod rpc_deprecated_v1_7 { #![allow(deprecated)] use super::*; @@ -3874,51 +3995,56 @@ pub mod rpc_obsolete_v1_7 { } } -const WORST_CASE_BASE58_TX: usize = 1683; // Golden, bump if PACKET_DATA_SIZE changes -const WORST_CASE_BASE64_TX: usize = 1644; // Golden, bump if PACKET_DATA_SIZE changes -fn deserialize_transaction( - encoded_transaction: String, +const MAX_BASE58_SIZE: usize = 1683; // Golden, bump if PACKET_DATA_SIZE changes +const MAX_BASE64_SIZE: usize = 1644; // Golden, bump if PACKET_DATA_SIZE changes +fn decode_and_deserialize( + encoded: String, encoding: UiTransactionEncoding, -) -> Result<(Vec, Transaction)> { - let wire_transaction = match encoding { +) -> Result<(Vec, T)> +where + T: serde::de::DeserializeOwned + Sanitize, +{ + let wire_output = match encoding { UiTransactionEncoding::Base58 => { inc_new_counter_info!("rpc-base58_encoded_tx", 1); - if encoded_transaction.len() > WORST_CASE_BASE58_TX { + if encoded.len() > MAX_BASE58_SIZE { return Err(Error::invalid_params(format!( - "encoded transaction too large: {} bytes (max: encoded/raw {}/{})", - encoded_transaction.len(), - WORST_CASE_BASE58_TX, + "encoded {} too large: {} bytes (max: encoded/raw {}/{})", + type_name::(), + encoded.len(), + MAX_BASE58_SIZE, PACKET_DATA_SIZE, ))); } - bs58::decode(encoded_transaction) + bs58::decode(encoded) .into_vec() .map_err(|e| Error::invalid_params(format!("{:?}", e)))? } UiTransactionEncoding::Base64 => { inc_new_counter_info!("rpc-base64_encoded_tx", 1); - if encoded_transaction.len() > WORST_CASE_BASE64_TX { + if encoded.len() > MAX_BASE64_SIZE { return Err(Error::invalid_params(format!( - "encoded transaction too large: {} bytes (max: encoded/raw {}/{})", - encoded_transaction.len(), - WORST_CASE_BASE64_TX, + "encoded {} too large: {} bytes (max: encoded/raw {}/{})", + type_name::(), + encoded.len(), + MAX_BASE64_SIZE, PACKET_DATA_SIZE, ))); } - base64::decode(encoded_transaction) - .map_err(|e| Error::invalid_params(format!("{:?}", e)))? + base64::decode(encoded).map_err(|e| Error::invalid_params(format!("{:?}", e)))? } _ => { return Err(Error::invalid_params(format!( - "unsupported transaction encoding: {}. Supported encodings: base58, base64", + "unsupported encoding: {}. Supported encodings: base58, base64", encoding ))) } }; - if wire_transaction.len() > PACKET_DATA_SIZE { + if wire_output.len() > PACKET_DATA_SIZE { let err = format!( - "transaction too large: {} bytes (max: {} bytes)", - wire_transaction.len(), + "encoded {} too large: {} bytes (max: {} bytes)", + type_name::(), + wire_output.len(), PACKET_DATA_SIZE ); info!("{}", err); @@ -3928,22 +4054,23 @@ fn deserialize_transaction( .with_limit(PACKET_DATA_SIZE as u64) .with_fixint_encoding() .allow_trailing_bytes() - .deserialize_from(&wire_transaction[..]) + .deserialize_from(&wire_output[..]) .map_err(|err| { - info!("transaction deserialize error: {:?}", err); + info!("deserialize error: {}", err); Error::invalid_params(&err.to_string()) }) - .and_then(|transaction: Transaction| { - if let Err(err) = transaction.sanitize() { + .and_then(|output: T| { + if let Err(err) = output.sanitize() { Err(Error::invalid_params(format!( - "invalid transaction: {}", + "invalid {}: {}", + type_name::(), err ))) } else { - Ok(transaction) + Ok(output) } }) - .map(|transaction| (wire_transaction, transaction)) + .map(|output| (wire_output, output)) } pub(crate) fn create_validator_exit(exit: &Arc) -> Arc> { @@ -3966,7 +4093,7 @@ pub fn create_test_transactions_and_populate_blockstore( let keypair2 = keypairs[2]; let keypair3 = keypairs[3]; let slot = bank.slot(); - let blockhash = bank.confirmed_last_blockhash().0; + let blockhash = bank.confirmed_last_blockhash(); // Generate transactions for processing // Successful transaction @@ -4032,7 +4159,9 @@ pub fn create_test_transactions_and_populate_blockstore( #[cfg(test)] pub mod tests { use { - super::{rpc_accounts::*, rpc_bank::*, rpc_full::*, rpc_minimal::*, *}, + super::{ + rpc_accounts::*, rpc_bank::*, rpc_deprecated_v1_8::*, rpc_full::*, rpc_minimal::*, *, + }, crate::{ optimistically_confirmed_bank_tracker::{ BankNotification, OptimisticallyConfirmedBankTracker, @@ -4196,7 +4325,7 @@ pub mod tests { let exit = Arc::new(AtomicBool::new(false)); let validator_exit = create_validator_exit(&exit); - let blockhash = bank.confirmed_last_blockhash().0; + let blockhash = bank.confirmed_last_blockhash(); let tx = system_transaction::transfer(&alice, pubkey, 20, blockhash); bank.process_transaction(&tx).expect("process transaction"); let tx = @@ -4267,6 +4396,7 @@ pub mod tests { io.extend_with(rpc_bank::BankDataImpl.to_delegate()); io.extend_with(rpc_accounts::AccountsDataImpl.to_delegate()); io.extend_with(rpc_full::FullImpl.to_delegate()); + io.extend_with(rpc_deprecated_v1_8::DeprecatedV1_8Impl.to_delegate()); RpcHandler { io, meta, @@ -5675,6 +5805,7 @@ pub mod tests { let bob_pubkey = solana_sdk::pubkey::new_rand(); let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); + #[allow(deprecated)] let (blockhash, fee_calculator) = bank.last_blockhash_with_fee_calculator(); let fee_calculator = RpcFeeCalculator { fee_calculator }; @@ -5855,7 +5986,7 @@ pub mod tests { assert_eq!( res, Some( - r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid transaction: index out of bounds"},"id":1}"#.to_string(), + r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid solana_sdk::transaction::Transaction: index out of bounds"},"id":1}"#.to_string(), ) ); let mut bad_transaction = system_transaction::transfer( @@ -5919,7 +6050,7 @@ pub mod tests { assert_eq!( res, Some( - r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid transaction: index out of bounds"},"id":1}"#.to_string(), + r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"invalid solana_sdk::transaction::Transaction: index out of bounds"},"id":1}"#.to_string(), ) ); } @@ -7615,66 +7746,68 @@ pub mod tests { fn test_worst_case_encoded_tx_goldens() { let ff_tx = vec![0xffu8; PACKET_DATA_SIZE]; let tx58 = bs58::encode(&ff_tx).into_string(); - assert_eq!(tx58.len(), WORST_CASE_BASE58_TX); + assert_eq!(tx58.len(), MAX_BASE58_SIZE); let tx64 = base64::encode(&ff_tx); - assert_eq!(tx64.len(), WORST_CASE_BASE64_TX); + assert_eq!(tx64.len(), MAX_BASE64_SIZE); } #[test] - fn test_deserialize_transaction_too_large_payloads_fail() { + fn test_decode_and_deserialize_too_large_payloads_fail() { // +2 because +1 still fits in base64 encoded worst-case let too_big = PACKET_DATA_SIZE + 2; let tx_ser = vec![0xffu8; too_big]; let tx58 = bs58::encode(&tx_ser).into_string(); let tx58_len = tx58.len(); let expect58 = Error::invalid_params(format!( - "encoded transaction too large: {} bytes (max: encoded/raw {}/{})", - tx58_len, WORST_CASE_BASE58_TX, PACKET_DATA_SIZE, + "encoded solana_sdk::transaction::Transaction too large: {} bytes (max: encoded/raw {}/{})", + tx58_len, MAX_BASE58_SIZE, PACKET_DATA_SIZE, )); assert_eq!( - deserialize_transaction(tx58, UiTransactionEncoding::Base58).unwrap_err(), + decode_and_deserialize::(tx58, UiTransactionEncoding::Base58).unwrap_err(), expect58 ); let tx64 = base64::encode(&tx_ser); let tx64_len = tx64.len(); let expect64 = Error::invalid_params(format!( - "encoded transaction too large: {} bytes (max: encoded/raw {}/{})", - tx64_len, WORST_CASE_BASE64_TX, PACKET_DATA_SIZE, + "encoded solana_sdk::transaction::Transaction too large: {} bytes (max: encoded/raw {}/{})", + tx64_len, MAX_BASE64_SIZE, PACKET_DATA_SIZE, )); assert_eq!( - deserialize_transaction(tx64, UiTransactionEncoding::Base64).unwrap_err(), + decode_and_deserialize::(tx64, UiTransactionEncoding::Base64).unwrap_err(), expect64 ); let too_big = PACKET_DATA_SIZE + 1; let tx_ser = vec![0x00u8; too_big]; let tx58 = bs58::encode(&tx_ser).into_string(); let expect = Error::invalid_params(format!( - "transaction too large: {} bytes (max: {} bytes)", + "encoded solana_sdk::transaction::Transaction too large: {} bytes (max: {} bytes)", too_big, PACKET_DATA_SIZE )); assert_eq!( - deserialize_transaction(tx58, UiTransactionEncoding::Base58).unwrap_err(), + decode_and_deserialize::(tx58, UiTransactionEncoding::Base58).unwrap_err(), expect ); let tx64 = base64::encode(&tx_ser); assert_eq!( - deserialize_transaction(tx64, UiTransactionEncoding::Base64).unwrap_err(), + decode_and_deserialize::(tx64, UiTransactionEncoding::Base64).unwrap_err(), expect ); } #[test] - fn test_deserialize_transaction_unsanitary() { + fn test_decode_and_deserialize_unsanitary() { let unsanitary_tx58 = "ju9xZWuDBX4pRxX2oZkTjxU5jB4SSTgEGhX8bQ8PURNzyzqKMPPpNvWihx8zUe\ FfrbVNoAaEsNKZvGzAnTDy5bhNT9kt6KFCTBixpvrLCzg4M5UdFUQYrn1gdgjX\ pLHxcaShD81xBNaFDgnA2nkkdHnKtZt4hVSfKAmw3VRZbjrZ7L2fKZBx21CwsG\ hD6onjM2M3qZW5C8J6d1pj41MxKmZgPBSha3MyKkNLkAGFASK" .to_string(); - let expect58 = - Error::invalid_params("invalid transaction: index out of bounds".to_string()); + let expect58 = Error::invalid_params( + "invalid solana_sdk::transaction::Transaction: index out of bounds".to_string(), + ); assert_eq!( - deserialize_transaction(unsanitary_tx58, UiTransactionEncoding::Base58).unwrap_err(), + decode_and_deserialize::(unsanitary_tx58, UiTransactionEncoding::Base58) + .unwrap_err(), expect58 ); } diff --git a/rpc/src/rpc_service.rs b/rpc/src/rpc_service.rs index 83cac96ab..e40c5aa2d 100644 --- a/rpc/src/rpc_service.rs +++ b/rpc/src/rpc_service.rs @@ -5,8 +5,8 @@ use { max_slots::MaxSlots, optimistically_confirmed_bank_tracker::OptimisticallyConfirmedBank, rpc::{ - rpc_accounts::*, rpc_bank::*, rpc_deprecated_v1_7::*, rpc_full::*, rpc_minimal::*, - rpc_obsolete_v1_7::*, *, + rpc_accounts::*, rpc_bank::*, rpc_deprecated_v1_7::*, rpc_deprecated_v1_8::*, + rpc_full::*, rpc_minimal::*, rpc_obsolete_v1_7::*, *, }, rpc_health::*, send_transaction_service::{LeaderInfo, SendTransactionService}, @@ -408,6 +408,7 @@ impl JsonRpcService { io.extend_with(rpc_accounts::AccountsDataImpl.to_delegate()); io.extend_with(rpc_full::FullImpl.to_delegate()); io.extend_with(rpc_deprecated_v1_7::DeprecatedV1_7Impl.to_delegate()); + io.extend_with(rpc_deprecated_v1_8::DeprecatedV1_8Impl.to_delegate()); } if obsolete_v1_7_api { io.extend_with(rpc_obsolete_v1_7::ObsoleteV1_7Impl.to_delegate()); diff --git a/rpc/src/transaction_status_service.rs b/rpc/src/transaction_status_service.rs index ce246961b..d28a94299 100644 --- a/rpc/src/transaction_status_service.rs +++ b/rpc/src/transaction_status_service.rs @@ -106,9 +106,11 @@ impl TransactionStatusService { let fee_calculator = nonce_rollback .map(|nonce_rollback| nonce_rollback.fee_calculator()) .unwrap_or_else(|| { + #[allow(deprecated)] bank.get_fee_calculator(&transaction.message().recent_blockhash) }) .expect("FeeCalculator must exist"); + #[allow(deprecated)] let fee = fee_calculator.calculate_fee(transaction.message()); let (writable_keys, readonly_keys) = transaction.message.get_account_keys_by_lock_type(); diff --git a/runtime/benches/bank.rs b/runtime/benches/bank.rs index b489c40bd..a061f83d9 100644 --- a/runtime/benches/bank.rs +++ b/runtime/benches/bank.rs @@ -54,7 +54,7 @@ pub fn create_builtin_transactions( .unwrap_or_else(|_| panic!("{}:{}", line!(), file!())); let instruction = create_invoke_instruction(rando0.pubkey(), program_id, &1u8); - let (blockhash, _fee_calculator) = bank_client.get_recent_blockhash().unwrap(); + let blockhash = bank_client.get_latest_blockhash().unwrap(); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); Transaction::new(&[&rando0], message, blockhash) }) @@ -76,7 +76,7 @@ pub fn create_native_loader_transactions( .unwrap_or_else(|_| panic!("{}:{}", line!(), file!())); let instruction = create_invoke_instruction(rando0.pubkey(), program_id, &1u8); - let (blockhash, _fee_calculator) = bank_client.get_recent_blockhash().unwrap(); + let blockhash = bank_client.get_latest_blockhash().unwrap(); let message = Message::new(&[instruction], Some(&mint_keypair.pubkey())); Transaction::new(&[&rando0], message, blockhash) }) diff --git a/runtime/src/accounts.rs b/runtime/src/accounts.rs index 140864fc2..230487990 100644 --- a/runtime/src/accounts.rs +++ b/runtime/src/accounts.rs @@ -450,11 +450,13 @@ impl Accounts { .as_ref() .map(|nonce_rollback| nonce_rollback.fee_calculator()) .unwrap_or_else(|| { + #[allow(deprecated)] hash_queue .get_fee_calculator(&tx.message().recent_blockhash) .cloned() }); let fee = if let Some(fee_calculator) = fee_calculator { + #[allow(deprecated)] fee_calculator.calculate_fee(tx.message()) } else { return (Err(TransactionError::BlockhashNotFound), None); @@ -1292,7 +1294,9 @@ mod tests { ); let fee_calculator = FeeCalculator::new(10); - assert_eq!(fee_calculator.calculate_fee(tx.message()), 10); + #[allow(deprecated)] + let fee = fee_calculator.calculate_fee(tx.message()); + assert_eq!(fee, 10); let loaded_accounts = load_accounts_with_fee(tx, &accounts, &fee_calculator, &mut error_counters); diff --git a/runtime/src/bank.rs b/runtime/src/bank.rs index 1ea0b320e..14016bbed 100644 --- a/runtime/src/bank.rs +++ b/runtime/src/bank.rs @@ -2667,15 +2667,25 @@ impl Bank { self.blockhash_queue.read().unwrap().last_hash() } + pub fn is_blockhash_valid(&self, hash: &Hash) -> bool { + let blockhash_queue = self.blockhash_queue.read().unwrap(); + blockhash_queue.check_hash(hash) + } + pub fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> u64 { self.rent_collector.rent.minimum_balance(data_len) } + #[deprecated( + since = "1.8.0", + note = "Please use `last_blockhash` and `get_fee_for_message` instead" + )] pub fn last_blockhash_with_fee_calculator(&self) -> (Hash, FeeCalculator) { let blockhash_queue = self.blockhash_queue.read().unwrap(); let last_hash = blockhash_queue.last_hash(); ( last_hash, + #[allow(deprecated)] blockhash_queue .get_fee_calculator(&last_hash) .unwrap() @@ -2683,15 +2693,26 @@ impl Bank { ) } + #[deprecated(since = "1.8.0", note = "Please use `get_fee_for_message` instead")] pub fn get_fee_calculator(&self, hash: &Hash) -> Option { let blockhash_queue = self.blockhash_queue.read().unwrap(); + #[allow(deprecated)] blockhash_queue.get_fee_calculator(hash).cloned() } + #[deprecated(since = "1.8.0", note = "Please use `get_fee_for_message` instead")] pub fn get_fee_rate_governor(&self) -> &FeeRateGovernor { &self.fee_rate_governor } + pub fn get_fee_for_message(&self, hash: &Hash, message: &Message) -> Option { + let blockhash_queue = self.blockhash_queue.read().unwrap(); + #[allow(deprecated)] + let fee_calculator = blockhash_queue.get_fee_calculator(hash)?; + #[allow(deprecated)] + Some(fee_calculator.calculate_fee(message)) + } + #[deprecated( since = "1.6.11", note = "Please use `get_blockhash_last_valid_block_height`" @@ -2714,18 +2735,36 @@ impl Bank { .map(|age| self.block_height + blockhash_queue.len() as u64 - age) } - pub fn confirmed_last_blockhash(&self) -> (Hash, FeeCalculator) { + #[deprecated( + since = "1.8.0", + note = "Please use `confirmed_last_blockhash` and `get_fee_for_message` instead" + )] + pub fn confirmed_last_blockhash_with_fee_calculator(&self) -> (Hash, FeeCalculator) { const NUM_BLOCKHASH_CONFIRMATIONS: usize = 3; let parents = self.parents(); if parents.is_empty() { + #[allow(deprecated)] self.last_blockhash_with_fee_calculator() } else { let index = NUM_BLOCKHASH_CONFIRMATIONS.min(parents.len() - 1); + #[allow(deprecated)] parents[index].last_blockhash_with_fee_calculator() } } + pub fn confirmed_last_blockhash(&self) -> Hash { + const NUM_BLOCKHASH_CONFIRMATIONS: usize = 3; + + let parents = self.parents(); + if parents.is_empty() { + self.last_blockhash() + } else { + let index = NUM_BLOCKHASH_CONFIRMATIONS.min(parents.len() - 1); + parents[index].last_blockhash() + } + } + /// Forget all signatures. Useful for benchmarking. pub fn clear_signatures(&self) { self.src.status_cache.write().unwrap().clear(); @@ -3400,6 +3439,7 @@ impl Bank { let blockhash = blockhash_queue.last_hash(); ( blockhash, + #[allow(deprecated)] blockhash_queue .get_fee_calculator(&blockhash) .cloned() @@ -3582,6 +3622,7 @@ impl Bank { .map(|maybe_fee_calculator| (maybe_fee_calculator, true)) .unwrap_or_else(|| { ( + #[allow(deprecated)] hash_queue .get_fee_calculator(&tx.message().recent_blockhash) .cloned(), @@ -3590,6 +3631,7 @@ impl Bank { }); let fee_calculator = fee_calculator.ok_or(TransactionError::BlockhashNotFound)?; + #[allow(deprecated)] let fee = fee_calculator.calculate_fee(tx.message()); let message = tx.message(); @@ -3658,6 +3700,7 @@ impl Bank { } let mut write_time = Measure::start("write_time"); + #[allow(deprecated)] self.rc.accounts.store_cached( self.slot(), sanitized_txs.as_transactions_iter(), @@ -8304,11 +8347,13 @@ pub(crate) mod tests { let mut bank = Bank::new_for_tests(&genesis_config); goto_end_of_slot(&mut bank); + #[allow(deprecated)] let (cheap_blockhash, cheap_fee_calculator) = bank.last_blockhash_with_fee_calculator(); assert_eq!(cheap_fee_calculator.lamports_per_signature, 0); let mut bank = Bank::new_from_parent(&Arc::new(bank), &leader, 1); goto_end_of_slot(&mut bank); + #[allow(deprecated)] let (expensive_blockhash, expensive_fee_calculator) = bank.last_blockhash_with_fee_calculator(); assert!( diff --git a/runtime/src/bank_client.rs b/runtime/src/bank_client.rs index 30b54c9b7..8dbdb4fc4 100644 --- a/runtime/src/bank_client.rs +++ b/runtime/src/bank_client.rs @@ -148,6 +148,7 @@ impl SyncClient for BankClient { } fn get_recent_blockhash(&self) -> Result<(Hash, FeeCalculator)> { + #[allow(deprecated)] Ok(self.bank.last_blockhash_with_fee_calculator()) } @@ -155,6 +156,7 @@ impl SyncClient for BankClient { &self, _commitment_config: CommitmentConfig, ) -> Result<(Hash, FeeCalculator, u64)> { + #[allow(deprecated)] let (blockhash, fee_calculator) = self.bank.last_blockhash_with_fee_calculator(); #[allow(deprecated)] let last_valid_slot = self @@ -165,10 +167,12 @@ impl SyncClient for BankClient { } fn get_fee_calculator_for_blockhash(&self, blockhash: &Hash) -> Result> { + #[allow(deprecated)] Ok(self.bank.get_fee_calculator(blockhash)) } fn get_fee_rate_governor(&self) -> Result { + #[allow(deprecated)] Ok(self.bank.get_fee_rate_governor().clone()) } @@ -258,9 +262,10 @@ impl SyncClient for BankClient { } fn get_new_blockhash(&self, blockhash: &Hash) -> Result<(Hash, FeeCalculator)> { - let (last_blockhash, fee_calculator) = self.get_recent_blockhash()?; - if last_blockhash != *blockhash { - Ok((last_blockhash, fee_calculator)) + #[allow(deprecated)] + let (recent_blockhash, fee_calculator) = self.get_recent_blockhash()?; + if recent_blockhash != *blockhash { + Ok((recent_blockhash, fee_calculator)) } else { Err(TransportError::IoError(io::Error::new( io::ErrorKind::Other, @@ -272,6 +277,53 @@ impl SyncClient for BankClient { fn get_epoch_info(&self) -> Result { Ok(self.bank.get_epoch_info()) } + + fn get_latest_blockhash(&self) -> Result { + Ok(self.bank.last_blockhash()) + } + + fn get_latest_blockhash_with_commitment( + &self, + _commitment_config: CommitmentConfig, + ) -> Result<(Hash, u64)> { + let blockhash = self.bank.last_blockhash(); + let last_valid_block_height = self + .bank + .get_blockhash_last_valid_block_height(&blockhash) + .expect("bank blockhash queue should contain blockhash"); + Ok((blockhash, last_valid_block_height)) + } + + fn is_blockhash_valid( + &self, + blockhash: &Hash, + _commitment_config: CommitmentConfig, + ) -> Result { + Ok(self.bank.is_blockhash_valid(blockhash)) + } + + fn get_fee_for_message(&self, blockhash: &Hash, message: &Message) -> Result { + self.bank + .get_fee_for_message(blockhash, message) + .ok_or_else(|| { + TransportError::IoError(io::Error::new( + io::ErrorKind::Other, + "Unable calculate fee", + )) + }) + } + + fn get_new_latest_blockhash(&self, blockhash: &Hash) -> Result { + let latest_blockhash = self.get_latest_blockhash()?; + if latest_blockhash != *blockhash { + Ok(latest_blockhash) + } else { + Err(TransportError::IoError(io::Error::new( + io::ErrorKind::Other, + "Unable to get new blockhash", + ))) + } + } } impl BankClient { diff --git a/runtime/src/blockhash_queue.rs b/runtime/src/blockhash_queue.rs index 1f0f1adc1..07e5afafd 100644 --- a/runtime/src/blockhash_queue.rs +++ b/runtime/src/blockhash_queue.rs @@ -46,6 +46,10 @@ impl BlockhashQueue { self.last_hash.expect("no hash has been set") } + #[deprecated( + since = "1.8.0", + note = "Please do not use, will no longer be available in the future" + )] pub fn get_fee_calculator(&self, hash: &Hash) -> Option<&FeeCalculator> { self.ages.get(hash).map(|hash_age| &hash_age.fee_calculator) } @@ -66,9 +70,8 @@ impl BlockhashQueue { } /// check if hash is valid - #[cfg(test)] - pub fn check_hash(&self, hash: Hash) -> bool { - self.ages.get(&hash).is_some() + pub fn check_hash(&self, hash: &Hash) -> bool { + self.ages.get(hash).is_some() } pub fn genesis_hash(&mut self, hash: &Hash, fee_calculator: &FeeCalculator) { @@ -148,9 +151,9 @@ mod tests { fn test_register_hash() { let last_hash = Hash::default(); let mut hash_queue = BlockhashQueue::new(100); - assert!(!hash_queue.check_hash(last_hash)); + assert!(!hash_queue.check_hash(&last_hash)); hash_queue.register_hash(&last_hash, &FeeCalculator::default()); - assert!(hash_queue.check_hash(last_hash)); + assert!(hash_queue.check_hash(&last_hash)); assert_eq!(hash_queue.hash_height(), 1); } @@ -163,12 +166,12 @@ mod tests { hash_queue.register_hash(&last_hash, &FeeCalculator::default()); } // Assert we're no longer able to use the oldest hash. - assert!(!hash_queue.check_hash(last_hash)); + assert!(!hash_queue.check_hash(&last_hash)); assert_eq!(None, hash_queue.check_hash_age(&last_hash, 0)); // Assert we are not able to use the oldest remaining hash. let last_valid_hash = hash(&serialize(&1).unwrap()); - assert!(hash_queue.check_hash(last_valid_hash)); + assert!(hash_queue.check_hash(&last_valid_hash)); assert_eq!(Some(false), hash_queue.check_hash_age(&last_valid_hash, 0)); } diff --git a/sdk/program/src/fee_calculator.rs b/sdk/program/src/fee_calculator.rs index f9a9f48bb..af448e9ef 100644 --- a/sdk/program/src/fee_calculator.rs +++ b/sdk/program/src/fee_calculator.rs @@ -27,11 +27,15 @@ impl FeeCalculator { } } + #[deprecated( + since = "1.8.0", + note = "Please do not use, will no longer be available in the future" + )] pub fn calculate_fee(&self, message: &Message) -> u64 { let mut num_secp256k1_signatures: u64 = 0; for instruction in &message.instructions { let program_index = instruction.program_id_index as usize; - // Transaction may not be sanitized here + // Message may not be sanitized here if program_index < message.account_keys.len() { let id = message.account_keys[program_index]; if secp256k1_program::check_id(&id) && !instruction.data.is_empty() { @@ -193,6 +197,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_fee_calculator_calculate_fee() { // Default: no fee. let message = Message::default(); @@ -216,6 +221,7 @@ mod tests { } #[test] + #[allow(deprecated)] fn test_fee_calculator_calculate_fee_secp256k1() { use crate::instruction::Instruction; let pubkey0 = Pubkey::new(&[0; 32]); diff --git a/sdk/src/client.rs b/sdk/src/client.rs index 15e3cfa7a..7bfc22942 100644 --- a/sdk/src/client.rs +++ b/sdk/src/client.rs @@ -81,9 +81,14 @@ pub trait SyncClient { fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> Result; /// Get recent blockhash + #[deprecated(since = "1.8.0", note = "Please use `get_latest_blockhash` instead")] fn get_recent_blockhash(&self) -> Result<(Hash, FeeCalculator)>; /// Get recent blockhash. Uses explicit commitment configuration. + #[deprecated( + since = "1.8.0", + note = "Please use `get_latest_blockhash_with_commitment` and `get_fee_for_message` instead" + )] fn get_recent_blockhash_with_commitment( &self, commitment_config: CommitmentConfig, @@ -91,9 +96,17 @@ pub trait SyncClient { /// Get `Some(FeeCalculator)` associated with `blockhash` if it is still in /// the BlockhashQueue`, otherwise `None` + #[deprecated( + since = "1.8.0", + note = "Please use `get_fee_for_message` or `is_blockhash_valid` instead" + )] fn get_fee_calculator_for_blockhash(&self, blockhash: &Hash) -> Result>; /// Get recent fee rate governor + #[deprecated( + since = "1.8.0", + note = "Please do not use, will no longer be available in the future" + )] fn get_fee_rate_governor(&self) -> Result; /// Get signature status. @@ -136,7 +149,29 @@ pub trait SyncClient { /// Poll to confirm a transaction. fn poll_for_signature(&self, signature: &Signature) -> Result<()>; + #[deprecated( + since = "1.8.0", + note = "Please use `get_new_latest_blockhash` instead" + )] fn get_new_blockhash(&self, blockhash: &Hash) -> Result<(Hash, FeeCalculator)>; + + /// Get last known blockhash + fn get_latest_blockhash(&self) -> Result; + + /// Get recent blockhash. Uses explicit commitment configuration. + fn get_latest_blockhash_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> Result<(Hash, u64)>; + + /// Check if the blockhash is valid + fn is_blockhash_valid(&self, blockhash: &Hash, commitment: CommitmentConfig) -> Result; + + /// Calculate the fee for a `Message` + fn get_fee_for_message(&self, blockhash: &Hash, message: &Message) -> Result; + + /// Get a new blockhash after the one specified + fn get_new_latest_blockhash(&self, blockhash: &Hash) -> Result; } pub trait AsyncClient { diff --git a/stake-accounts/src/main.rs b/stake-accounts/src/main.rs index 94cca8701..bbf680365 100644 --- a/stake-accounts/src/main.rs +++ b/stake-accounts/src/main.rs @@ -205,8 +205,7 @@ fn send_and_confirm_message( ) -> Result { let mut transaction = Transaction::new_unsigned(message); - let (blockhash, _fee_calculator) = - client.get_new_blockhash(&transaction.message().recent_blockhash)?; + let blockhash = client.get_new_latest_blockhash(&transaction.message().recent_blockhash)?; transaction.try_sign(signers, blockhash)?; if no_wait { diff --git a/tokens/src/commands.rs b/tokens/src/commands.rs index 11abba3a9..03ee118bb 100644 --- a/tokens/src/commands.rs +++ b/tokens/src/commands.rs @@ -19,7 +19,6 @@ use solana_client::{ rpc_client::RpcClient, rpc_config::RpcSendTransactionConfig, rpc_request::MAX_GET_SIGNATURE_STATUSES_QUERY_ITEMS, - rpc_response::Fees, }; use solana_sdk::{ clock::Slot, @@ -164,7 +163,7 @@ fn transfer( let create_instruction = system_instruction::transfer(&sender_keypair.pubkey(), to_pubkey, lamports); let message = Message::new(&[create_instruction], Some(&sender_keypair.pubkey())); - let (recent_blockhash, _fees) = client.get_recent_blockhash()?; + let recent_blockhash = client.get_latest_blockhash()?; Ok(Transaction::new( &[sender_keypair], message, @@ -387,13 +386,8 @@ fn send_messages( if args.dry_run { Ok((Transaction::new_unsigned(message), std::u64::MAX)) } else { - let Fees { - blockhash, - last_valid_block_height, - .. - } = client - .get_fees_with_commitment(CommitmentConfig::default())? - .value; + let (blockhash, last_valid_block_height) = + client.get_latest_blockhash_with_commitment(CommitmentConfig::default())?; let transaction = Transaction::new(&signers, message, blockhash); let config = RpcSendTransactionConfig { skip_preflight: true, @@ -448,14 +442,10 @@ fn distribute_allocations( &mut created_accounts, )?; - let num_signatures = messages - .iter() - .map(|message| message.header.num_required_signatures as usize) - .sum(); if args.spl_token_args.is_some() { - check_spl_token_balances(num_signatures, allocations, client, args, created_accounts)?; + check_spl_token_balances(&messages, allocations, client, args, created_accounts)?; } else { - check_payer_balances(num_signatures, allocations, client, args)?; + check_payer_balances(&messages, allocations, client, args)?; } send_messages(client, db, allocations, args, exit, messages, stake_extras)?; @@ -733,18 +723,21 @@ fn log_transaction_confirmations( } fn check_payer_balances( - num_signatures: usize, + messages: &[Message], allocations: &[Allocation], client: &RpcClient, args: &DistributeTokensArgs, ) -> Result<(), Error> { let mut undistributed_tokens: u64 = allocations.iter().map(|x| x.amount).sum(); - let (_blockhash, fee_calculator) = client.get_recent_blockhash()?; - let fees = fee_calculator - .lamports_per_signature - .checked_mul(num_signatures as u64) - .unwrap(); + let blockhash = client.get_latest_blockhash()?; + let fees = messages + .iter() + .map(|message| client.get_fee_for_message(&blockhash, message)) + .collect::, _>>() + .unwrap() + .iter() + .sum(); let (distribution_source, unlocked_sol_source) = if let Some(stake_args) = &args.stake_args { let total_unlocked_sol = allocations.len() as u64 * stake_args.unlocked_sol; @@ -988,7 +981,7 @@ pub fn test_process_create_stake_with_client(client: &RpcClient, sender_keypair: ); let message = Message::new(&instructions, Some(&sender_keypair.pubkey())); let signers = [&sender_keypair, &stake_account_keypair]; - let (blockhash, _fees) = client.get_recent_blockhash().unwrap(); + let blockhash = client.get_latest_blockhash().unwrap(); let transaction = Transaction::new(&signers, message, blockhash); client .send_and_confirm_transaction_with_spinner(&transaction) @@ -1110,7 +1103,7 @@ pub fn test_process_distribute_stake_with_client(client: &RpcClient, sender_keyp ); let message = Message::new(&instructions, Some(&sender_keypair.pubkey())); let signers = [&sender_keypair, &stake_account_keypair]; - let (blockhash, _fees) = client.get_recent_blockhash().unwrap(); + let blockhash = client.get_latest_blockhash().unwrap(); let transaction = Transaction::new(&signers, message, blockhash); client .send_and_confirm_transaction_with_spinner(&transaction) @@ -1210,12 +1203,24 @@ mod tests { use super::*; use solana_core::test_validator::TestValidator; use solana_sdk::{ + instruction::AccountMeta, signature::{read_keypair_file, write_keypair_file, Signer}, stake::instruction::StakeInstruction, }; use solana_streamer::socket::SocketAddrSpace; use solana_transaction_status::TransactionConfirmationStatus; + fn one_signer_message() -> Message { + Message::new( + &[Instruction::new_with_bytes( + Pubkey::new_unique(), + &[], + vec![AccountMeta::new(Pubkey::default(), true)], + )], + None, + ) + } + #[test] fn test_process_token_allocations() { let alice = Keypair::new(); @@ -1594,7 +1599,7 @@ mod tests { &sender_keypair_file, None, ); - check_payer_balances(1, &allocations, &client, &args).unwrap(); + check_payer_balances(&[one_signer_message()], &allocations, &client, &args).unwrap(); // Unfunded payer let unfunded_payer = Keypair::new(); @@ -1607,7 +1612,9 @@ mod tests { .unwrap() .into(); - let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); + let err_result = + check_payer_balances(&[one_signer_message()], &allocations, &client, &args) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!( sources, @@ -1644,7 +1651,9 @@ mod tests { args.fee_payer = read_keypair_file(&partially_funded_payer_keypair_file) .unwrap() .into(); - let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); + let err_result = + check_payer_balances(&[one_signer_message()], &allocations, &client, &args) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!( sources, @@ -1697,7 +1706,7 @@ mod tests { &sender_keypair_file, None, ); - check_payer_balances(1, &allocations, &client, &args).unwrap(); + check_payer_balances(&[one_signer_message()], &allocations, &client, &args).unwrap(); // Unfunded sender let unfunded_payer = Keypair::new(); @@ -1708,7 +1717,9 @@ mod tests { .into(); args.fee_payer = read_keypair_file(&sender_keypair_file).unwrap().into(); - let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); + let err_result = + check_payer_balances(&[one_signer_message()], &allocations, &client, &args) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::SystemAccount].into()); assert_eq!(amount, allocation_amount.to_string()); @@ -1722,7 +1733,9 @@ mod tests { .unwrap() .into(); - let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); + let err_result = + check_payer_balances(&[one_signer_message()], &allocations, &client, &args) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::FeePayer].into()); assert_eq!(amount, fees_in_sol.to_string()); @@ -1756,7 +1769,7 @@ mod tests { ); let message = Message::new(&instructions, Some(&sender_keypair.pubkey())); let signers = [sender_keypair, &stake_account_keypair]; - let (blockhash, _fees) = client.get_recent_blockhash().unwrap(); + let blockhash = client.get_latest_blockhash().unwrap(); let transaction = Transaction::new(&signers, message, blockhash); client .send_and_confirm_transaction_with_spinner(&transaction) @@ -1809,7 +1822,7 @@ mod tests { &sender_keypair_file, Some(stake_args), ); - check_payer_balances(1, &allocations, &client, &args).unwrap(); + check_payer_balances(&[one_signer_message()], &allocations, &client, &args).unwrap(); // Underfunded stake-account let expensive_allocation_amount = 5000.0; @@ -1818,8 +1831,13 @@ mod tests { amount: sol_to_lamports(expensive_allocation_amount), lockup_date: "".to_string(), }]; - let err_result = - check_payer_balances(1, &expensive_allocations, &client, &args).unwrap_err(); + let err_result = check_payer_balances( + &[one_signer_message()], + &expensive_allocations, + &client, + &args, + ) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::StakeAccount].into()); assert_eq!( @@ -1841,7 +1859,9 @@ mod tests { .unwrap() .into(); - let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); + let err_result = + check_payer_balances(&[one_signer_message()], &allocations, &client, &args) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!( sources, @@ -1878,7 +1898,9 @@ mod tests { args.fee_payer = read_keypair_file(&partially_funded_payer_keypair_file) .unwrap() .into(); - let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); + let err_result = + check_payer_balances(&[one_signer_message()], &allocations, &client, &args) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!( sources, @@ -1938,7 +1960,7 @@ mod tests { &sender_keypair_file, Some(stake_args), ); - check_payer_balances(1, &allocations, &client, &args).unwrap(); + check_payer_balances(&[one_signer_message()], &allocations, &client, &args).unwrap(); // Unfunded sender let unfunded_payer = Keypair::new(); @@ -1949,7 +1971,9 @@ mod tests { .into(); args.fee_payer = read_keypair_file(&sender_keypair_file).unwrap().into(); - let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); + let err_result = + check_payer_balances(&[one_signer_message()], &allocations, &client, &args) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::SystemAccount].into()); assert_eq!(amount, unlocked_sol.to_string()); @@ -1963,7 +1987,9 @@ mod tests { .unwrap() .into(); - let err_result = check_payer_balances(1, &allocations, &client, &args).unwrap_err(); + let err_result = + check_payer_balances(&[one_signer_message()], &allocations, &client, &args) + .unwrap_err(); if let Error::InsufficientFunds(sources, amount) = err_result { assert_eq!(sources, vec![FundingSource::FeePayer].into()); assert_eq!(amount, fees_in_sol.to_string()); diff --git a/tokens/src/spl_token.rs b/tokens/src/spl_token.rs index a5a2e1136..5e0627737 100644 --- a/tokens/src/spl_token.rs +++ b/tokens/src/spl_token.rs @@ -8,7 +8,7 @@ use solana_account_decoder::parse_token::{ spl_token_v2_0_pubkey, }; use solana_client::rpc_client::RpcClient; -use solana_sdk::{instruction::Instruction, native_token::lamports_to_sol}; +use solana_sdk::{instruction::Instruction, message::Message, native_token::lamports_to_sol}; use solana_transaction_status::parse_token::spl_token_v2_0_instruction; use spl_associated_token_account_v1_0::{ create_associated_token_account, get_associated_token_address, @@ -85,7 +85,7 @@ pub fn build_spl_token_instructions( } pub fn check_spl_token_balances( - num_signatures: usize, + messages: &[Message], allocations: &[Allocation], client: &RpcClient, args: &DistributeTokensArgs, @@ -97,11 +97,14 @@ pub fn check_spl_token_balances( .expect("spl_token_args must be some"); let allocation_amount: u64 = allocations.iter().map(|x| x.amount).sum(); - let fee_calculator = client.get_recent_blockhash()?.1; - let fees = fee_calculator - .lamports_per_signature - .checked_mul(num_signatures as u64) - .unwrap(); + let blockhash = client.get_latest_blockhash()?; + let fees: u64 = messages + .iter() + .map(|message| client.get_fee_for_message(&blockhash, message)) + .collect::, _>>() + .unwrap() + .iter() + .sum(); let token_account_rent_exempt_balance = client.get_minimum_balance_for_rent_exemption(SplTokenAccount::LEN)?; diff --git a/watchtower/src/main.rs b/watchtower/src/main.rs index 3dd06dd8a..b168bda9e 100644 --- a/watchtower/src/main.rs +++ b/watchtower/src/main.rs @@ -183,7 +183,7 @@ fn get_cluster_info( rpc_client: &RpcClient, ) -> client_error::Result<(u64, Hash, RpcVoteAccountStatus, HashMap)> { let transaction_count = rpc_client.get_transaction_count()?; - let recent_blockhash = rpc_client.get_recent_blockhash()?.0; + let recent_blockhash = rpc_client.get_latest_blockhash()?; let vote_accounts = rpc_client.get_vote_accounts()?; let mut validator_balances = HashMap::new();