diff --git a/archiver/src/main.rs b/archiver/src/main.rs index 23cbec8c7b..3489aaa817 100644 --- a/archiver/src/main.rs +++ b/archiver/src/main.rs @@ -1,13 +1,15 @@ use clap::{crate_description, crate_name, crate_version, App, Arg}; use console::style; -use solana_core::archiver::Archiver; -use solana_core::cluster_info::{Node, VALIDATOR_PORT_RANGE}; -use solana_core::contact_info::ContactInfo; -use solana_sdk::signature::{read_keypair_file, Keypair, KeypairUtil}; -use std::net::SocketAddr; -use std::path::PathBuf; -use std::process::exit; -use std::sync::Arc; +use solana_core::{ + archiver::Archiver, + cluster_info::{Node, VALIDATOR_PORT_RANGE}, + contact_info::ContactInfo, +}; +use solana_sdk::{ + commitment_config::CommitmentConfig, + signature::{read_keypair_file, Keypair, KeypairUtil}, +}; +use std::{net::SocketAddr, path::PathBuf, process::exit, sync::Arc}; // Return an error if a keypair file cannot be parsed. fn is_keypair(string: String) -> Result<(), String> { @@ -118,6 +120,7 @@ fn main() { entrypoint_info, Arc::new(keypair), Arc::new(storage_keypair), + CommitmentConfig::recent(), ) .unwrap(); diff --git a/bench-exchange/src/bench.rs b/bench-exchange/src/bench.rs index 00ce6b7e76..693a68e262 100644 --- a/bench-exchange/src/bench.rs +++ b/bench-exchange/src/bench.rs @@ -8,31 +8,35 @@ use rayon::prelude::*; use solana_client::perf_utils::{sample_txs, SampleStats}; use solana_core::gen_keys::GenKeys; use solana_drone::drone::request_airdrop_transaction; -use solana_exchange_api::exchange_instruction; -use solana_exchange_api::exchange_state::*; -use solana_exchange_api::id; +use solana_exchange_api::{exchange_instruction, exchange_state::*, id}; use solana_genesis::Base64Account; use solana_metrics::datapoint_info; -use solana_sdk::client::Client; -use solana_sdk::client::SyncClient; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::{Keypair, KeypairUtil}; -use solana_sdk::timing::{duration_as_ms, duration_as_s}; -use solana_sdk::transaction::Transaction; -use solana_sdk::{system_instruction, system_program}; -use std::cmp; -use std::collections::{HashMap, VecDeque}; -use std::fs::File; -use std::io::prelude::*; -use std::mem; -use std::net::SocketAddr; -use std::path::Path; -use std::process::exit; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::{Arc, RwLock}; -use std::thread::{sleep, Builder}; -use std::time::{Duration, Instant}; +use solana_sdk::{ + client::{Client, SyncClient}, + commitment_config::CommitmentConfig, + pubkey::Pubkey, + signature::{Keypair, KeypairUtil}, + timing::{duration_as_ms, duration_as_s}, + transaction::Transaction, + {system_instruction, system_program}, +}; +use std::{ + cmp, + collections::{HashMap, VecDeque}, + fs::File, + io::prelude::*, + mem, + net::SocketAddr, + path::Path, + process::exit, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + mpsc::{channel, Receiver, Sender}, + Arc, RwLock, + }, + thread::{sleep, Builder}, + time::{Duration, Instant}, +}; // TODO Chunk length as specified results in a bunch of failures, divide by 10 helps... // Assume 4MB network buffers, and 512 byte packets @@ -380,7 +384,10 @@ fn swapper( let mut tries = 0; let mut trade_index = 0; while client - .get_balance(&trade_infos[trade_index].trade_account) + .get_balance_with_commitment( + &trade_infos[trade_index].trade_account, + CommitmentConfig::recent(), + ) .unwrap_or(0) == 0 { @@ -434,7 +441,7 @@ fn swapper( account_group = (account_group + 1) % account_groups as usize; let (blockhash, _fee_calculator) = client - .get_recent_blockhash() + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) .expect("Failed to get blockhash"); let to_swap_txs: Vec<_> = to_swap .par_iter() @@ -562,7 +569,7 @@ fn trader( account_group = (account_group + 1) % account_groups as usize; let (blockhash, _fee_calculator) = client - .get_recent_blockhash() + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) .expect("Failed to get blockhash"); trades.chunks(chunk_size).for_each(|chunk| { @@ -638,7 +645,9 @@ where T: SyncClient + ?Sized, { for s in &tx.signatures { - if let Ok(Some(r)) = sync_client.get_signature_status(s) { + if let Ok(Some(r)) = + sync_client.get_signature_status_with_commitment(s, CommitmentConfig::recent()) + { match r { Ok(_) => { return true; @@ -659,12 +668,15 @@ fn verify_funding_transfer( ) -> bool { if verify_transaction(client, tx) { for a in &tx.message().account_keys[1..] { - if client.get_balance(a).unwrap_or(0) >= amount { + if client + .get_balance_with_commitment(a, CommitmentConfig::recent()) + .unwrap_or(0) + >= amount + { return true; } } } - false } @@ -742,8 +754,9 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc], to_fund_txs.len(), ); - let (blockhash, _fee_calculator) = - client.get_recent_blockhash().expect("blockhash"); + let (blockhash, _fee_calculator) = client + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) + .expect("blockhash"); to_fund_txs.par_iter_mut().for_each(|(k, tx)| { tx.sign(&[*k], blockhash); }); @@ -780,7 +793,11 @@ pub fn fund_keys(client: &dyn Client, source: &Keypair, dests: &[Arc], }); funded.append(&mut new_funded); funded.retain(|(k, b)| { - client.get_balance(&k.pubkey()).unwrap_or(0) > lamports && *b > lamports + client + .get_balance_with_commitment(&k.pubkey(), CommitmentConfig::recent()) + .unwrap_or(0) + > lamports + && *b > lamports }); debug!(" Funded: {} left: {}", funded.len(), notfunded.len()); } @@ -819,7 +836,7 @@ pub fn create_token_accounts(client: &dyn Client, signers: &[Arc], acco let mut retries = 0; while !to_create_txs.is_empty() { let (blockhash, _fee_calculator) = client - .get_recent_blockhash() + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) .expect("Failed to get blockhash"); to_create_txs.par_iter_mut().for_each(|(k, tx)| { let kp: &Keypair = k; @@ -863,7 +880,11 @@ pub fn create_token_accounts(client: &dyn Client, signers: &[Arc], acco let mut new_notfunded: Vec<(&Arc, &Pubkey)> = vec![]; for f in ¬funded { - if client.get_balance(&f.1).unwrap_or(0) == 0 { + if client + .get_balance_with_commitment(&f.1, CommitmentConfig::recent()) + .unwrap_or(0) + == 0 + { new_notfunded.push(*f) } } @@ -920,7 +941,7 @@ fn generate_keypairs(num: u64) -> Vec { } pub fn airdrop_lamports(client: &dyn Client, drone_addr: &SocketAddr, id: &Keypair, amount: u64) { - let balance = client.get_balance(&id.pubkey()); + let balance = client.get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent()); let balance = balance.unwrap_or(0); if balance >= amount { return; @@ -938,19 +959,26 @@ pub fn airdrop_lamports(client: &dyn Client, drone_addr: &SocketAddr, id: &Keypa let mut tries = 0; loop { let (blockhash, _fee_calculator) = client - .get_recent_blockhash() + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) .expect("Failed to get blockhash"); match request_airdrop_transaction(&drone_addr, &id.pubkey(), amount_to_drop, blockhash) { Ok(transaction) => { let signature = client.async_send_transaction(transaction).unwrap(); for _ in 0..30 { - if let Ok(Some(_)) = client.get_signature_status(&signature) { + if let Ok(Some(_)) = client.get_signature_status_with_commitment( + &signature, + CommitmentConfig::recent(), + ) { break; } sleep(Duration::from_millis(100)); } - if client.get_balance(&id.pubkey()).unwrap_or(0) >= amount { + if client + .get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent()) + .unwrap_or(0) + >= amount + { break; } } diff --git a/bench-tps/src/bench.rs b/bench-tps/src/bench.rs index 66de7ea7f7..e9478e6a03 100644 --- a/bench-tps/src/bench.rs +++ b/bench-tps/src/bench.rs @@ -1,5 +1,3 @@ -use solana_metrics; - use crate::cli::Config; use log::*; use rayon::prelude::*; @@ -9,10 +7,11 @@ use solana_drone::drone::request_airdrop_transaction; #[cfg(feature = "move")] use solana_librapay_api::{create_genesis, upload_mint_program, upload_payment_program}; use solana_measure::measure::Measure; -use solana_metrics::datapoint_debug; +use solana_metrics::{self, datapoint_debug}; use solana_sdk::{ client::Client, clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, MAX_PROCESSING_AGE}, + commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, hash::Hash, pubkey::Pubkey, @@ -55,7 +54,7 @@ type LibraKeys = (Keypair, Pubkey, Pubkey, Vec); fn get_recent_blockhash(client: &T) -> (Hash, FeeCalculator) { loop { - match client.get_recent_blockhash() { + match client.get_recent_blockhash_with_commitment(CommitmentConfig::recent()) { Ok((blockhash, fee_calculator)) => return (blockhash, fee_calculator), Err(err) => { info!("Couldn't get recent blockhash: {:?}", err); @@ -451,7 +450,11 @@ fn do_tx_transfers( fn verify_funding_transfer(client: &T, tx: &Transaction, amount: u64) -> bool { for a in &tx.message().account_keys[1..] { - if client.get_balance(a).unwrap_or(0) >= amount { + if client + .get_balance_with_commitment(a, CommitmentConfig::recent()) + .unwrap_or(0) + >= amount + { return true; } } @@ -666,10 +669,12 @@ pub fn airdrop_lamports( } }; - let current_balance = client.get_balance(&id.pubkey()).unwrap_or_else(|e| { - info!("airdrop error {}", e); - starting_balance - }); + let current_balance = client + .get_balance_with_commitment(&id.pubkey(), CommitmentConfig::recent()) + .unwrap_or_else(|e| { + info!("airdrop error {}", e); + starting_balance + }); info!("current balance {}...", current_balance); metrics_submit_lamport_balance(current_balance); @@ -815,7 +820,11 @@ fn fund_move_keys( let create_len = 8; let mut funding_time = Measure::start("funding_time"); for (i, keys) in keypairs.chunks(create_len).enumerate() { - if client.get_balance(&keys[0].pubkey()).unwrap_or(0) > 0 { + if client + .get_balance_with_commitment(&keys[0].pubkey(), CommitmentConfig::recent()) + .unwrap_or(0) + > 0 + { // already created these accounts. break; } @@ -853,7 +862,9 @@ fn fund_move_keys( client.send_message(&[funding_key], tx.message).unwrap(); let mut balance = 0; for _ in 0..20 { - if let Ok(balance_) = client.get_balance(&funding_keys[0].pubkey()) { + if let Ok(balance_) = client + .get_balance_with_commitment(&funding_keys[0].pubkey(), CommitmentConfig::recent()) + { if balance_ > 0 { balance = balance_; break; @@ -1078,7 +1089,12 @@ mod tests { generate_and_fund_keypairs(&client, None, &id, tx_count, lamports, false).unwrap(); for kp in &keypairs { - assert_eq!(client.get_balance(&kp.pubkey()).unwrap(), lamports); + assert_eq!( + client + .get_balance_with_commitment(&kp.pubkey(), CommitmentConfig::recent()) + .unwrap(), + lamports + ); } } @@ -1096,7 +1112,7 @@ mod tests { generate_and_fund_keypairs(&client, None, &id, tx_count, lamports, false).unwrap(); let max_fee = client - .get_recent_blockhash() + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) .unwrap() .1 .max_lamports_per_signature; diff --git a/book/src/api-reference/jsonrpc-api.md b/book/src/api-reference/jsonrpc-api.md index 75f8d70577..e403abc361 100644 --- a/book/src/api-reference/jsonrpc-api.md +++ b/book/src/api-reference/jsonrpc-api.md @@ -78,6 +78,24 @@ Requests can be sent in batches by sending an array of JSON-RPC request objects * Signature: An Ed25519 signature of a chunk of data. * Transaction: A Solana instruction signed by a client key-pair. +## Configuring State Commitment + +Solana nodes choose which bank state to query based on a commitment requirement +set by the client. Clients may specify either: +* `{"commitment":"max"}` - the node will query the most recent bank having reached `MAX_LOCKOUT_HISTORY` confirmations +* `{"commitment":"recent"}` - the node will query its most recent bank state + +The commitment parameter should be included as the last element in the `params` array: + +```bash +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getBalance", "params":["83astBRguLMdt2h5U1Tpdq5tjFoJ6noeGwaY3mDLVcri",{"commitment":"max"}]}' 192.168.1.88:8899 +``` + +#### Default: +If commitment configuration is not provided, the node will default to `"commitment":"max"` + +Only methods that query bank state accept the commitment parameter. They are indicated in the API Reference below. + ## JSON RPC API Reference ### confirmTransaction @@ -91,6 +109,7 @@ Returns a transaction receipt #### Results: * `boolean` - Transaction status, true if Transaction is confirmed +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Example: @@ -109,6 +128,7 @@ Returns all information associated with the account of provided Pubkey #### Parameters: * `string` - Pubkey of account to query, as base-58 encoded string +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -136,6 +156,7 @@ Returns the balance of the account of provided Pubkey #### Parameters: * `string` - Pubkey of account to query, as base-58 encoded string +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -212,7 +233,7 @@ Returns information about the current epoch #### Parameters: -None +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -288,7 +309,7 @@ Returns the leader schedule for the current epoch #### Parameters: -None +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -311,6 +332,7 @@ Returns minimum balance required to make account rent exempt. #### Parameters: * `integer` - account data length, as unsigned integer +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -333,6 +355,7 @@ Returns the current number of blocks since signature has been confirmed. #### Parameters: * `string` - Signature of Transaction to confirm, as base-58 encoded string +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -355,6 +378,7 @@ Returns all accounts owned by the provided program Pubkey #### Parameters: * `string` - Pubkey of program, as base-58 encoded string +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -382,7 +406,7 @@ Returns a recent block hash from the ledger, and a fee schedule that can be used #### Parameters: -None +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -408,6 +432,7 @@ Returns the status of a given signature. This method is similar to [confirmTrans #### Parameters: * `string` - Signature of Transaction to confirm, as base-58 encoded string +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -432,7 +457,7 @@ Returns the current slot the node is processing #### Parameters: -None +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -454,7 +479,7 @@ Returns the current slot leader #### Parameters: -None +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -476,7 +501,7 @@ Returns the current storage segment size in terms of slots #### Parameters: -None +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -542,7 +567,7 @@ Returns the current Transaction count from the ledger #### Parameters: -None +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -569,6 +594,7 @@ None #### Results: * `integer` - Total supply, as unsigned 64-bit integer +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Example: @@ -609,7 +635,7 @@ Returns the account info and associated stake for all the voting accounts in the #### Parameters: -None +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: @@ -640,6 +666,7 @@ Requests an airdrop of lamports to a Pubkey * `string` - Pubkey of account to receive lamports, as base-58 encoded string * `integer` - lamports, as a signed 64-bit integer +* `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) (used for retrieving blockhash and verifying airdrop success) #### Results: diff --git a/ci/localnet-sanity.sh b/ci/localnet-sanity.sh index 42dcbe1e55..a79ca40962 100755 --- a/ci/localnet-sanity.sh +++ b/ci/localnet-sanity.sh @@ -364,7 +364,7 @@ while [[ $iteration -le $iterations ]]; do echo "--- Wallet sanity ($iteration)" ( set -x - timeout 60s scripts/wallet-sanity.sh --url http://127.0.0.1"$walletRpcPort" + timeout 90s scripts/wallet-sanity.sh --url http://127.0.0.1"$walletRpcPort" ) || flag_error iteration=$((iteration + 1)) diff --git a/cli/src/cluster_query.rs b/cli/src/cluster_query.rs index c3c4aad472..83194fa7ec 100644 --- a/cli/src/cluster_query.rs +++ b/cli/src/cluster_query.rs @@ -11,6 +11,7 @@ use serde_json::Value; use solana_client::{rpc_client::RpcClient, rpc_request::RpcVoteAccountInfo}; use solana_sdk::{ clock, + commitment_config::CommitmentConfig, hash::Hash, signature::{Keypair, KeypairUtil}, system_transaction, @@ -72,7 +73,7 @@ impl ClusterQuerySubCommands for App<'_, '_> { .long("timeout") .value_name("SECONDS") .takes_value(true) - .default_value("10") + .default_value("15") .help("Wait up to timeout seconds for transaction confirmation"), ), ) @@ -221,7 +222,10 @@ pub fn process_ping( Ok(signature) => { let transaction_sent = Instant::now(); loop { - let signature_status = rpc_client.get_signature_status(&signature)?; + let signature_status = rpc_client.get_signature_status_with_commitment( + &signature, + CommitmentConfig::recent(), + )?; let elapsed_time = Instant::now().duration_since(transaction_sent); if let Some(transaction_status) = signature_status { match transaction_status { diff --git a/cli/src/validator_info.rs b/cli/src/validator_info.rs index a4d44c8506..d9097c0de6 100644 --- a/cli/src/validator_info.rs +++ b/cli/src/validator_info.rs @@ -11,11 +11,14 @@ use serde_derive::{Deserialize, Serialize}; use serde_json::{Map, Value}; use solana_client::rpc_client::RpcClient; use solana_config_api::{config_instruction, get_config_data, ConfigKeys, ConfigState}; -use solana_sdk::account::Account; -use solana_sdk::message::Message; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::{Keypair, KeypairUtil}; -use solana_sdk::transaction::Transaction; +use solana_sdk::{ + account::Account, + commitment_config::CommitmentConfig, + message::Message, + pubkey::Pubkey, + signature::{Keypair, KeypairUtil}, + transaction::Transaction, +}; use std::error; pub const MAX_SHORT_FIELD_LENGTH: usize = 70; @@ -297,7 +300,9 @@ pub fn process_set_validator_info( }; // Check existence of validator-info account - let balance = rpc_client.poll_get_balance(&info_pubkey).unwrap_or(0); + let balance = rpc_client + .poll_get_balance_with_commitment(&info_pubkey, CommitmentConfig::default()) + .unwrap_or(0); let keys = vec![(id(), false), (config.keypair.pubkey(), true)]; let (message, signers): (Message, Vec<&Keypair>) = if balance == 0 { diff --git a/cli/tests/deploy.rs b/cli/tests/deploy.rs index 2d040d708c..31f5c414d6 100644 --- a/cli/tests/deploy.rs +++ b/cli/tests/deploy.rs @@ -1,14 +1,16 @@ -use serde_json::{json, Value}; +use serde_json::Value; use solana_cli::cli::{process_command, CliCommand, CliConfig}; use solana_client::rpc_client::RpcClient; -use solana_client::rpc_request::RpcRequest; use solana_core::validator::new_validator_for_tests; use solana_drone::drone::run_local_drone; -use solana_sdk::bpf_loader; -use std::fs::{remove_dir_all, File}; -use std::io::Read; -use std::path::PathBuf; -use std::sync::mpsc::channel; +use solana_sdk::{bpf_loader, commitment_config::CommitmentConfig, pubkey::Pubkey}; +use std::{ + fs::{remove_dir_all, File}, + io::Read, + path::PathBuf, + str::FromStr, + sync::mpsc::channel, +}; #[test] fn test_cli_deploy_program() { @@ -56,35 +58,20 @@ fn test_cli_deploy_program() { .unwrap() .as_str() .unwrap(); + let program_id = Pubkey::from_str(&program_id_str).unwrap(); - let params = json!([program_id_str]); - let account_info = rpc_client - .retry_make_rpc_request(&RpcRequest::GetAccountInfo, Some(params), 0) + let account = rpc_client + .get_account_with_commitment(&program_id, CommitmentConfig::recent()) .unwrap(); - let account_info_obj = account_info.as_object().unwrap(); - assert_eq!( - account_info_obj.get("lamports").unwrap().as_u64().unwrap(), - minimum_balance_for_rent_exemption - ); - let owner_array = account_info.get("owner").unwrap(); - assert_eq!(owner_array, &json!(bpf_loader::id())); - assert_eq!( - account_info_obj - .get("executable") - .unwrap() - .as_bool() - .unwrap(), - true - ); + assert_eq!(account.lamports, minimum_balance_for_rent_exemption); + assert_eq!(account.owner, bpf_loader::id()); + assert_eq!(account.executable, true); let mut file = File::open(pathbuf.to_str().unwrap().to_string()).unwrap(); let mut elf = Vec::new(); file.read_to_end(&mut elf).unwrap(); - assert_eq!( - account_info_obj.get("data").unwrap().as_array().unwrap(), - &elf - ); + assert_eq!(account.data, elf); server.close().unwrap(); remove_dir_all(ledger_path).unwrap(); diff --git a/client/src/generic_rpc_client_request.rs b/client/src/generic_rpc_client_request.rs index 590c30aa3b..aefcea0782 100644 --- a/client/src/generic_rpc_client_request.rs +++ b/client/src/generic_rpc_client_request.rs @@ -1,5 +1,5 @@ -use crate::client_error::ClientError; -use crate::rpc_request::RpcRequest; +use crate::{client_error::ClientError, rpc_request::RpcRequest}; +use solana_sdk::commitment_config::CommitmentConfig; pub(crate) trait GenericRpcClientRequest { fn send( @@ -7,5 +7,6 @@ pub(crate) trait GenericRpcClientRequest { request: &RpcRequest, params: Option, retries: usize, + commitment_config: Option, ) -> Result; } diff --git a/client/src/mock_rpc_client_request.rs b/client/src/mock_rpc_client_request.rs index d17ef7c9e4..38019e9c58 100644 --- a/client/src/mock_rpc_client_request.rs +++ b/client/src/mock_rpc_client_request.rs @@ -1,9 +1,13 @@ -use crate::client_error::ClientError; -use crate::generic_rpc_client_request::GenericRpcClientRequest; -use crate::rpc_request::RpcRequest; +use crate::{ + client_error::ClientError, generic_rpc_client_request::GenericRpcClientRequest, + rpc_request::RpcRequest, +}; use serde_json::{Number, Value}; -use solana_sdk::fee_calculator::FeeCalculator; -use solana_sdk::transaction::{self, TransactionError}; +use solana_sdk::{ + commitment_config::CommitmentConfig, + fee_calculator::FeeCalculator, + transaction::{self, TransactionError}, +}; pub const PUBKEY: &str = "7RoSF9fUmdphVCpabEoefH81WwrW7orsWonXWqTXkKV8"; pub const SIGNATURE: &str = @@ -25,6 +29,7 @@ impl GenericRpcClientRequest for MockRpcClientRequest { request: &RpcRequest, params: Option, _retries: usize, + _commitment_config: Option, ) -> Result { if self.url == "fails" { return Ok(Value::Null); diff --git a/client/src/perf_utils.rs b/client/src/perf_utils.rs index 0a1d2f7efe..6de5623dc9 100644 --- a/client/src/perf_utils.rs +++ b/client/src/perf_utils.rs @@ -1,10 +1,13 @@ use log::*; -use solana_sdk::client::Client; -use solana_sdk::timing::duration_as_s; -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::{Arc, RwLock}; -use std::thread::sleep; -use std::time::{Duration, Instant}; +use solana_sdk::{client::Client, commitment_config::CommitmentConfig, timing::duration_as_s}; +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, RwLock, + }, + thread::sleep, + time::{Duration, Instant}, +}; #[derive(Default)] pub struct SampleStats { @@ -29,7 +32,9 @@ pub fn sample_txs( let mut total_txs; let mut now = Instant::now(); let start_time = now; - let initial_txs = client.get_transaction_count().expect("transaction count"); + let initial_txs = client + .get_transaction_count_with_commitment(CommitmentConfig::recent()) + .expect("transaction count"); let mut last_txs = initial_txs; loop { @@ -37,7 +42,7 @@ pub fn sample_txs( let elapsed = now.elapsed(); now = Instant::now(); let mut txs; - match client.get_transaction_count() { + match client.get_transaction_count_with_commitment(CommitmentConfig::recent()) { Err(e) => { // ThinClient with multiple options should pick a better one now. info!("Couldn't get transaction count {:?}", e); diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index d8e9b0340f..0ad8a04d3d 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -1,14 +1,17 @@ -use crate::client_error::ClientError; -use crate::generic_rpc_client_request::GenericRpcClientRequest; -use crate::mock_rpc_client_request::MockRpcClientRequest; -use crate::rpc_client_request::RpcClientRequest; -use crate::rpc_request::{RpcEpochInfo, RpcRequest, RpcVoteAccountStatus}; +use crate::{ + client_error::ClientError, + generic_rpc_client_request::GenericRpcClientRequest, + mock_rpc_client_request::MockRpcClientRequest, + rpc_client_request::RpcClientRequest, + rpc_request::{RpcEpochInfo, RpcRequest, RpcVoteAccountStatus}, +}; use bincode::serialize; use log::*; use serde_json::{json, Value}; use solana_sdk::{ account::Account, clock::{Slot, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT}, + commitment_config::CommitmentConfig, epoch_schedule::EpochSchedule, fee_calculator::FeeCalculator, hash::Hash, @@ -54,10 +57,10 @@ impl RpcClient { pub fn send_transaction(&self, transaction: &Transaction) -> Result { let serialized = serialize(transaction).unwrap(); - let params = json!([serialized]); + let params = json!(serialized); let signature = self .client - .send(&RpcRequest::SendTransaction, Some(params), 5)?; + .send(&RpcRequest::SendTransaction, Some(params), 5, None)?; if signature.as_str().is_none() { Err(io::Error::new( io::ErrorKind::Other, @@ -73,19 +76,37 @@ impl RpcClient { &self, signature: &str, ) -> Result>, ClientError> { - let params = json!([signature.to_string()]); - let signature_status = - self.client - .send(&RpcRequest::GetSignatureStatus, Some(params), 5)?; + self.get_signature_status_with_commitment(signature, CommitmentConfig::default()) + } + + pub fn get_signature_status_with_commitment( + &self, + signature: &str, + commitment_config: CommitmentConfig, + ) -> Result>, ClientError> { + let params = json!(signature.to_string()); + let signature_status = self.client.send( + &RpcRequest::GetSignatureStatus, + Some(params), + 5, + commitment_config.ok(), + )?; let result: Option> = serde_json::from_value(signature_status).unwrap(); Ok(result) } pub fn get_slot(&self) -> io::Result { + self.get_slot_with_commitment(CommitmentConfig::default()) + } + + pub fn get_slot_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> io::Result { let response = self .client - .send(&RpcRequest::GetSlot, None, 0) + .send(&RpcRequest::GetSlot, None, 0, commitment_config.ok()) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -104,7 +125,7 @@ impl RpcClient { pub fn get_vote_accounts(&self) -> io::Result { let response = self .client - .send(&RpcRequest::GetVoteAccounts, None, 0) + .send(&RpcRequest::GetVoteAccounts, None, 0, None) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -123,7 +144,7 @@ impl RpcClient { pub fn get_epoch_info(&self) -> io::Result { let response = self .client - .send(&RpcRequest::GetEpochInfo, None, 0) + .send(&RpcRequest::GetEpochInfo, None, 0, None) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -142,7 +163,7 @@ impl RpcClient { pub fn get_epoch_schedule(&self) -> io::Result { let response = self .client - .send(&RpcRequest::GetEpochSchedule, None, 0) + .send(&RpcRequest::GetEpochSchedule, None, 0, None) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -161,7 +182,7 @@ impl RpcClient { pub fn get_inflation(&self) -> io::Result { let response = self .client - .send(&RpcRequest::GetInflation, None, 0) + .send(&RpcRequest::GetInflation, None, 0, None) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -180,7 +201,7 @@ impl RpcClient { pub fn get_version(&self) -> io::Result { let response = self .client - .send(&RpcRequest::GetVersion, None, 0) + .send(&RpcRequest::GetVersion, None, 0, None) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -203,7 +224,7 @@ impl RpcClient { ) -> Result { let mut send_retries = 20; loop { - let mut status_retries = 4; + let mut status_retries = 15; let signature_str = self.send_transaction(transaction)?; let status = loop { let status = self.get_signature_status(&signature_str)?; @@ -216,10 +237,8 @@ impl RpcClient { break status; } if cfg!(not(test)) { - // Retry ~twice during a slot - sleep(Duration::from_millis( - 500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND, - )); + // Retry twice a second + sleep(Duration::from_millis(500)); } }; send_retries = if let Some(result) = status.clone() { @@ -252,7 +271,7 @@ impl RpcClient { ) -> Result<(), Box> { let mut send_retries = 5; loop { - let mut status_retries = 4; + let mut status_retries = 15; // Send all transactions let mut transactions_signatures = vec![]; @@ -273,10 +292,8 @@ impl RpcClient { status_retries -= 1; if cfg!(not(test)) { - // Retry ~twice during a slot - sleep(Duration::from_millis( - 500 * DEFAULT_TICKS_PER_SLOT / DEFAULT_TICKS_PER_SECOND, - )); + // Retry twice a second + sleep(Duration::from_millis(500)); } transactions_signatures = transactions_signatures @@ -333,19 +350,30 @@ impl RpcClient { pubkey: &Pubkey, retries: usize, ) -> Result, Box> { - let params = json!([format!("{}", pubkey)]); + let params = json!(format!("{}", pubkey)); let res = self .client - .send(&RpcRequest::GetBalance, Some(params), retries)? + .send(&RpcRequest::GetBalance, Some(params), retries, None)? .as_u64(); Ok(res) } pub fn get_account(&self, pubkey: &Pubkey) -> io::Result { - let params = json!([format!("{}", pubkey)]); - let response = self - .client - .send(&RpcRequest::GetAccountInfo, Some(params), 0); + self.get_account_with_commitment(pubkey, CommitmentConfig::default()) + } + + pub fn get_account_with_commitment( + &self, + pubkey: &Pubkey, + commitment_config: CommitmentConfig, + ) -> io::Result { + let params = json!(format!("{}", pubkey)); + let response = self.client.send( + &RpcRequest::GetAccountInfo, + Some(params), + 0, + commitment_config.ok(), + ); response .and_then(|account_json| { @@ -366,13 +394,14 @@ impl RpcClient { } pub fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> io::Result { - let params = json!([data_len]); + let params = json!(data_len); let minimum_balance_json = self .client .send( &RpcRequest::GetMinimumBalanceForRentExemption, Some(params), 0, + None, ) .map_err(|err| { io::Error::new( @@ -398,18 +427,25 @@ impl RpcClient { Ok(minimum_balance) } - /// Request the balance of the user holding `pubkey`. This method blocks - /// until the server sends a response. If the response packet is dropped - /// by the network, this method will hang indefinitely. + /// Request the balance of the account `pubkey`. pub fn get_balance(&self, pubkey: &Pubkey) -> io::Result { - self.get_account(pubkey).map(|account| account.lamports) + self.get_balance_with_commitment(pubkey, CommitmentConfig::default()) + } + + pub fn get_balance_with_commitment( + &self, + pubkey: &Pubkey, + commitment_config: CommitmentConfig, + ) -> io::Result { + self.get_account_with_commitment(pubkey, commitment_config) + .map(|account| account.lamports) } pub fn get_program_accounts(&self, pubkey: &Pubkey) -> io::Result> { - let params = json!([format!("{}", pubkey)]); + let params = json!(format!("{}", pubkey)); let response = self .client - .send(&RpcRequest::GetProgramAccounts, Some(params), 0) + .send(&RpcRequest::GetProgramAccounts, Some(params), 0, None) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -438,12 +474,23 @@ impl RpcClient { Ok(pubkey_accounts) } - /// Request the transaction count. If the response packet is dropped by the network, - /// this method will try again 5 times. + /// Request the transaction count. pub fn get_transaction_count(&self) -> io::Result { + self.get_transaction_count_with_commitment(CommitmentConfig::default()) + } + + pub fn get_transaction_count_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> io::Result { let response = self .client - .send(&RpcRequest::GetTransactionCount, None, 0) + .send( + &RpcRequest::GetTransactionCount, + None, + 0, + commitment_config.ok(), + ) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -460,9 +507,21 @@ impl RpcClient { } pub fn get_recent_blockhash(&self) -> io::Result<(Hash, FeeCalculator)> { + self.get_recent_blockhash_with_commitment(CommitmentConfig::default()) + } + + pub fn get_recent_blockhash_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> io::Result<(Hash, FeeCalculator)> { let response = self .client - .send(&RpcRequest::GetRecentBlockhash, None, 0) + .send( + &RpcRequest::GetRecentBlockhash, + None, + 0, + commitment_config.ok(), + ) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -518,7 +577,7 @@ impl RpcClient { pub fn get_genesis_blockhash(&self) -> io::Result { let response = self .client - .send(&RpcRequest::GetGenesisBlockhash, None, 0) + .send(&RpcRequest::GetGenesisBlockhash, None, 0, None) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -542,15 +601,16 @@ impl RpcClient { Ok(blockhash) } - pub fn poll_balance_with_timeout( + pub fn poll_balance_with_timeout_and_commitment( &self, pubkey: &Pubkey, polling_frequency: &Duration, timeout: &Duration, + commitment_config: CommitmentConfig, ) -> io::Result { let now = Instant::now(); loop { - match self.get_balance(&pubkey) { + match self.get_balance_with_commitment(&pubkey, commitment_config.clone()) { Ok(bal) => { return Ok(bal); } @@ -564,14 +624,29 @@ impl RpcClient { } } - pub fn poll_get_balance(&self, pubkey: &Pubkey) -> io::Result { - self.poll_balance_with_timeout(pubkey, &Duration::from_millis(100), &Duration::from_secs(1)) + pub fn poll_get_balance_with_commitment( + &self, + pubkey: &Pubkey, + commitment_config: CommitmentConfig, + ) -> io::Result { + self.poll_balance_with_timeout_and_commitment( + pubkey, + &Duration::from_millis(100), + &Duration::from_secs(1), + commitment_config, + ) } - pub fn wait_for_balance(&self, pubkey: &Pubkey, expected_balance: Option) -> Option { + pub fn wait_for_balance_with_commitment( + &self, + pubkey: &Pubkey, + expected_balance: Option, + commitment_config: CommitmentConfig, + ) -> Option { const LAST: usize = 30; for run in 0..LAST { - let balance_result = self.poll_get_balance(pubkey); + let balance_result = + self.poll_get_balance_with_commitment(pubkey, commitment_config.clone()); if expected_balance.is_none() { return balance_result.ok(); } @@ -593,8 +668,23 @@ impl RpcClient { /// Poll the server to confirm a transaction. pub fn poll_for_signature(&self, signature: &Signature) -> io::Result<()> { + self.poll_for_signature_with_commitment(signature, CommitmentConfig::default()) + } + + /// Poll the server to confirm a transaction. + pub fn poll_for_signature_with_commitment( + &self, + signature: &Signature, + commitment_config: CommitmentConfig, + ) -> io::Result<()> { let now = Instant::now(); - while !self.check_signature(signature) { + loop { + if let Ok(Some(_)) = self.get_signature_status_with_commitment( + &signature.to_string(), + commitment_config.clone(), + ) { + break; + } if now.elapsed().as_secs() > 15 { // TODO: Return a better error. return Err(io::Error::new(io::ErrorKind::Other, "signature not found")); @@ -607,12 +697,15 @@ impl RpcClient { /// Check a signature in the bank. pub fn check_signature(&self, signature: &Signature) -> bool { trace!("check_signature: {:?}", signature); - let params = json!([format!("{}", signature)]); + let params = json!(format!("{}", signature)); for _ in 0..30 { - let response = - self.client - .send(&RpcRequest::ConfirmTransaction, Some(params.clone()), 0); + let response = self.client.send( + &RpcRequest::ConfirmTransaction, + Some(params.clone()), + 0, + Some(CommitmentConfig::recent()), + ); match response { Ok(confirmation) => { @@ -666,7 +759,7 @@ impl RpcClient { debug!("check_confirmations request failed: {:?}", err); } }; - if now.elapsed().as_secs() > 15 { + if now.elapsed().as_secs() > 20 { info!( "signature {} confirmed {} out of {} failed after {} ms", signature, @@ -690,13 +783,14 @@ impl RpcClient { &self, sig: &Signature, ) -> io::Result { - let params = json!([format!("{}", sig)]); + let params = json!(format!("{}", sig)); let response = self .client .send( &RpcRequest::GetNumBlocksSinceSignatureConfirmation, Some(params.clone()), 1, + CommitmentConfig::recent().ok(), ) .map_err(|err| { io::Error::new( @@ -721,7 +815,7 @@ impl RpcClient { pub fn validator_exit(&self) -> io::Result { let response = self .client - .send(&RpcRequest::ValidatorExit, None, 0) + .send(&RpcRequest::ValidatorExit, None, 0, None) .map_err(|err| { io::Error::new( io::ErrorKind::Other, @@ -742,8 +836,9 @@ impl RpcClient { request: &RpcRequest, params: Option, retries: usize, + commitment: Option, ) -> Result { - self.client.send(request, params, retries) + self.client.send(request, params, retries, commitment) } } @@ -763,11 +858,12 @@ mod tests { use jsonrpc_http_server::{AccessControlAllowOrigin, DomainsValidation, ServerBuilder}; use serde_json::Number; use solana_logger; - use solana_sdk::signature::{Keypair, KeypairUtil}; - use solana_sdk::system_transaction; - use solana_sdk::transaction::TransactionError; - use std::sync::mpsc::channel; - use std::thread; + use solana_sdk::{ + signature::{Keypair, KeypairUtil}, + system_transaction, + transaction::TransactionError, + }; + use std::{sync::mpsc::channel, thread}; #[test] fn test_make_rpc_request() { @@ -808,10 +904,12 @@ mod tests { &RpcRequest::GetBalance, Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"])), 0, + None, ); assert_eq!(balance.unwrap().as_u64().unwrap(), 50); - let blockhash = rpc_client.retry_make_rpc_request(&RpcRequest::GetRecentBlockhash, None, 0); + let blockhash = + rpc_client.retry_make_rpc_request(&RpcRequest::GetRecentBlockhash, None, 0, None); assert_eq!( blockhash.unwrap().as_str().unwrap(), "deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx" @@ -822,6 +920,7 @@ mod tests { &RpcRequest::GetRecentBlockhash, Some(json!("parameter")), 0, + None, ); assert_eq!(blockhash.is_err(), true); } @@ -860,6 +959,7 @@ mod tests { &RpcRequest::GetBalance, Some(json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhw"])), 10, + None, ); assert_eq!(balance.unwrap().as_u64().unwrap(), 5); } diff --git a/client/src/rpc_client_request.rs b/client/src/rpc_client_request.rs index ce7fb0d292..b7b5186c0a 100644 --- a/client/src/rpc_client_request.rs +++ b/client/src/rpc_client_request.rs @@ -1,11 +1,15 @@ -use crate::client_error::ClientError; -use crate::generic_rpc_client_request::GenericRpcClientRequest; -use crate::rpc_request::{RpcError, RpcRequest}; +use crate::{ + client_error::ClientError, + generic_rpc_client_request::GenericRpcClientRequest, + rpc_request::{RpcError, RpcRequest}, +}; use log::*; use reqwest::{self, header::CONTENT_TYPE}; -use solana_sdk::clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT}; -use std::thread::sleep; -use std::time::Duration; +use solana_sdk::{ + clock::{DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT}, + commitment_config::CommitmentConfig, +}; +use std::{thread::sleep, time::Duration}; pub struct RpcClientRequest { client: reqwest::Client, @@ -36,11 +40,12 @@ impl GenericRpcClientRequest for RpcClientRequest { request: &RpcRequest, params: Option, mut retries: usize, + commitment_config: Option, ) -> Result { // Concurrent requests are not supported so reuse the same request id for all requests let request_id = 1; - let request_json = request.build_request_json(request_id, params); + let request_json = request.build_request_json(request_id, params, commitment_config); loop { match self diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 807785df5d..6fa1471eae 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -1,5 +1,8 @@ use serde_json::{json, Value}; -use solana_sdk::clock::{Epoch, Slot}; +use solana_sdk::{ + clock::{Epoch, Slot}, + commitment_config::CommitmentConfig, +}; use std::{error, fmt}; #[derive(Serialize, Deserialize, Clone, Debug)] @@ -83,7 +86,12 @@ pub enum RpcRequest { } impl RpcRequest { - pub(crate) fn build_request_json(&self, id: u64, params: Option) -> Value { + pub(crate) fn build_request_json( + &self, + id: u64, + params: Option, + commitment_config: Option, + ) -> Value { let jsonrpc = "2.0"; let method = match self { RpcRequest::ConfirmTransaction => "confirmTransaction", @@ -123,7 +131,13 @@ impl RpcRequest { "method": method, }); if let Some(param_string) = params { - request["params"] = param_string; + if let Some(config) = commitment_config { + request["params"] = json!([param_string, config]); + } else { + request["params"] = json!([param_string]); + } + } else if let Some(config) = commitment_config { + request["params"] = json!([config]); } request } @@ -154,45 +168,65 @@ impl error::Error for RpcError { #[cfg(test)] mod tests { use super::*; + use solana_sdk::commitment_config::CommitmentLevel; #[test] fn test_build_request_json() { let test_request = RpcRequest::GetAccountInfo; - let addr = json!(["deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"]); - let request = test_request.build_request_json(1, Some(addr.clone())); + let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"); + let request = test_request.build_request_json(1, Some(addr.clone()), None); assert_eq!(request["method"], "getAccountInfo"); - assert_eq!(request["params"], addr,); + assert_eq!(request["params"], json!([addr])); let test_request = RpcRequest::GetBalance; - let request = test_request.build_request_json(1, Some(addr)); + let request = test_request.build_request_json(1, Some(addr), None); assert_eq!(request["method"], "getBalance"); let test_request = RpcRequest::GetEpochInfo; - let request = test_request.build_request_json(1, None); + let request = test_request.build_request_json(1, None, None); assert_eq!(request["method"], "getEpochInfo"); let test_request = RpcRequest::GetInflation; - let request = test_request.build_request_json(1, None); + let request = test_request.build_request_json(1, None, None); assert_eq!(request["method"], "getInflation"); let test_request = RpcRequest::GetRecentBlockhash; - let request = test_request.build_request_json(1, None); + let request = test_request.build_request_json(1, None, None); assert_eq!(request["method"], "getRecentBlockhash"); let test_request = RpcRequest::GetSlot; - let request = test_request.build_request_json(1, None); + let request = test_request.build_request_json(1, None, None); assert_eq!(request["method"], "getSlot"); let test_request = RpcRequest::GetTransactionCount; - let request = test_request.build_request_json(1, None); + let request = test_request.build_request_json(1, None, None); assert_eq!(request["method"], "getTransactionCount"); let test_request = RpcRequest::RequestAirdrop; - let request = test_request.build_request_json(1, None); + let request = test_request.build_request_json(1, None, None); assert_eq!(request["method"], "requestAirdrop"); let test_request = RpcRequest::SendTransaction; - let request = test_request.build_request_json(1, None); + let request = test_request.build_request_json(1, None, None); assert_eq!(request["method"], "sendTransaction"); } + + #[test] + fn test_build_request_json_config_options() { + let commitment_config = CommitmentConfig { + commitment: CommitmentLevel::Max, + }; + let addr = json!("deadbeefXjn8o3yroDHxUtKsZZgoy4GPkPPXfouKNHhx"); + + // Test request with CommitmentConfig and no params + let test_request = RpcRequest::GetRecentBlockhash; + let request = test_request.build_request_json(1, None, Some(commitment_config.clone())); + assert_eq!(request["params"], json!([commitment_config.clone()])); + + // Test request with CommitmentConfig and params + let test_request = RpcRequest::GetBalance; + let request = + test_request.build_request_json(1, Some(addr.clone()), Some(commitment_config.clone())); + assert_eq!(request["params"], json!([addr, commitment_config])); + } } diff --git a/client/src/thin_client.rs b/client/src/thin_client.rs index 9cf72fcbc7..0c557501dc 100644 --- a/client/src/thin_client.rs +++ b/client/src/thin_client.rs @@ -6,25 +6,32 @@ use crate::rpc_client::RpcClient; use bincode::{serialize_into, serialized_size}; use log::*; -use solana_sdk::account::Account; -use solana_sdk::client::{AsyncClient, Client, SyncClient}; -use solana_sdk::clock::MAX_PROCESSING_AGE; -use solana_sdk::fee_calculator::FeeCalculator; -use solana_sdk::hash::Hash; -use solana_sdk::instruction::Instruction; -use solana_sdk::message::Message; -use solana_sdk::packet::PACKET_DATA_SIZE; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::{Keypair, KeypairUtil, Signature}; -use solana_sdk::system_instruction; -use solana_sdk::timing::duration_as_ms; -use solana_sdk::transaction::{self, Transaction}; -use solana_sdk::transport::Result as TransportResult; -use std::io; -use std::net::{SocketAddr, UdpSocket}; -use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; -use std::sync::RwLock; -use std::time::{Duration, Instant}; +use solana_sdk::{ + account::Account, + client::{AsyncClient, Client, SyncClient}, + clock::MAX_PROCESSING_AGE, + commitment_config::CommitmentConfig, + fee_calculator::FeeCalculator, + hash::Hash, + instruction::Instruction, + message::Message, + packet::PACKET_DATA_SIZE, + pubkey::Pubkey, + signature::{Keypair, KeypairUtil, Signature}, + system_instruction, + timing::duration_as_ms, + transaction::{self, Transaction}, + transport::Result as TransportResult, +}; +use std::{ + io, + net::{SocketAddr, UdpSocket}, + sync::{ + atomic::{AtomicBool, AtomicUsize, Ordering}, + RwLock, + }, + time::{Duration, Instant}, +}; struct ClientOptimizer { cur_index: AtomicUsize, @@ -244,22 +251,78 @@ impl ThinClient { )) } + pub fn poll_balance_with_timeout_and_commitment( + &self, + pubkey: &Pubkey, + polling_frequency: &Duration, + timeout: &Duration, + commitment_config: CommitmentConfig, + ) -> io::Result { + self.rpc_client().poll_balance_with_timeout_and_commitment( + pubkey, + polling_frequency, + timeout, + commitment_config, + ) + } + pub fn poll_balance_with_timeout( &self, pubkey: &Pubkey, polling_frequency: &Duration, timeout: &Duration, ) -> io::Result { - self.rpc_client() - .poll_balance_with_timeout(pubkey, polling_frequency, timeout) + self.rpc_client().poll_balance_with_timeout_and_commitment( + pubkey, + polling_frequency, + timeout, + CommitmentConfig::default(), + ) } pub fn poll_get_balance(&self, pubkey: &Pubkey) -> io::Result { - self.rpc_client().poll_get_balance(pubkey) + self.rpc_client() + .poll_get_balance_with_commitment(pubkey, CommitmentConfig::default()) + } + + pub fn poll_get_balance_with_commitment( + &self, + pubkey: &Pubkey, + commitment_config: CommitmentConfig, + ) -> io::Result { + self.rpc_client() + .poll_get_balance_with_commitment(pubkey, commitment_config) } pub fn wait_for_balance(&self, pubkey: &Pubkey, expected_balance: Option) -> Option { - self.rpc_client().wait_for_balance(pubkey, expected_balance) + self.rpc_client().wait_for_balance_with_commitment( + pubkey, + expected_balance, + CommitmentConfig::default(), + ) + } + + pub fn wait_for_balance_with_commitment( + &self, + pubkey: &Pubkey, + expected_balance: Option, + commitment_config: CommitmentConfig, + ) -> Option { + self.rpc_client().wait_for_balance_with_commitment( + pubkey, + expected_balance, + commitment_config, + ) + } + + pub fn poll_for_signature_with_commitment( + &self, + signature: &Signature, + commitment_config: CommitmentConfig, + ) -> TransportResult<()> { + Ok(self + .rpc_client() + .poll_for_signature_with_commitment(signature, commitment_config)?) } /// Check a signature in the bank. This method blocks @@ -323,15 +386,45 @@ impl SyncClient for ThinClient { Ok(self.rpc_client().get_account(pubkey).ok()) } + fn get_account_with_commitment( + &self, + pubkey: &Pubkey, + commitment_config: CommitmentConfig, + ) -> TransportResult> { + Ok(self + .rpc_client() + .get_account_with_commitment(pubkey, commitment_config) + .ok()) + } + fn get_balance(&self, pubkey: &Pubkey) -> TransportResult { let balance = self.rpc_client().get_balance(pubkey)?; Ok(balance) } + fn get_balance_with_commitment( + &self, + pubkey: &Pubkey, + commitment_config: CommitmentConfig, + ) -> TransportResult { + let balance = self + .rpc_client() + .get_balance_with_commitment(pubkey, commitment_config)?; + Ok(balance) + } + fn get_recent_blockhash(&self) -> TransportResult<(Hash, FeeCalculator)> { + self.get_recent_blockhash_with_commitment(CommitmentConfig::default()) + } + + fn get_recent_blockhash_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> TransportResult<(Hash, FeeCalculator)> { let index = self.optimizer.experiment(); let now = Instant::now(); - let recent_blockhash = self.rpc_clients[index].get_recent_blockhash(); + let recent_blockhash = + self.rpc_clients[index].get_recent_blockhash_with_commitment(commitment_config); match recent_blockhash { Ok(recent_blockhash) => { self.optimizer.report(index, duration_as_ms(&now.elapsed())); @@ -360,13 +453,40 @@ impl SyncClient for ThinClient { Ok(status) } + fn get_signature_status_with_commitment( + &self, + signature: &Signature, + commitment_config: CommitmentConfig, + ) -> TransportResult>> { + let status = self + .rpc_client() + .get_signature_status_with_commitment(&signature.to_string(), commitment_config) + .map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("send_transaction failed with error {:?}", err), + ) + })?; + Ok(status) + } + fn get_slot(&self) -> TransportResult { - let slot = self.rpc_client().get_slot().map_err(|err| { - io::Error::new( - io::ErrorKind::Other, - format!("send_transaction failed with error {:?}", err), - ) - })?; + self.get_slot_with_commitment(CommitmentConfig::default()) + } + + fn get_slot_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> TransportResult { + let slot = self + .rpc_client() + .get_slot_with_commitment(commitment_config) + .map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("send_transaction failed with error {:?}", err), + ) + })?; Ok(slot) } @@ -385,6 +505,27 @@ impl SyncClient for ThinClient { } } + fn get_transaction_count_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> TransportResult { + let index = self.optimizer.experiment(); + let now = Instant::now(); + match self + .rpc_client() + .get_transaction_count_with_commitment(commitment_config) + { + Ok(transaction_count) => { + self.optimizer.report(index, duration_as_ms(&now.elapsed())); + Ok(transaction_count) + } + Err(e) => { + self.optimizer.report(index, std::u64::MAX); + Err(e.into()) + } + } + } + /// Poll the server until the signature has been confirmed by at least `min_confirmed_blocks` fn poll_for_signature_confirmation( &self, diff --git a/core/src/archiver.rs b/core/src/archiver.rs index 9bd3cb4db8..0dda45559b 100644 --- a/core/src/archiver.rs +++ b/core/src/archiver.rs @@ -29,6 +29,7 @@ use solana_sdk::{ account_utils::State, client::{AsyncClient, SyncClient}, clock::{get_complete_segment_from_slot, get_segment_from_slot, Slot}, + commitment_config::CommitmentConfig, hash::{Hash, Hasher}, message::Message, signature::{Keypair, KeypairUtil, Signature}, @@ -78,6 +79,7 @@ struct ArchiverMeta { blockhash: Hash, sha_state: Hash, num_chacha_blocks: usize, + client_commitment: CommitmentConfig, } pub(crate) fn sample_file(in_path: &Path, sample_offsets: &[u64]) -> io::Result { @@ -208,6 +210,7 @@ impl Archiver { cluster_entrypoint: ContactInfo, keypair: Arc, storage_keypair: Arc, + client_commitment: CommitmentConfig, ) -> Result { let exit = Arc::new(AtomicBool::new(false)); @@ -246,7 +249,12 @@ impl Archiver { let client = crate::gossip_service::get_client(&nodes); info!("Setting up mining account..."); - if let Err(e) = Self::setup_mining_account(&client, &keypair, &storage_keypair) { + if let Err(e) = Self::setup_mining_account( + &client, + &keypair, + &storage_keypair, + client_commitment.clone(), + ) { //shutdown services before exiting exit.store(true, Ordering::Relaxed); gossip_service.join()?; @@ -279,6 +287,7 @@ impl Archiver { let node_info = node.info.clone(); let mut meta = ArchiverMeta { ledger_path: ledger_path.to_path_buf(), + client_commitment, ..ArchiverMeta::default() }; spawn(move || { @@ -383,7 +392,12 @@ impl Archiver { } }; meta.blockhash = storage_blockhash; - Self::redeem_rewards(&cluster_info, archiver_keypair, storage_keypair); + Self::redeem_rewards( + &cluster_info, + archiver_keypair, + storage_keypair, + meta.client_commitment.clone(), + ); } exit.store(true, Ordering::Relaxed); } @@ -392,11 +406,14 @@ impl Archiver { cluster_info: &Arc>, archiver_keypair: &Arc, storage_keypair: &Arc, + client_commitment: CommitmentConfig, ) { let nodes = cluster_info.read().unwrap().tvu_peers(); let client = crate::gossip_service::get_client(&nodes); - if let Ok(Some(account)) = client.get_account(&storage_keypair.pubkey()) { + if let Ok(Some(account)) = + client.get_account_with_commitment(&storage_keypair.pubkey(), client_commitment.clone()) + { if let Ok(StorageContract::ArchiverStorage { validations, .. }) = account.state() { if !validations.is_empty() { let ix = storage_instruction::claim_reward( @@ -410,7 +427,10 @@ impl Archiver { } else { info!( "collected mining rewards: Account balance {:?}", - client.get_balance(&archiver_keypair.pubkey()) + client.get_balance_with_commitment( + &archiver_keypair.pubkey(), + client_commitment.clone() + ) ); } } @@ -432,15 +452,16 @@ impl Archiver { blob_fetch_receiver: PacketReceiver, slot_sender: Sender, ) -> Result<(WindowService)> { - let slots_per_segment = match Self::get_segment_config(&cluster_info) { - Ok(slots_per_segment) => slots_per_segment, - Err(e) => { - error!("unable to get segment size configuration, exiting..."); - //shutdown services before exiting - exit.store(true, Ordering::Relaxed); - return Err(e); - } - }; + let slots_per_segment = + match Self::get_segment_config(&cluster_info, meta.client_commitment.clone()) { + Ok(slots_per_segment) => slots_per_segment, + Err(e) => { + error!("unable to get segment size configuration, exiting..."); + //shutdown services before exiting + exit.store(true, Ordering::Relaxed); + return Err(e); + } + }; let (segment_blockhash, segment_slot) = match Self::poll_for_segment( &cluster_info, slots_per_segment, @@ -588,13 +609,15 @@ impl Archiver { client: &ThinClient, keypair: &Keypair, storage_keypair: &Keypair, + client_commitment: CommitmentConfig, ) -> Result<()> { // make sure archiver has some balance info!("checking archiver keypair..."); - if client.poll_balance_with_timeout( + if client.poll_balance_with_timeout_and_commitment( &keypair.pubkey(), &Duration::from_millis(100), &Duration::from_secs(5), + client_commitment.clone(), )? == 0 { return Err( @@ -604,17 +627,19 @@ impl Archiver { info!("checking storage account keypair..."); // check if the storage account exists - let balance = client.poll_get_balance(&storage_keypair.pubkey()); + let balance = client + .poll_get_balance_with_commitment(&storage_keypair.pubkey(), client_commitment.clone()); if balance.is_err() || balance.unwrap() == 0 { - let blockhash = match client.get_recent_blockhash() { - Ok((blockhash, _)) => blockhash, - Err(_) => { - return Err(Error::IO(::new( - io::ErrorKind::Other, - "unable to get recent blockhash, can't submit proof", - ))); - } - }; + let blockhash = + match client.get_recent_blockhash_with_commitment(client_commitment.clone()) { + Ok((blockhash, _)) => blockhash, + Err(_) => { + return Err(Error::IO(::new( + io::ErrorKind::Other, + "unable to get recent blockhash, can't submit proof", + ))); + } + }; let ix = storage_instruction::create_storage_account( &keypair.pubkey(), @@ -626,7 +651,7 @@ impl Archiver { let tx = Transaction::new_signed_instructions(&[keypair], ix, blockhash); let signature = client.async_send_transaction(tx)?; client - .poll_for_signature(&signature) + .poll_for_signature_with_commitment(&signature, client_commitment.clone()) .map_err(|err| match err { TransportError::IoError(e) => e, TransportError::TransactionError(_) => io::Error::new( @@ -647,25 +672,32 @@ impl Archiver { // No point if we've got no storage account... let nodes = cluster_info.read().unwrap().tvu_peers(); let client = crate::gossip_service::get_client(&nodes); - let storage_balance = client.poll_get_balance(&storage_keypair.pubkey()); + let storage_balance = client.poll_get_balance_with_commitment( + &storage_keypair.pubkey(), + meta.client_commitment.clone(), + ); if storage_balance.is_err() || storage_balance.unwrap() == 0 { error!("Unable to submit mining proof, no storage account"); return; } // ...or no lamports for fees - let balance = client.poll_get_balance(&archiver_keypair.pubkey()); + let balance = client.poll_get_balance_with_commitment( + &archiver_keypair.pubkey(), + meta.client_commitment.clone(), + ); if balance.is_err() || balance.unwrap() == 0 { error!("Unable to submit mining proof, insufficient Archiver Account balance"); return; } - let blockhash = match client.get_recent_blockhash() { - Ok((blockhash, _)) => blockhash, - Err(_) => { - error!("unable to get recent blockhash, can't submit proof"); - return; - } - }; + let blockhash = + match client.get_recent_blockhash_with_commitment(meta.client_commitment.clone()) { + Ok((blockhash, _)) => blockhash, + Err(_) => { + error!("unable to get recent blockhash, can't submit proof"); + return; + } + }; let instruction = storage_instruction::mining_proof( &storage_keypair.pubkey(), meta.sha_state, @@ -700,7 +732,10 @@ impl Archiver { } } - fn get_segment_config(cluster_info: &Arc>) -> result::Result { + fn get_segment_config( + cluster_info: &Arc>, + client_commitment: CommitmentConfig, + ) -> result::Result { let rpc_peers = { let cluster_info = cluster_info.read().unwrap(); cluster_info.rpc_peers() @@ -712,7 +747,12 @@ impl Archiver { RpcClient::new_socket(rpc_peers[node_index].rpc) }; Ok(rpc_client - .retry_make_rpc_request(&RpcRequest::GetSlotsPerSegment, None, 0) + .retry_make_rpc_request( + &RpcRequest::GetSlotsPerSegment, + None, + 0, + Some(client_commitment), + ) .map_err(|err| { warn!("Error while making rpc request {:?}", err); Error::IO(io::Error::new(ErrorKind::Other, "rpc error")) @@ -764,7 +804,7 @@ impl Archiver { RpcClient::new_socket(rpc_peers[node_index].rpc) }; let response = rpc_client - .retry_make_rpc_request(&RpcRequest::GetStorageTurn, None, 0) + .retry_make_rpc_request(&RpcRequest::GetStorageTurn, None, 0, None) .map_err(|err| { warn!("Error while making rpc request {:?}", err); Error::IO(io::Error::new(ErrorKind::Other, "rpc error")) diff --git a/core/src/rpc.rs b/core/src/rpc.rs index da1d8da7b8..b7b0ae9dbd 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -19,6 +19,7 @@ use solana_runtime::bank::Bank; use solana_sdk::{ account::Account, clock::Slot, + commitment_config::{CommitmentConfig, CommitmentLevel}, epoch_schedule::EpochSchedule, fee_calculator::FeeCalculator, hash::Hash, @@ -60,8 +61,18 @@ pub struct JsonRpcRequestProcessor { } impl JsonRpcRequestProcessor { - fn bank(&self) -> Arc { - self.bank_forks.read().unwrap().working_bank() + fn bank(&self, commitment: Option) -> Arc { + debug!("RPC commitment_config: {:?}", commitment); + let r_bank_forks = self.bank_forks.read().unwrap(); + if commitment.is_some() && commitment.unwrap().commitment == CommitmentLevel::Recent { + let bank = r_bank_forks.working_bank(); + debug!("RPC using working_bank: {:?}", bank.slot()); + bank + } else { + let slot = r_bank_forks.root(); + debug!("RPC using block: {:?}", slot); + r_bank_forks.get(slot).cloned().unwrap() + } } pub fn new( @@ -80,43 +91,62 @@ impl JsonRpcRequestProcessor { } } - pub fn get_account_info(&self, pubkey: &Pubkey) -> Result { - self.bank() + pub fn get_account_info( + &self, + pubkey: &Pubkey, + commitment: Option, + ) -> Result { + self.bank(commitment) .get_account(&pubkey) .ok_or_else(Error::invalid_request) } - pub fn get_minimum_balance_for_rent_exemption(&self, data_len: usize) -> Result { - Ok(self.bank().get_minimum_balance_for_rent_exemption(data_len)) + pub fn get_minimum_balance_for_rent_exemption( + &self, + data_len: usize, + commitment: Option, + ) -> Result { + Ok(self + .bank(commitment) + .get_minimum_balance_for_rent_exemption(data_len)) } - pub fn get_program_accounts(&self, program_id: &Pubkey) -> Result> { + pub fn get_program_accounts( + &self, + program_id: &Pubkey, + commitment: Option, + ) -> Result> { Ok(self - .bank() + .bank(commitment) .get_program_accounts(&program_id) .into_iter() .map(|(pubkey, account)| (pubkey.to_string(), account)) .collect()) } - pub fn get_inflation(&self) -> Result { - Ok(self.bank().inflation()) + pub fn get_inflation(&self, commitment: Option) -> Result { + Ok(self.bank(commitment).inflation()) } pub fn get_epoch_schedule(&self) -> Result { - Ok(*self.bank().epoch_schedule()) + // Since epoch schedule data comes from the genesis block, any commitment level should be + // fine + Ok(*self.bank(None).epoch_schedule()) } - pub fn get_balance(&self, pubkey: &Pubkey) -> u64 { - self.bank().get_balance(&pubkey) + pub fn get_balance(&self, pubkey: &Pubkey, commitment: Option) -> u64 { + self.bank(commitment).get_balance(&pubkey) } - fn get_recent_blockhash(&self) -> (String, FeeCalculator) { - let (blockhash, fee_calculator) = self.bank().confirmed_last_blockhash(); + fn get_recent_blockhash( + &self, + commitment: Option, + ) -> (String, FeeCalculator) { + let (blockhash, fee_calculator) = self.bank(commitment).confirmed_last_blockhash(); (blockhash.to_string(), fee_calculator) } - fn get_block_commitment(&self, block: u64) -> (Option, u64) { + fn get_block_commitment(&self, block: Slot) -> (Option, u64) { let r_block_commitment = self.block_commitment_cache.read().unwrap(); ( r_block_commitment.get_block_commitment(block).cloned(), @@ -124,41 +154,36 @@ impl JsonRpcRequestProcessor { ) } - pub fn get_signature_status(&self, signature: Signature) -> Option> { - self.get_signature_confirmation_status(signature) - .map(|x| x.1) - } - - pub fn get_signature_confirmations(&self, signature: Signature) -> Option { - self.get_signature_confirmation_status(signature) - .map(|x| x.0) - } - pub fn get_signature_confirmation_status( &self, signature: Signature, + commitment: Option, ) -> Option<(usize, transaction::Result<()>)> { - self.bank().get_signature_confirmation_status(&signature) + self.bank(commitment) + .get_signature_confirmation_status(&signature) } - fn get_slot(&self) -> Result { - Ok(self.bank().slot()) + fn get_slot(&self, commitment: Option) -> Result { + Ok(self.bank(commitment).slot()) } - fn get_slot_leader(&self) -> Result { - Ok(self.bank().collector_id().to_string()) + fn get_slot_leader(&self, commitment: Option) -> Result { + Ok(self.bank(commitment).collector_id().to_string()) } - fn get_transaction_count(&self) -> Result { - Ok(self.bank().transaction_count() as u64) + fn get_transaction_count(&self, commitment: Option) -> Result { + Ok(self.bank(commitment).transaction_count() as u64) } - fn get_total_supply(&self) -> Result { - Ok(self.bank().capitalization()) + fn get_total_supply(&self, commitment: Option) -> Result { + Ok(self.bank(commitment).capitalization()) } - fn get_vote_accounts(&self) -> Result { - let bank = self.bank(); + fn get_vote_accounts( + &self, + commitment: Option, + ) -> Result { + let bank = self.bank(commitment); let vote_accounts = bank.vote_accounts(); let epoch_vote_accounts = bank .epoch_vote_accounts(bank.get_epoch_and_slot_index(bank.slot()).0) @@ -212,8 +237,8 @@ impl JsonRpcRequestProcessor { )) } - fn get_slots_per_segment(&self) -> Result { - Ok(self.bank().slots_per_segment()) + fn get_slots_per_segment(&self, commitment: Option) -> Result { + Ok(self.bank(commitment).slots_per_segment()) } fn get_storage_pubkeys_for_slot(&self, slot: Slot) -> Result> { @@ -281,139 +306,224 @@ pub trait RpcSol { type Metadata; #[rpc(meta, name = "confirmTransaction")] - fn confirm_transaction(&self, _: Self::Metadata, _: String) -> Result; + fn confirm_transaction( + &self, + meta: Self::Metadata, + signature_str: String, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getAccountInfo")] - fn get_account_info(&self, _: Self::Metadata, _: String) -> Result; + fn get_account_info( + &self, + meta: Self::Metadata, + pubkey_str: String, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getProgramAccounts")] - fn get_program_accounts(&self, _: Self::Metadata, _: String) -> Result>; + fn get_program_accounts( + &self, + meta: Self::Metadata, + program_id_str: String, + commitment: Option, + ) -> Result>; #[rpc(meta, name = "getMinimumBalanceForRentExemption")] - fn get_minimum_balance_for_rent_exemption(&self, _: Self::Metadata, _: usize) -> Result; + fn get_minimum_balance_for_rent_exemption( + &self, + meta: Self::Metadata, + data_len: usize, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getInflation")] - fn get_inflation(&self, _: Self::Metadata) -> Result; + fn get_inflation( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getEpochSchedule")] - fn get_epoch_schedule(&self, _: Self::Metadata) -> Result; + fn get_epoch_schedule(&self, meta: Self::Metadata) -> Result; #[rpc(meta, name = "getBalance")] - fn get_balance(&self, _: Self::Metadata, _: String) -> Result; + fn get_balance( + &self, + meta: Self::Metadata, + pubkey_str: String, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getClusterNodes")] - fn get_cluster_nodes(&self, _: Self::Metadata) -> Result>; + fn get_cluster_nodes(&self, meta: Self::Metadata) -> Result>; #[rpc(meta, name = "getEpochInfo")] - fn get_epoch_info(&self, _: Self::Metadata) -> Result; + fn get_epoch_info( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getBlockCommitment")] fn get_block_commitment( &self, - _: Self::Metadata, - _: u64, + meta: Self::Metadata, + block: u64, ) -> Result<(Option, u64)>; #[rpc(meta, name = "getGenesisBlockhash")] - fn get_genesis_blockhash(&self, _: Self::Metadata) -> Result; + fn get_genesis_blockhash(&self, meta: Self::Metadata) -> Result; #[rpc(meta, name = "getLeaderSchedule")] - fn get_leader_schedule(&self, _: Self::Metadata) -> Result>>; + fn get_leader_schedule( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result>>; #[rpc(meta, name = "getRecentBlockhash")] - fn get_recent_blockhash(&self, _: Self::Metadata) -> Result<(String, FeeCalculator)>; + fn get_recent_blockhash( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result<(String, FeeCalculator)>; #[rpc(meta, name = "getSignatureStatus")] fn get_signature_status( &self, - _: Self::Metadata, - _: String, + meta: Self::Metadata, + signature_str: String, + commitment: Option, ) -> Result>>; #[rpc(meta, name = "getSlot")] - fn get_slot(&self, _: Self::Metadata) -> Result; + fn get_slot(&self, meta: Self::Metadata, commitment: Option) -> Result; #[rpc(meta, name = "getTransactionCount")] - fn get_transaction_count(&self, _: Self::Metadata) -> Result; + fn get_transaction_count( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getTotalSupply")] - fn get_total_supply(&self, _: Self::Metadata) -> Result; + fn get_total_supply( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result; #[rpc(meta, name = "requestAirdrop")] - fn request_airdrop(&self, _: Self::Metadata, _: String, _: u64) -> Result; + fn request_airdrop( + &self, + meta: Self::Metadata, + pubkey_str: String, + lamports: u64, + commitment: Option, + ) -> Result; #[rpc(meta, name = "sendTransaction")] - fn send_transaction(&self, _: Self::Metadata, _: Vec) -> Result; + fn send_transaction(&self, meta: Self::Metadata, data: Vec) -> Result; #[rpc(meta, name = "getSlotLeader")] - fn get_slot_leader(&self, _: Self::Metadata) -> Result; + fn get_slot_leader( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getVoteAccounts")] - fn get_vote_accounts(&self, _: Self::Metadata) -> Result; + fn get_vote_accounts( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getStorageTurnRate")] - fn get_storage_turn_rate(&self, _: Self::Metadata) -> Result; + fn get_storage_turn_rate(&self, meta: Self::Metadata) -> Result; #[rpc(meta, name = "getStorageTurn")] - fn get_storage_turn(&self, _: Self::Metadata) -> Result<(String, u64)>; + fn get_storage_turn(&self, meta: Self::Metadata) -> Result<(String, u64)>; #[rpc(meta, name = "getSlotsPerSegment")] - fn get_slots_per_segment(&self, _: Self::Metadata) -> Result; + fn get_slots_per_segment( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result; #[rpc(meta, name = "getStoragePubkeysForSlot")] - fn get_storage_pubkeys_for_slot(&self, _: Self::Metadata, _: u64) -> Result>; + fn get_storage_pubkeys_for_slot(&self, meta: Self::Metadata, slot: u64) -> Result>; #[rpc(meta, name = "validatorExit")] - fn validator_exit(&self, _: Self::Metadata) -> Result; + fn validator_exit(&self, meta: Self::Metadata) -> Result; #[rpc(meta, name = "getNumBlocksSinceSignatureConfirmation")] fn get_num_blocks_since_signature_confirmation( &self, - _: Self::Metadata, - _: String, + meta: Self::Metadata, + signature_str: String, + commitment: Option, ) -> Result>; #[rpc(meta, name = "getSignatureConfirmation")] fn get_signature_confirmation( &self, - _: Self::Metadata, - _: String, + meta: Self::Metadata, + signature_str: String, + commitment: Option, ) -> Result)>>; #[rpc(meta, name = "getVersion")] - fn get_version(&self, _: Self::Metadata) -> Result; + fn get_version(&self, meta: Self::Metadata) -> Result; #[rpc(meta, name = "setLogFilter")] - fn set_log_filter(&self, _: Self::Metadata, _: String) -> Result<()>; + fn set_log_filter(&self, _meta: Self::Metadata, filter: String) -> Result<()>; } pub struct RpcSolImpl; impl RpcSol for RpcSolImpl { type Metadata = Meta; - fn confirm_transaction(&self, meta: Self::Metadata, id: String) -> Result { - debug!("confirm_transaction rpc request received: {:?}", id); - self.get_signature_status(meta, id).map(|status_option| { - if status_option.is_none() { - return false; - } - status_option.unwrap().is_ok() - }) + fn confirm_transaction( + &self, + meta: Self::Metadata, + signature_str: String, + commitment: Option, + ) -> Result { + debug!( + "confirm_transaction rpc request received: {:?}", + signature_str + ); + self.get_signature_status(meta, signature_str, commitment) + .map(|status_option| { + if status_option.is_none() { + return false; + } + status_option.unwrap().is_ok() + }) } - fn get_account_info(&self, meta: Self::Metadata, id: String) -> Result { - debug!("get_account_info rpc request received: {:?}", id); - let pubkey = verify_pubkey(id)?; + fn get_account_info( + &self, + meta: Self::Metadata, + pubkey_str: String, + commitment: Option, + ) -> Result { + debug!("get_account_info rpc request received: {:?}", pubkey_str); + let pubkey = verify_pubkey(pubkey_str)?; meta.request_processor .read() .unwrap() - .get_account_info(&pubkey) + .get_account_info(&pubkey, commitment) } fn get_minimum_balance_for_rent_exemption( &self, meta: Self::Metadata, data_len: usize, + commitment: Option, ) -> Result { debug!( "get_minimum_balance_for_rent_exemption rpc request received: {:?}", @@ -422,29 +532,37 @@ impl RpcSol for RpcSolImpl { meta.request_processor .read() .unwrap() - .get_minimum_balance_for_rent_exemption(data_len) + .get_minimum_balance_for_rent_exemption(data_len, commitment) } fn get_program_accounts( &self, meta: Self::Metadata, - id: String, + program_id_str: String, + commitment: Option, ) -> Result> { - debug!("get_program_accounts rpc request received: {:?}", id); - let program_id = verify_pubkey(id)?; + debug!( + "get_program_accounts rpc request received: {:?}", + program_id_str + ); + let program_id = verify_pubkey(program_id_str)?; meta.request_processor .read() .unwrap() - .get_program_accounts(&program_id) + .get_program_accounts(&program_id, commitment) } - fn get_inflation(&self, meta: Self::Metadata) -> Result { + fn get_inflation( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result { debug!("get_inflation rpc request received"); Ok(meta .request_processor .read() .unwrap() - .get_inflation() + .get_inflation(commitment) .unwrap()) } @@ -458,10 +576,19 @@ impl RpcSol for RpcSolImpl { .unwrap()) } - fn get_balance(&self, meta: Self::Metadata, id: String) -> Result { - debug!("get_balance rpc request received: {:?}", id); - let pubkey = verify_pubkey(id)?; - Ok(meta.request_processor.read().unwrap().get_balance(&pubkey)) + fn get_balance( + &self, + meta: Self::Metadata, + pubkey_str: String, + commitment: Option, + ) -> Result { + debug!("get_balance rpc request received: {:?}", pubkey_str); + let pubkey = verify_pubkey(pubkey_str)?; + Ok(meta + .request_processor + .read() + .unwrap() + .get_balance(&pubkey, commitment)) } fn get_cluster_nodes(&self, meta: Self::Metadata) -> Result> { @@ -491,8 +618,12 @@ impl RpcSol for RpcSolImpl { .collect()) } - fn get_epoch_info(&self, meta: Self::Metadata) -> Result { - let bank = meta.request_processor.read().unwrap().bank(); + fn get_epoch_info( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result { + let bank = meta.request_processor.read().unwrap().bank(commitment); let epoch_schedule = bank.epoch_schedule(); let (epoch, slot_index) = epoch_schedule.get_epoch_and_slot_index(bank.slot()); let slot = bank.slot(); @@ -507,7 +638,7 @@ impl RpcSol for RpcSolImpl { fn get_block_commitment( &self, meta: Self::Metadata, - block: u64, + block: Slot, ) -> Result<(Option, u64)> { Ok(meta .request_processor @@ -521,8 +652,12 @@ impl RpcSol for RpcSolImpl { Ok(meta.genesis_blockhash.to_string()) } - fn get_leader_schedule(&self, meta: Self::Metadata) -> Result>> { - let bank = meta.request_processor.read().unwrap().bank(); + fn get_leader_schedule( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result>> { + let bank = meta.request_processor.read().unwrap().bank(commitment); Ok( solana_ledger::leader_schedule_utils::leader_schedule(bank.epoch(), &bank).map( |leader_schedule| { @@ -536,66 +671,93 @@ impl RpcSol for RpcSolImpl { ) } - fn get_recent_blockhash(&self, meta: Self::Metadata) -> Result<(String, FeeCalculator)> { + fn get_recent_blockhash( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result<(String, FeeCalculator)> { debug!("get_recent_blockhash rpc request received"); Ok(meta .request_processor .read() .unwrap() - .get_recent_blockhash()) + .get_recent_blockhash(commitment)) } fn get_signature_status( &self, meta: Self::Metadata, - id: String, + signature_str: String, + commitment: Option, ) -> Result>> { - self.get_signature_confirmation(meta, id) + self.get_signature_confirmation(meta, signature_str, commitment) .map(|res| res.map(|x| x.1)) } - fn get_slot(&self, meta: Self::Metadata) -> Result { - meta.request_processor.read().unwrap().get_slot() + fn get_slot(&self, meta: Self::Metadata, commitment: Option) -> Result { + meta.request_processor.read().unwrap().get_slot(commitment) } fn get_num_blocks_since_signature_confirmation( &self, meta: Self::Metadata, - id: String, + signature_str: String, + commitment: Option, ) -> Result> { - self.get_signature_confirmation(meta, id) + self.get_signature_confirmation(meta, signature_str, commitment) .map(|res| res.map(|x| x.0)) } fn get_signature_confirmation( &self, meta: Self::Metadata, - id: String, + signature_str: String, + commitment: Option, ) -> Result)>> { - debug!("get_signature_confirmation rpc request received: {:?}", id); - let signature = verify_signature(&id)?; + debug!( + "get_signature_confirmation rpc request received: {:?}", + signature_str + ); + let signature = verify_signature(&signature_str)?; Ok(meta .request_processor .read() .unwrap() - .get_signature_confirmation_status(signature)) + .get_signature_confirmation_status(signature, commitment)) } - fn get_transaction_count(&self, meta: Self::Metadata) -> Result { + fn get_transaction_count( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result { debug!("get_transaction_count rpc request received"); meta.request_processor .read() .unwrap() - .get_transaction_count() + .get_transaction_count(commitment) } - fn get_total_supply(&self, meta: Self::Metadata) -> Result { + fn get_total_supply( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result { debug!("get_total_supply rpc request received"); - meta.request_processor.read().unwrap().get_total_supply() + meta.request_processor + .read() + .unwrap() + .get_total_supply(commitment) } - fn request_airdrop(&self, meta: Self::Metadata, id: String, lamports: u64) -> Result { - trace!("request_airdrop id={} lamports={}", id, lamports); + fn request_airdrop( + &self, + meta: Self::Metadata, + pubkey_str: String, + lamports: u64, + commitment: Option, + ) -> Result { + trace!("request_airdrop id={} lamports={}", pubkey_str, lamports); let drone_addr = meta .request_processor @@ -604,13 +766,13 @@ impl RpcSol for RpcSolImpl { .config .drone_addr .ok_or_else(Error::invalid_request)?; - let pubkey = verify_pubkey(id)?; + let pubkey = verify_pubkey(pubkey_str)?; let blockhash = meta .request_processor .read() .unwrap() - .bank() + .bank(commitment.clone()) .confirmed_last_blockhash() .0; let transaction = request_airdrop_transaction(&drone_addr, &pubkey, lamports, blockhash) @@ -641,7 +803,8 @@ impl RpcSol for RpcSolImpl { .request_processor .read() .unwrap() - .get_signature_status(signature); + .get_signature_confirmation_status(signature, commitment.clone()) + .map(|x| x.1); if signature_status == Some(Ok(())) { info!("airdrop signature ok"); @@ -689,12 +852,26 @@ impl RpcSol for RpcSolImpl { Ok(signature) } - fn get_slot_leader(&self, meta: Self::Metadata) -> Result { - meta.request_processor.read().unwrap().get_slot_leader() + fn get_slot_leader( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result { + meta.request_processor + .read() + .unwrap() + .get_slot_leader(commitment) } - fn get_vote_accounts(&self, meta: Self::Metadata) -> Result { - meta.request_processor.read().unwrap().get_vote_accounts() + fn get_vote_accounts( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result { + meta.request_processor + .read() + .unwrap() + .get_vote_accounts(commitment) } fn get_storage_turn_rate(&self, meta: Self::Metadata) -> Result { @@ -708,11 +885,15 @@ impl RpcSol for RpcSolImpl { meta.request_processor.read().unwrap().get_storage_turn() } - fn get_slots_per_segment(&self, meta: Self::Metadata) -> Result { + fn get_slots_per_segment( + &self, + meta: Self::Metadata, + commitment: Option, + ) -> Result { meta.request_processor .read() .unwrap() - .get_slots_per_segment() + .get_slots_per_segment(commitment) } fn get_storage_pubkeys_for_slot( @@ -736,7 +917,7 @@ impl RpcSol for RpcSolImpl { }) } - fn set_log_filter(&self, _: Self::Metadata, filter: String) -> Result<()> { + fn set_log_filter(&self, _meta: Self::Metadata, filter: String) -> Result<()> { solana_logger::setup_with_filter(&filter); Ok(()) } @@ -863,7 +1044,7 @@ pub mod tests { }) .join() .unwrap(); - assert_eq!(request_processor.get_transaction_count().unwrap(), 1); + assert_eq!(request_processor.get_transaction_count(None).unwrap(), 1); } #[test] diff --git a/core/src/rpc_service.rs b/core/src/rpc_service.rs index e1e268f358..6ac929e59e 100644 --- a/core/src/rpc_service.rs +++ b/core/src/rpc_service.rs @@ -221,7 +221,7 @@ mod tests { .request_processor .read() .unwrap() - .get_balance(&mint_keypair.pubkey()) + .get_balance(&mint_keypair.pubkey(), None) ); rpc_service.exit(); rpc_service.join().unwrap(); diff --git a/core/src/validator.rs b/core/src/validator.rs index ed26585a17..de5eb748e3 100644 --- a/core/src/validator.rs +++ b/core/src/validator.rs @@ -511,7 +511,7 @@ pub fn new_validator_for_tests() -> (Validator, ContactInfo, Keypair, PathBuf) { let GenesisBlockInfo { mut genesis_block, mint_keypair, - .. + voting_keypair, } = create_genesis_block_with_leader(10_000, &contact_info.id, 42); genesis_block .native_instruction_processors @@ -522,14 +522,14 @@ pub fn new_validator_for_tests() -> (Validator, ContactInfo, Keypair, PathBuf) { let (ledger_path, _blockhash) = create_new_tmp_ledger!(&genesis_block); - let voting_keypair = Arc::new(Keypair::new()); + let leader_voting_keypair = Arc::new(voting_keypair); let storage_keypair = Arc::new(Keypair::new()); let node = Validator::new( node, &node_keypair, &ledger_path, - &voting_keypair.pubkey(), - &voting_keypair, + &leader_voting_keypair.pubkey(), + &leader_voting_keypair, &storage_keypair, None, true, diff --git a/local_cluster/src/cluster_tests.rs b/local_cluster/src/cluster_tests.rs index 2f2f55bef7..ac242e40a1 100644 --- a/local_cluster/src/cluster_tests.rs +++ b/local_cluster/src/cluster_tests.rs @@ -1,9 +1,9 @@ -use rand::{thread_rng, Rng}; -use solana_client::thin_client::create_client; /// Cluster independant integration tests /// /// All tests must start from an entry point and a funding keypair and /// discover the rest of the network. +use rand::{thread_rng, Rng}; +use solana_client::thin_client::create_client; use solana_core::{ cluster_info::VALIDATOR_PORT_RANGE, consensus::VOTE_THRESHOLD_DEPTH, contact_info::ContactInfo, gossip_service::discover_cluster, @@ -15,6 +15,7 @@ use solana_ledger::{ use solana_sdk::{ client::SyncClient, clock::{Slot, DEFAULT_TICKS_PER_SECOND, DEFAULT_TICKS_PER_SLOT, NUM_CONSECUTIVE_LEADER_SLOTS}, + commitment_config::CommitmentConfig, epoch_schedule::MINIMUM_SLOTS_PER_EPOCH, hash::Hash, poh_config::PohConfig, @@ -49,10 +50,12 @@ pub fn spend_and_verify_all_nodes( let random_keypair = Keypair::new(); let client = create_client(ingress_node.client_facing_addr(), VALIDATOR_PORT_RANGE); let bal = client - .poll_get_balance(&funding_keypair.pubkey()) + .poll_get_balance_with_commitment(&funding_keypair.pubkey(), CommitmentConfig::recent()) .expect("balance in source"); assert!(bal > 0); - let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap(); + let (blockhash, _fee_calculator) = client + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) + .unwrap(); let mut transaction = system_transaction::transfer(&funding_keypair, &random_keypair.pubkey(), 1, blockhash); let confs = VOTE_THRESHOLD_DEPTH + 1; @@ -75,7 +78,9 @@ pub fn verify_balances( ) { let client = create_client(node.client_facing_addr(), VALIDATOR_PORT_RANGE); for (pk, b) in expected_balances { - let bal = client.poll_get_balance(&pk).expect("balance in source"); + let bal = client + .poll_get_balance_with_commitment(&pk, CommitmentConfig::recent()) + .expect("balance in source"); assert_eq!(bal, b); } } @@ -91,10 +96,12 @@ pub fn send_many_transactions( for _ in 0..num_txs { let random_keypair = Keypair::new(); let bal = client - .poll_get_balance(&funding_keypair.pubkey()) + .poll_get_balance_with_commitment(&funding_keypair.pubkey(), CommitmentConfig::recent()) .expect("balance in source"); assert!(bal > 0); - let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap(); + let (blockhash, _fee_calculator) = client + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) + .unwrap(); let transfer_amount = thread_rng().gen_range(1, max_tokens_per_transfer); let mut transaction = system_transaction::transfer( @@ -188,7 +195,7 @@ pub fn kill_entry_and_spend_and_verify_rest( for ingress_node in &cluster_nodes { client - .poll_get_balance(&ingress_node.id) + .poll_get_balance_with_commitment(&ingress_node.id, CommitmentConfig::recent()) .unwrap_or_else(|err| panic!("Node {} has no balance: {}", ingress_node.id, err)); } @@ -212,7 +219,7 @@ pub fn kill_entry_and_spend_and_verify_rest( let client = create_client(ingress_node.client_facing_addr(), VALIDATOR_PORT_RANGE); let balance = client - .poll_get_balance(&funding_keypair.pubkey()) + .poll_get_balance_with_commitment(&funding_keypair.pubkey(), CommitmentConfig::recent()) .expect("balance in source"); assert_ne!(balance, 0); @@ -225,7 +232,9 @@ pub fn kill_entry_and_spend_and_verify_rest( } let random_keypair = Keypair::new(); - let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap(); + let (blockhash, _fee_calculator) = client + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) + .unwrap(); let mut transaction = system_transaction::transfer( &funding_keypair, &random_keypair.pubkey(), diff --git a/local_cluster/src/local_cluster.rs b/local_cluster/src/local_cluster.rs index 0785d36702..7c1840d6c3 100644 --- a/local_cluster/src/local_cluster.rs +++ b/local_cluster/src/local_cluster.rs @@ -13,6 +13,7 @@ use solana_ledger::blocktree::create_new_tmp_ledger; use solana_sdk::{ client::SyncClient, clock::{DEFAULT_SLOTS_PER_EPOCH, DEFAULT_SLOTS_PER_SEGMENT, DEFAULT_TICKS_PER_SLOT}, + commitment_config::CommitmentConfig, epoch_schedule::EpochSchedule, genesis_block::{GenesisBlock, OperatingMode}, message::Message, @@ -386,6 +387,7 @@ impl LocalCluster { self.entry_point_info.clone(), archiver_keypair, storage_keypair, + CommitmentConfig::recent(), ) .unwrap_or_else(|err| panic!("Archiver::new() failed: {:?}", err)); @@ -424,7 +426,9 @@ impl LocalCluster { lamports: u64, ) -> u64 { trace!("getting leader blockhash"); - let (blockhash, _fee_calculator) = client.get_recent_blockhash().unwrap(); + let (blockhash, _fee_calculator) = client + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) + .unwrap(); let mut tx = system_transaction::transfer(&source_keypair, dest_pubkey, lamports, blockhash); info!( @@ -437,7 +441,11 @@ impl LocalCluster { .retry_transfer(&source_keypair, &mut tx, 10) .expect("client transfer"); client - .wait_for_balance(dest_pubkey, Some(lamports)) + .wait_for_balance_with_commitment( + dest_pubkey, + Some(lamports), + CommitmentConfig::recent(), + ) .expect("get balance") } @@ -453,7 +461,11 @@ impl LocalCluster { let stake_account_pubkey = stake_account_keypair.pubkey(); // Create the vote account if necessary - if client.poll_get_balance(&vote_account_pubkey).unwrap_or(0) == 0 { + if client + .poll_get_balance_with_commitment(&vote_account_pubkey, CommitmentConfig::recent()) + .unwrap_or(0) + == 0 + { // 1) Create vote account let mut transaction = Transaction::new_signed_instructions( @@ -469,13 +481,20 @@ impl LocalCluster { }, amount, ), - client.get_recent_blockhash().unwrap().0, + client + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) + .unwrap() + .0, ); client .retry_transfer(&from_account, &mut transaction, 10) .expect("fund vote"); client - .wait_for_balance(&vote_account_pubkey, Some(amount)) + .wait_for_balance_with_commitment( + &vote_account_pubkey, + Some(amount), + CommitmentConfig::recent(), + ) .expect("get balance"); let mut transaction = Transaction::new_signed_instructions( @@ -487,7 +506,10 @@ impl LocalCluster { &StakeAuthorized::auto(&stake_account_pubkey), amount, ), - client.get_recent_blockhash().unwrap().0, + client + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) + .unwrap() + .0, ); client @@ -499,7 +521,11 @@ impl LocalCluster { ) .expect("delegate stake"); client - .wait_for_balance(&stake_account_pubkey, Some(amount)) + .wait_for_balance_with_commitment( + &stake_account_pubkey, + Some(amount), + CommitmentConfig::recent(), + ) .expect("get balance"); } else { warn!( @@ -509,8 +535,8 @@ impl LocalCluster { } info!("Checking for vote account registration of {}", node_pubkey); match ( - client.get_account(&stake_account_pubkey), - client.get_account(&vote_account_pubkey), + client.get_account_with_commitment(&stake_account_pubkey, CommitmentConfig::recent()), + client.get_account_with_commitment(&vote_account_pubkey, CommitmentConfig::recent()), ) { (Ok(Some(stake_account)), Ok(Some(vote_account))) => { match ( @@ -568,7 +594,10 @@ impl LocalCluster { Some(&from_keypair.pubkey()), ); let signer_keys = vec![from_keypair.as_ref()]; - let blockhash = client.get_recent_blockhash().unwrap().0; + let blockhash = client + .get_recent_blockhash_with_commitment(CommitmentConfig::recent()) + .unwrap() + .0; let mut transaction = Transaction::new(&signer_keys, message, blockhash); client .retry_transfer(&from_keypair, &mut transaction, 10) diff --git a/local_cluster/src/tests/archiver.rs b/local_cluster/src/tests/archiver.rs index 41518ab4c2..4f200d9acc 100644 --- a/local_cluster/src/tests/archiver.rs +++ b/local_cluster/src/tests/archiver.rs @@ -1,17 +1,24 @@ use crate::local_cluster::{ClusterConfig, LocalCluster}; use serial_test_derive::serial; use solana_client::thin_client::create_client; -use solana_core::archiver::Archiver; -use solana_core::cluster_info::{ClusterInfo, Node, VALIDATOR_PORT_RANGE}; -use solana_core::contact_info::ContactInfo; -use solana_core::gossip_service::discover_cluster; -use solana_core::storage_stage::SLOTS_PER_TURN_TEST; -use solana_core::validator::ValidatorConfig; +use solana_core::{ + archiver::Archiver, + cluster_info::{ClusterInfo, Node, VALIDATOR_PORT_RANGE}, + contact_info::ContactInfo, + gossip_service::discover_cluster, + storage_stage::SLOTS_PER_TURN_TEST, + validator::ValidatorConfig, +}; use solana_ledger::blocktree::{create_new_tmp_ledger, get_tmp_ledger_path, Blocktree}; -use solana_sdk::genesis_block::create_genesis_block; -use solana_sdk::signature::{Keypair, KeypairUtil}; -use std::fs::remove_dir_all; -use std::sync::{Arc, RwLock}; +use solana_sdk::{ + commitment_config::CommitmentConfig, + genesis_block::create_genesis_block, + signature::{Keypair, KeypairUtil}, +}; +use std::{ + fs::remove_dir_all, + sync::{Arc, RwLock}, +}; /// Start the cluster with the given configuration and wait till the archivers are discovered /// Then download blobs from one of them. @@ -99,6 +106,7 @@ fn test_archiver_startup_leader_hang() { leader_info, archiver_keypair, storage_keypair, + CommitmentConfig::recent(), ); assert!(archiver_res.is_err()); @@ -134,6 +142,7 @@ fn test_archiver_startup_ledger_hang() { cluster.entry_point_info.clone(), bad_keys, storage_keypair, + CommitmentConfig::recent(), ); assert!(archiver_res.is_err()); @@ -168,7 +177,10 @@ fn test_account_setup() { cluster.archiver_infos.iter().for_each(|(_, value)| { assert_eq!( client - .poll_get_balance(&value.archiver_storage_pubkey) + .poll_get_balance_with_commitment( + &value.archiver_storage_pubkey, + CommitmentConfig::recent() + ) .unwrap(), 1 ); diff --git a/local_cluster/src/tests/local_cluster.rs b/local_cluster/src/tests/local_cluster.rs index 9ad51570cc..3bb10abbf2 100644 --- a/local_cluster/src/tests/local_cluster.rs +++ b/local_cluster/src/tests/local_cluster.rs @@ -15,14 +15,15 @@ use solana_runtime::accounts_db::AccountsDB; use solana_sdk::{ client::SyncClient, clock, + commitment_config::CommitmentConfig, epoch_schedule::{EpochSchedule, MINIMUM_SLOTS_PER_EPOCH}, genesis_block::OperatingMode, poh_config::PohConfig, }; -use std::path::{Path, PathBuf}; use std::{ collections::{HashMap, HashSet}, fs, + path::{Path, PathBuf}, thread::sleep, time::Duration, }; @@ -329,7 +330,12 @@ fn test_softlaunch_operating_mode() { .iter() { assert_eq!( - (program_id, client.get_account(program_id).unwrap()), + ( + program_id, + client + .get_account_with_commitment(program_id, CommitmentConfig::recent()) + .unwrap() + ), (program_id, None) ); } @@ -460,7 +466,7 @@ fn test_snapshots_blocktree_floor() { let target_slot = slot_floor + 40; while current_slot <= target_slot { trace!("current_slot: {}", current_slot); - if let Ok(slot) = validator_client.get_slot() { + if let Ok(slot) = validator_client.get_slot_with_commitment(CommitmentConfig::recent()) { current_slot = slot; } else { continue; @@ -751,7 +757,7 @@ fn run_repairman_catchup(num_repairmen: u64) { let target_slot = (num_warmup_epochs) * num_slots_per_epoch + 1; while current_slot <= target_slot { trace!("current_slot: {}", current_slot); - if let Ok(slot) = repairee_client.get_slot() { + if let Ok(slot) = repairee_client.get_slot_with_commitment(CommitmentConfig::recent()) { current_slot = slot; } else { continue; @@ -765,7 +771,9 @@ fn wait_for_next_snapshot>(cluster: &LocalCluster, tar: P) { let client = cluster .get_validator_client(&cluster.entry_point_info.id) .unwrap(); - let last_slot = client.get_slot().expect("Couldn't get slot"); + let last_slot = client + .get_slot_with_commitment(CommitmentConfig::recent()) + .expect("Couldn't get slot"); // Wait for a snapshot for a bank >= last_slot to be made so we know that the snapshot // must include the transactions just pushed diff --git a/runtime/src/bank_client.rs b/runtime/src/bank_client.rs index d76f3fdde7..3b3241aa67 100644 --- a/runtime/src/bank_client.rs +++ b/runtime/src/bank_client.rs @@ -1,22 +1,27 @@ use crate::bank::Bank; -use solana_sdk::account::Account; -use solana_sdk::client::{AsyncClient, Client, SyncClient}; -use solana_sdk::fee_calculator::FeeCalculator; -use solana_sdk::hash::Hash; -use solana_sdk::instruction::Instruction; -use solana_sdk::message::Message; -use solana_sdk::pubkey::Pubkey; -use solana_sdk::signature::Signature; -use solana_sdk::signature::{Keypair, KeypairUtil}; -use solana_sdk::system_instruction; -use solana_sdk::transaction::{self, Transaction}; -use solana_sdk::transport::{Result, TransportError}; -use std::io; -use std::sync::mpsc::{channel, Receiver, Sender}; -use std::sync::Arc; -use std::sync::Mutex; -use std::thread::{sleep, Builder}; -use std::time::{Duration, Instant}; +use solana_sdk::{ + account::Account, + client::{AsyncClient, Client, SyncClient}, + commitment_config::CommitmentConfig, + fee_calculator::FeeCalculator, + hash::Hash, + instruction::Instruction, + message::Message, + pubkey::Pubkey, + signature::{Keypair, KeypairUtil, Signature}, + system_instruction, + transaction::{self, Transaction}, + transport::{Result, TransportError}, +}; +use std::{ + io, + sync::{ + mpsc::{channel, Receiver, Sender}, + Arc, Mutex, + }, + thread::{sleep, Builder}, + time::{Duration, Instant}, +}; pub struct BankClient { bank: Arc, @@ -100,14 +105,37 @@ impl SyncClient for BankClient { Ok(self.bank.get_account(pubkey)) } + fn get_account_with_commitment( + &self, + pubkey: &Pubkey, + _commitment_config: CommitmentConfig, + ) -> Result> { + Ok(self.bank.get_account(pubkey)) + } + fn get_balance(&self, pubkey: &Pubkey) -> Result { Ok(self.bank.get_balance(pubkey)) } + fn get_balance_with_commitment( + &self, + pubkey: &Pubkey, + _commitment_config: CommitmentConfig, + ) -> Result { + Ok(self.bank.get_balance(pubkey)) + } + fn get_recent_blockhash(&self) -> Result<(Hash, FeeCalculator)> { Ok(self.bank.last_blockhash_with_fee_calculator()) } + fn get_recent_blockhash_with_commitment( + &self, + _commitment_config: CommitmentConfig, + ) -> Result<(Hash, FeeCalculator)> { + Ok(self.bank.last_blockhash_with_fee_calculator()) + } + fn get_signature_status( &self, signature: &Signature, @@ -115,14 +143,33 @@ impl SyncClient for BankClient { Ok(self.bank.get_signature_status(signature)) } + fn get_signature_status_with_commitment( + &self, + signature: &Signature, + _commitment_config: CommitmentConfig, + ) -> Result>> { + Ok(self.bank.get_signature_status(signature)) + } + fn get_slot(&self) -> Result { Ok(self.bank.slot()) } + fn get_slot_with_commitment(&self, _commitment_config: CommitmentConfig) -> Result { + Ok(self.bank.slot()) + } + fn get_transaction_count(&self) -> Result { Ok(self.bank.transaction_count()) } + fn get_transaction_count_with_commitment( + &self, + _commitment_config: CommitmentConfig, + ) -> Result { + Ok(self.bank.transaction_count()) + } + fn poll_for_signature_confirmation( &self, signature: &Signature, diff --git a/sdk/src/client.rs b/sdk/src/client.rs index b35885b5b9..ea78c52deb 100644 --- a/sdk/src/client.rs +++ b/sdk/src/client.rs @@ -10,6 +10,7 @@ use crate::{ account::Account, clock::Slot, + commitment_config::CommitmentConfig, fee_calculator::FeeCalculator, hash::Hash, instruction::Instruction, @@ -44,24 +45,60 @@ pub trait SyncClient { /// Get an account or None if not found. fn get_account(&self, pubkey: &Pubkey) -> Result>; + /// Get an account or None if not found. Uses explicit commitment configuration. + fn get_account_with_commitment( + &self, + pubkey: &Pubkey, + commitment_config: CommitmentConfig, + ) -> Result>; + /// Get account balance or 0 if not found. fn get_balance(&self, pubkey: &Pubkey) -> Result; + /// Get account balance or 0 if not found. Uses explicit commitment configuration. + fn get_balance_with_commitment( + &self, + pubkey: &Pubkey, + commitment_config: CommitmentConfig, + ) -> Result; + /// Get recent blockhash fn get_recent_blockhash(&self) -> Result<(Hash, FeeCalculator)>; + /// Get recent blockhash. Uses explicit commitment configuration. + fn get_recent_blockhash_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> Result<(Hash, FeeCalculator)>; + /// Get signature status. fn get_signature_status( &self, signature: &Signature, ) -> Result>>; + /// Get signature status. Uses explicit commitment configuration. + fn get_signature_status_with_commitment( + &self, + signature: &Signature, + commitment_config: CommitmentConfig, + ) -> Result>>; + /// Get last known slot fn get_slot(&self) -> Result; + /// Get last known slot. Uses explicit commitment configuration. + fn get_slot_with_commitment(&self, commitment_config: CommitmentConfig) -> Result; + /// Get transaction count fn get_transaction_count(&self) -> Result; + /// Get transaction count. Uses explicit commitment configuration. + fn get_transaction_count_with_commitment( + &self, + commitment_config: CommitmentConfig, + ) -> Result; + /// Poll until the signature has been confirmed by at least `min_confirmed_blocks` fn poll_for_signature_confirmation( &self, diff --git a/sdk/src/commitment_config.rs b/sdk/src/commitment_config.rs new file mode 100644 index 0000000000..74eaee8f31 --- /dev/null +++ b/sdk/src/commitment_config.rs @@ -0,0 +1,36 @@ +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct CommitmentConfig { + pub commitment: CommitmentLevel, +} + +impl Default for CommitmentConfig { + fn default() -> Self { + CommitmentConfig { + commitment: CommitmentLevel::Max, + } + } +} + +impl CommitmentConfig { + pub fn recent() -> Self { + Self { + commitment: CommitmentLevel::Recent, + } + } + + pub fn ok(&self) -> Option { + if self == &Self::default() { + None + } else { + Some(self.clone()) + } + } +} + +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] +#[serde(rename_all = "camelCase")] +pub enum CommitmentLevel { + Max, + Recent, +} diff --git a/sdk/src/lib.rs b/sdk/src/lib.rs index 1173308e6a..2f1ff794c3 100644 --- a/sdk/src/lib.rs +++ b/sdk/src/lib.rs @@ -35,6 +35,8 @@ pub mod bank_hash; #[cfg(not(feature = "program"))] pub mod client; #[cfg(not(feature = "program"))] +pub mod commitment_config; +#[cfg(not(feature = "program"))] pub mod genesis_block; #[cfg(not(feature = "program"))] pub mod packet;