diff --git a/account-decoder/src/lib.rs b/account-decoder/src/lib.rs index 3955d0d660..dd8c146cd5 100644 --- a/account-decoder/src/lib.rs +++ b/account-decoder/src/lib.rs @@ -33,7 +33,7 @@ pub type StringDecimals = String; pub const MAX_BASE58_BYTES: usize = 128; /// A duplicate representation of an Account for pretty JSON serialization -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub struct UiAccount { pub lamports: u64, diff --git a/client/src/rpc_response.rs b/client/src/rpc_response.rs index a625072ebb..abed3d5393 100644 --- a/client/src/rpc_response.rs +++ b/client/src/rpc_response.rs @@ -117,7 +117,7 @@ pub struct RpcInflationRate { pub epoch: Epoch, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RpcKeyedAccount { pub pubkey: String, @@ -246,7 +246,7 @@ pub struct RpcBlockProductionRange { pub last_slot: Slot, } -#[derive(Serialize, Deserialize, Clone)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RpcBlockProduction { /// Map of leader base58 identity pubkeys to a tuple of `(number of leader slots, number of blocks produced)` @@ -363,7 +363,7 @@ pub struct RpcAccountBalance { pub lamports: u64, } -#[derive(Serialize, Deserialize, Clone, Debug)] +#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] #[serde(rename_all = "camelCase")] pub struct RpcSupply { pub total: u64, diff --git a/rpc/src/rpc.rs b/rpc/src/rpc.rs index 8a06896a02..a4e143ba93 100644 --- a/rpc/src/rpc.rs +++ b/rpc/src/rpc.rs @@ -4376,7 +4376,14 @@ pub mod tests { bincode::deserialize, jsonrpc_core::{futures, ErrorCode, MetaIoHandler, Output, Response, Value}, jsonrpc_core_client::transports::local, - solana_client::rpc_filter::{Memcmp, MemcmpEncodedBytes}, + serde::de::DeserializeOwned, + solana_client::{ + rpc_custom_error::{ + JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE, + JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE, + }, + rpc_filter::{Memcmp, MemcmpEncodedBytes}, + }, solana_gossip::{contact_info::ContactInfo, socketaddr}, solana_ledger::{ blockstore_meta::PerfSample, @@ -4388,12 +4395,11 @@ pub mod tests { non_circulating_supply::non_circulating_accounts, }, solana_sdk::{ - account::Account, + account::{Account, WritableAccount}, clock::MAX_RECENT_BLOCKHASHES, fee_calculator::DEFAULT_BURN_PERCENT, hash::{hash, Hash}, instruction::InstructionError, - message::Message, nonce, rpc_port, signature::{Keypair, Signer}, system_program, system_transaction, @@ -4402,11 +4408,11 @@ pub mod tests { }, solana_transaction_status::{ EncodedConfirmedBlock, EncodedTransaction, EncodedTransactionWithStatusMeta, - TransactionDetails, UiMessage, + TransactionDetails, }, solana_vote_program::{ vote_instruction, - vote_state::{BlockTimestamp, Vote, VoteInit, VoteStateVersions, MAX_LOCKOUT_HISTORY}, + vote_state::{Vote, VoteInit, VoteStateVersions, MAX_LOCKOUT_HISTORY}, }, spl_token::{ solana_program::{program_option::COption, pubkey::Pubkey as SplTokenPubkey}, @@ -4422,213 +4428,235 @@ pub mod tests { const TEST_MINT_LAMPORTS: u64 = 1_000_000; const TEST_SLOTS_PER_EPOCH: u64 = DELINQUENT_VALIDATOR_SLOT_DISTANCE + 1; + fn create_test_request(method: &str, params: Option) -> serde_json::Value { + json!({ + "jsonrpc": "2.0", + "id": 1u64, + "method": method, + "params": params, + }) + } + + fn parse_success_result(response: Response) -> T { + if let Response::Single(output) = response { + match output { + Output::Success(success) => serde_json::from_value(success.result).unwrap(), + Output::Failure(failure) => { + panic!("Expected success but received: {:?}", failure); + } + } + } else { + panic!("Expected single response"); + } + } + + fn parse_failure_response(response: Response) -> (i64, String) { + if let Response::Single(output) = response { + match output { + Output::Success(success) => { + panic!("Expected failure but received: {:?}", success); + } + Output::Failure(failure) => (failure.error.code.code(), failure.error.message), + } + } else { + panic!("Expected single response"); + } + } + struct RpcHandler { io: MetaIoHandler, meta: JsonRpcRequestProcessor, - bank: Arc, - bank_forks: Arc>, - blockhash: Hash, - alice: Keypair, - leader_pubkey: Pubkey, + identity: Pubkey, + mint_keypair: Keypair, leader_vote_keypair: Arc, + blockstore: Arc, + bank_forks: Arc>, + max_slots: Arc, + max_complete_transaction_status_slot: Arc, block_commitment_cache: Arc>, - confirmed_block_signatures: Vec, } - fn start_rpc_handler_with_tx(pubkey: &Pubkey) -> RpcHandler { - start_rpc_handler_with_tx_and_blockstore(pubkey, vec![]) - } + impl RpcHandler { + fn start() -> Self { + let (bank_forks, mint_keypair, leader_vote_keypair) = new_bank_forks(); + let ledger_path = get_tmp_ledger_path!(); + let blockstore = Arc::new(Blockstore::open(&ledger_path).unwrap()); + let bank = bank_forks.read().unwrap().working_bank(); - fn start_rpc_handler_with_tx_and_blockstore( - pubkey: &Pubkey, - blockstore_roots: Vec, - ) -> RpcHandler { - let (bank_forks, alice, leader_vote_keypair) = new_bank_forks(); - let bank = bank_forks.read().unwrap().working_bank(); - let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); + let identity = Pubkey::new_unique(); + let leader_pubkey = *bank.collector_id(); + let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::default())); + let exit = Arc::new(AtomicBool::new(false)); + let validator_exit = create_validator_exit(&exit); + let cluster_info = Arc::new(ClusterInfo::new( + ContactInfo { + id: identity, + ..ContactInfo::default() + }, + Arc::new(Keypair::new()), + SocketAddrSpace::Unspecified, + )); + cluster_info.insert_info(ContactInfo::new_with_pubkey_socketaddr( + &leader_pubkey, + &socketaddr!("127.0.0.1:1234"), + )); + let max_slots = Arc::new(MaxSlots::default()); + // note that this means that slot 0 will always be considered complete + let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(0)); - let vote_pubkey = leader_vote_keypair.pubkey(); - let mut vote_account = bank.get_account(&vote_pubkey).unwrap_or_default(); - let mut vote_state = VoteState::from(&vote_account).unwrap_or_default(); - vote_state.last_timestamp = BlockTimestamp { - slot: bank.slot(), - timestamp: bank.clock().unix_timestamp, - }; - let versioned = VoteStateVersions::new_current(vote_state); - VoteState::to(&versioned, &mut vote_account).unwrap(); - bank.store_account(&vote_pubkey, &vote_account); + let meta = JsonRpcRequestProcessor::new( + JsonRpcConfig { + enable_rpc_transaction_history: true, + ..JsonRpcConfig::default() + }, + None, + bank_forks.clone(), + block_commitment_cache.clone(), + blockstore.clone(), + validator_exit, + RpcHealth::stub(), + cluster_info, + Hash::default(), + None, + OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), + Arc::new(RwLock::new(LargestAccountsCache::new(30))), + max_slots.clone(), + Arc::new(LeaderScheduleCache::new_from_bank(&bank)), + max_complete_transaction_status_slot.clone(), + ) + .0; - let ledger_path = get_tmp_ledger_path!(); - let blockstore = Blockstore::open(&ledger_path).unwrap(); - let blockstore = Arc::new(blockstore); + let mut io = MetaIoHandler::default(); + io.extend_with(rpc_minimal::MinimalImpl.to_delegate()); + io.extend_with(rpc_bank::BankDataImpl.to_delegate()); + io.extend_with(rpc_accounts::AccountsDataImpl.to_delegate()); + io.extend_with(rpc_full::FullImpl.to_delegate()); + io.extend_with(rpc_deprecated_v1_9::DeprecatedV1_9Impl.to_delegate()); + Self { + io, + meta, + identity, + mint_keypair, + leader_vote_keypair, + bank_forks, + blockstore, + max_slots, + max_complete_transaction_status_slot, + block_commitment_cache, + } + } - let keypair1 = Keypair::new(); - let keypair2 = Keypair::new(); - let keypair3 = Keypair::new(); - bank.transfer(rent_exempt_amount, &alice, &keypair2.pubkey()) - .unwrap(); - let max_complete_transaction_status_slot = Arc::new(AtomicU64::new(blockstore.max_root())); - let confirmed_block_signatures = create_test_transactions_and_populate_blockstore( - vec![&alice, &keypair1, &keypair2, &keypair3], - 0, - bank.clone(), - blockstore.clone(), - max_complete_transaction_status_slot.clone(), - ); + fn handle_request_sync(&self, req: serde_json::Value) -> Response { + let response = &self + .io + .handle_request_sync(&req.to_string(), self.meta.clone()) + .expect("no response"); + serde_json::from_str(response).expect("failed to deserialize response") + } - let mut commitment_slot0 = BlockCommitment::default(); - commitment_slot0.increase_confirmation_stake(2, 9); - let mut commitment_slot1 = BlockCommitment::default(); - commitment_slot1.increase_confirmation_stake(1, 9); - let mut block_commitment: HashMap = HashMap::new(); - block_commitment.entry(0).or_insert(commitment_slot0); - block_commitment.entry(1).or_insert(commitment_slot1); - let block_commitment_cache = Arc::new(RwLock::new(BlockCommitmentCache::new( - block_commitment, - 10, - CommitmentSlots::new_from_slot(bank.slot()), - ))); + fn create_test_transactions_and_populate_blockstore(&self) -> Vec { + let mint_keypair = &self.mint_keypair; + let keypair1 = Keypair::new(); + let keypair2 = Keypair::new(); + let keypair3 = Keypair::new(); + let bank = self.bank_forks.read().unwrap().working_bank(); + let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); + bank.transfer(rent_exempt_amount, mint_keypair, &keypair2.pubkey()) + .unwrap(); + create_test_transactions_and_populate_blockstore( + vec![&self.mint_keypair, &keypair1, &keypair2, &keypair3], + 0, + bank, + self.blockstore.clone(), + self.max_complete_transaction_status_slot.clone(), + ) + } - let mut roots = blockstore_roots; - if !roots.is_empty() { - roots.retain(|&x| x > 0); - let mut parent_bank = bank; + fn add_roots_to_blockstore(&self, mut roots: Vec) { + roots.retain(|&slot| slot > 0); + if roots.is_empty() { + return; + } + + let mut parent_bank = self.bank_forks.read().unwrap().working_bank(); for (i, root) in roots.iter().enumerate() { let new_bank = Bank::new_from_parent(&parent_bank, parent_bank.collector_id(), *root); - parent_bank = bank_forks.write().unwrap().insert(new_bank); + parent_bank = self.bank_forks.write().unwrap().insert(new_bank); let parent = if i > 0 { roots[i - 1] } else { 0 }; - fill_blockstore_slot_with_ticks(&blockstore, 5, *root, parent, Hash::default()); + fill_blockstore_slot_with_ticks( + &self.blockstore, + 5, + *root, + parent, + Hash::default(), + ); } - blockstore.set_roots(roots.iter()).unwrap(); + self.blockstore.set_roots(roots.iter()).unwrap(); let new_bank = Bank::new_from_parent( &parent_bank, parent_bank.collector_id(), roots.iter().max().unwrap() + 1, ); - bank_forks.write().unwrap().insert(new_bank); + self.bank_forks.write().unwrap().insert(new_bank); for root in roots.iter() { - bank_forks - .write() - .unwrap() - .set_root(*root, &AbsRequestSender::default(), Some(0)); - let mut stakes = HashMap::new(); - stakes.insert( - leader_vote_keypair.pubkey(), - (1, AccountSharedData::default()), + self.bank_forks.write().unwrap().set_root( + *root, + &AbsRequestSender::default(), + Some(0), ); - let block_time = bank_forks + let block_time = self + .bank_forks .read() .unwrap() .get(*root) .unwrap() .clock() .unix_timestamp; - blockstore.cache_block_time(*root, block_time).unwrap(); + self.blockstore.cache_block_time(*root, block_time).unwrap(); } } - let bank = bank_forks.read().unwrap().working_bank(); + fn advance_bank_to_confirmed_slot(&self, slot: Slot) -> Arc { + let parent_bank = self.working_bank(); + let bank = self + .bank_forks + .write() + .unwrap() + .insert(Bank::new_from_parent( + &parent_bank, + &Pubkey::default(), + slot, + )); - let leader_pubkey = *bank.collector_id(); - let exit = Arc::new(AtomicBool::new(false)); - let validator_exit = create_validator_exit(&exit); + let mut new_block_commitment = BlockCommitmentCache::new( + HashMap::new(), + 0, + CommitmentSlots::new_from_slot(self.bank_forks.read().unwrap().highest_slot()), + ); + let mut w_block_commitment_cache = self.block_commitment_cache.write().unwrap(); + std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment); + bank + } - let blockhash = bank.confirmed_last_blockhash(); - let tx = system_transaction::transfer(&alice, pubkey, rent_exempt_amount, blockhash); - bank.process_transaction(&tx).expect("process transaction"); - let tx = system_transaction::transfer( - &alice, - &non_circulating_accounts()[0], - rent_exempt_amount, - blockhash, - ); - bank.process_transaction(&tx).expect("process transaction"); + fn store_vote_account(&self, vote_pubkey: &Pubkey, vote_state: VoteState) { + let bank = self.working_bank(); + let versioned = VoteStateVersions::new_current(vote_state); + let space = VoteState::size_of(); + let balance = bank.get_minimum_balance_for_rent_exemption(space); + let mut vote_account = + AccountSharedData::new(balance, space, &solana_vote_program::id()); + VoteState::to(&versioned, &mut vote_account).unwrap(); + bank.store_account(vote_pubkey, &vote_account); + } - let tx = system_transaction::transfer(&alice, pubkey, std::u64::MAX, blockhash); - let _ = bank.process_transaction(&tx); + fn working_bank(&self) -> Arc { + self.bank_forks.read().unwrap().working_bank() + } - let cluster_info = Arc::new(ClusterInfo::new( - ContactInfo { - id: alice.pubkey(), - ..ContactInfo::default() - }, - Arc::new(Keypair::new()), - SocketAddrSpace::Unspecified, - )); - let tpu_address = cluster_info.my_contact_info().tpu; - - cluster_info.insert_info(ContactInfo::new_with_pubkey_socketaddr( - &leader_pubkey, - &socketaddr!("127.0.0.1:1234"), - )); - - let sample1 = PerfSample { - num_slots: 1, - num_transactions: 4, - sample_period_secs: 60, - }; - - blockstore - .write_perf_sample(0, &sample1) - .expect("write to blockstore"); - - let max_slots = Arc::new(MaxSlots::default()); - max_slots.retransmit.store(42, Ordering::Relaxed); - max_slots.shred_insert.store(43, Ordering::Relaxed); - - let (meta, receiver) = JsonRpcRequestProcessor::new( - JsonRpcConfig { - enable_rpc_transaction_history: true, - ..JsonRpcConfig::default() - }, - None, - bank_forks.clone(), - block_commitment_cache.clone(), - blockstore, - validator_exit, - RpcHealth::stub(), - cluster_info.clone(), - Hash::default(), - None, - OptimisticallyConfirmedBank::locked_from_bank_forks_root(&bank_forks), - Arc::new(RwLock::new(LargestAccountsCache::new(30))), - max_slots, - Arc::new(LeaderScheduleCache::new_from_bank(&bank)), - max_complete_transaction_status_slot, - ); - SendTransactionService::new::( - tpu_address, - &bank_forks, - None, - receiver, - 1000, - 1, - ); - - cluster_info.insert_info(ContactInfo::new_with_pubkey_socketaddr( - &leader_pubkey, - &socketaddr!("127.0.0.1:1234"), - )); - - let mut io = MetaIoHandler::default(); - io.extend_with(rpc_minimal::MinimalImpl.to_delegate()); - io.extend_with(rpc_bank::BankDataImpl.to_delegate()); - io.extend_with(rpc_accounts::AccountsDataImpl.to_delegate()); - io.extend_with(rpc_full::FullImpl.to_delegate()); - io.extend_with(rpc_deprecated_v1_9::DeprecatedV1_9Impl.to_delegate()); - RpcHandler { - io, - meta, - bank, - bank_forks, - blockhash, - alice, - leader_pubkey, - leader_vote_keypair, - block_commitment_cache, - confirmed_block_signatures, + fn leader_pubkey(&self) -> Pubkey { + *self.working_bank().collector_id() } } @@ -4669,7 +4697,7 @@ pub mod tests { }); let result = serde_json::from_str::(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] @@ -4703,104 +4731,70 @@ pub mod tests { #[test] fn test_rpc_get_cluster_nodes() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, - meta, - leader_pubkey, - .. - } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getClusterNodes"}"#; - - let res = io.handle_request_sync(req, meta); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - - let expected = format!( - r#"{{"jsonrpc":"2.0","result":[{{"pubkey": "{}", "gossip": "127.0.0.1:1235", "shredVersion": 0, "tpu": "127.0.0.1:1234", "rpc": "127.0.0.1:{}", "version": null, "featureSet": null}}],"id":1}}"#, - leader_pubkey, - rpc_port::DEFAULT_RPC_PORT - ); - - let expected: Response = - serde_json::from_str(&expected).expect("expected response deserialization"); - assert_eq!(expected, result); + let rpc = RpcHandler::start(); + let request = create_test_request("getClusterNodes", None); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); + let expected = json!([{ + "pubkey": rpc.leader_pubkey().to_string(), + "gossip": "127.0.0.1:1235", + "shredVersion": 0u16, + "tpu": "127.0.0.1:1234", + "rpc": format!("127.0.0.1:{}", rpc_port::DEFAULT_RPC_PORT), + "version": null, + "featureSet": null, + }]); + assert_eq!(result, expected); } #[test] fn test_rpc_get_recent_performance_samples() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getRecentPerformanceSamples"}"#; + let slot = 0; + let num_slots = 1; + let num_transactions = 4; + let sample_period_secs = 60; + rpc.blockstore + .write_perf_sample( + slot, + &PerfSample { + num_slots, + num_transactions, + sample_period_secs, + }, + ) + .expect("write to blockstore"); - let res = io.handle_request_sync(req, meta); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - - let expected = json!({ - "jsonrpc": "2.0", - "id": 1, - "result": [ - { - "slot": 0, - "numSlots": 1, - "numTransactions": 4, - "samplePeriodSecs": 60 - } - ], - }); - - let expected: Response = - serde_json::from_value(expected).expect("expected response deserialization"); - assert_eq!(expected, result); + let request = create_test_request("getRecentPerformanceSamples", None); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); + let expected = json!([{ + "slot": slot, + "numSlots": num_slots, + "numTransactions": num_transactions, + "samplePeriodSecs": sample_period_secs, + }]); + assert_eq!(result, expected); } #[test] fn test_rpc_get_recent_performance_samples_invalid_limit() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = - r#"{"jsonrpc":"2.0","id":1,"method":"getRecentPerformanceSamples","params":[10000]}"#; - - let res = io.handle_request_sync(req, meta); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - - let expected = json!({ - "jsonrpc": "2.0", - "error": { - "code": -32602, - "message": "Invalid limit; max 720" - }, - "id": 1 - }); - - let expected: Response = - serde_json::from_value(expected).expect("expected response deserialization"); - assert_eq!(expected, result); + let rpc = RpcHandler::start(); + let request = create_test_request("getRecentPerformanceSamples", Some(json!([10_000]))); + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + ErrorCode::InvalidParams.code(), + String::from("Invalid limit; max 720"), + ); + assert_eq!(response, expected); } #[test] fn test_rpc_get_slot_leader() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, - meta, - leader_pubkey, - .. - } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSlotLeader"}"#; - let res = io.handle_request_sync(req, meta); - let expected = format!(r#"{{"jsonrpc":"2.0","result":"{}","id":1}}"#, leader_pubkey); - let expected: Response = - serde_json::from_str(&expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); + let rpc = RpcHandler::start(); + let request = create_test_request("getSlotLeader", None); + let result: String = parse_success_result(rpc.handle_request_sync(request)); + let expected = rpc.leader_pubkey().to_string(); + assert_eq!(result, expected); } #[test] @@ -4830,814 +4824,595 @@ pub mod tests { serde_json::from_str(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] fn test_rpc_minimum_ledger_slot() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = r#"{"jsonrpc":"2.0","id":1,"method":"minimumLedgerSlot"}"#; - let res = io.handle_request_sync(req, meta); - let expected = r#"{"jsonrpc":"2.0","result":0,"id":1}"#; - let expected: Response = - serde_json::from_str(expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); + let rpc = RpcHandler::start(); + // populate blockstore so that a minimum slot can be detected + rpc.create_test_transactions_and_populate_blockstore(); + let request = create_test_request("minimumLedgerSlot", None); + let result: Slot = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(0, result); } #[test] fn test_get_supply() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSupply"}"#; - let res = io.handle_request_sync(req, meta); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let supply: RpcSupply = serde_json::from_value(json["result"]["value"].clone()) - .expect("actual response deserialization"); - assert_eq!( - supply.non_circulating, - bank.get_minimum_balance_for_rent_exemption(0) - ); - assert!(supply.circulating >= TEST_MINT_LAMPORTS); - assert!(supply.total >= TEST_MINT_LAMPORTS + 20); - let expected_accounts: Vec = non_circulating_accounts() - .iter() - .map(|pubkey| pubkey.to_string()) - .collect(); - assert_eq!( - supply.non_circulating_accounts.len(), - expected_accounts.len() - ); - for address in supply.non_circulating_accounts { - assert!(expected_accounts.contains(&address)); - } + let rpc = RpcHandler::start(); + let request = create_test_request("getSupply", None); + let result = { + let mut result: RpcResponse = + parse_success_result(rpc.handle_request_sync(request)); + result.value.non_circulating_accounts.sort(); + result.value + }; + let expected = { + let mut non_circulating_accounts: Vec = non_circulating_accounts() + .iter() + .map(|pubkey| pubkey.to_string()) + .collect(); + non_circulating_accounts.sort(); + let total_capitalization = rpc.working_bank().capitalization(); + RpcSupply { + non_circulating: 0, + circulating: total_capitalization, + total: total_capitalization, + non_circulating_accounts, + } + }; + assert_eq!(result, expected); } #[test] fn test_get_supply_exclude_account_list() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getSupply","params":[{"excludeNonCirculatingAccountsList":true}]}"#; - let res = io.handle_request_sync(req, meta); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let supply: RpcSupply = serde_json::from_value(json["result"]["value"].clone()) - .expect("actual response deserialization"); - assert_eq!( - supply.non_circulating, - bank.get_minimum_balance_for_rent_exemption(0) + let rpc = RpcHandler::start(); + let request = create_test_request( + "getSupply", + Some(json!([{"excludeNonCirculatingAccountsList": true}])), ); - assert!(supply.circulating >= TEST_MINT_LAMPORTS); - assert!(supply.total >= TEST_MINT_LAMPORTS + 20); - assert!(supply.non_circulating_accounts.is_empty()); + let result: RpcResponse = parse_success_result(rpc.handle_request_sync(request)); + let expected = { + let total_capitalization = rpc.working_bank().capitalization(); + RpcSupply { + non_circulating: 0, + circulating: total_capitalization, + total: total_capitalization, + non_circulating_accounts: vec![], + } + }; + assert_eq!(result.value, expected); } #[test] fn test_get_largest_accounts() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, meta, alice, .. - } = start_rpc_handler_with_tx(&bob_pubkey); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getLargestAccounts"}"#; - let res = io.handle_request_sync(req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let largest_accounts: Vec = - serde_json::from_value(json["result"]["value"].clone()) - .expect("actual response deserialization"); - assert_eq!(largest_accounts.len(), 20); + let rpc = RpcHandler::start(); - // Get Alice balance - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBalance","params":["{}"]}}"#, - alice.pubkey() + // make a non-circulating account one of the largest accounts + let non_circulating_key = &non_circulating_accounts()[0]; + let bank = rpc.working_bank(); + bank.process_transaction(&system_transaction::transfer( + &rpc.mint_keypair, + non_circulating_key, + 500_000, + bank.confirmed_last_blockhash(), + )) + .expect("process transaction"); + + let request = create_test_request("getLargestAccounts", None); + let largest_accounts_result: RpcResponse> = + parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(largest_accounts_result.value.len(), 20); + + // Get mint balance + let request = create_test_request( + "getBalance", + Some(json!([rpc.mint_keypair.pubkey().to_string()])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let alice_balance: u64 = serde_json::from_value(json["result"]["value"].clone()) - .expect("actual response deserialization"); - assert!(largest_accounts.contains(&RpcAccountBalance { - address: alice.pubkey().to_string(), - lamports: alice_balance, + let mint_balance_result: RpcResponse = + parse_success_result(rpc.handle_request_sync(request)); + assert!(largest_accounts_result.value.contains(&RpcAccountBalance { + address: rpc.mint_keypair.pubkey().to_string(), + lamports: mint_balance_result.value, })); - // Get Bob balance - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBalance","params":["{}"]}}"#, - bob_pubkey - ); - let res = io.handle_request_sync(&req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let bob_balance: u64 = serde_json::from_value(json["result"]["value"].clone()) - .expect("actual response deserialization"); - assert!(largest_accounts.contains(&RpcAccountBalance { - address: bob_pubkey.to_string(), - lamports: bob_balance, + // Get non-circulating account balance + let request = + create_test_request("getBalance", Some(json!([non_circulating_key.to_string()]))); + let non_circulating_balance_result: RpcResponse = + parse_success_result(rpc.handle_request_sync(request)); + assert!(largest_accounts_result.value.contains(&RpcAccountBalance { + address: non_circulating_key.to_string(), + lamports: non_circulating_balance_result.value, })); // Test Circulating/NonCirculating Filter - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getLargestAccounts","params":[{"filter":"circulating"}]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let largest_accounts: Vec = - serde_json::from_value(json["result"]["value"].clone()) - .expect("actual response deserialization"); - assert_eq!(largest_accounts.len(), 20); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getLargestAccounts","params":[{"filter":"nonCirculating"}]}"#; - let res = io.handle_request_sync(req, meta); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let largest_accounts: Vec = - serde_json::from_value(json["result"]["value"].clone()) - .expect("actual response deserialization"); - assert_eq!(largest_accounts.len(), 1); + let request = create_test_request( + "getLargestAccounts", + Some(json!([{"filter":"circulating"}])), + ); + let largest_accounts_result: RpcResponse> = + parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(largest_accounts_result.value.len(), 20); + assert!(!largest_accounts_result.value.contains(&RpcAccountBalance { + address: non_circulating_key.to_string(), + lamports: non_circulating_balance_result.value, + })); + + let request = create_test_request( + "getLargestAccounts", + Some(json!([{"filter":"nonCirculating"}])), + ); + let largest_accounts_result: RpcResponse> = + parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(largest_accounts_result.value.len(), 1); + assert!(largest_accounts_result.value.contains(&RpcAccountBalance { + address: non_circulating_key.to_string(), + lamports: non_circulating_balance_result.value, + })); } #[test] fn test_rpc_get_minimum_balance_for_rent_exemption() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); + let rpc = RpcHandler::start(); let data_len = 50; - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getMinimumBalanceForRentExemption","params":[{}]}}"#, - data_len - ); - let rep = io.handle_request_sync(&req, meta); - let res: Response = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - let minimum_balance: u64 = if let Response::Single(res) = res { - if let Output::Success(res) = res { - if let Value::Number(num) = res.result { - num.as_u64().unwrap() - } else { - panic!("Expected number"); - } - } else { - panic!("Expected success"); - } - } else { - panic!("Expected single response"); - }; - assert_eq!( - minimum_balance, - bank.get_minimum_balance_for_rent_exemption(data_len) - ); + let request = + create_test_request("getMinimumBalanceForRentExemption", Some(json!([data_len]))); + let result: u64 = parse_success_result(rpc.handle_request_sync(request)); + let expected = rpc + .working_bank() + .get_minimum_balance_for_rent_exemption(data_len); + assert_eq!(result, expected); } #[test] fn test_rpc_get_inflation() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let request = create_test_request("getInflationGovernor", None); + let result: RpcInflationGovernor = parse_success_result(rpc.handle_request_sync(request)); + let expected: RpcInflationGovernor = bank.inflation().into(); + assert_eq!(result, expected); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getInflationGovernor"}"#; - let rep = io.handle_request_sync(req, meta.clone()); - let res: Response = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - let inflation_governor: RpcInflationGovernor = if let Response::Single(res) = res { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success"); - } - } else { - panic!("Expected single response"); - }; - let expected_inflation_governor: RpcInflationGovernor = bank.inflation().into(); - assert_eq!(inflation_governor, expected_inflation_governor); - - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getInflationRate"}"#; // Queries current epoch - let rep = io.handle_request_sync(req, meta); - let res: Response = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - let inflation_rate: RpcInflationRate = if let Response::Single(res) = res { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success"); - } - } else { - panic!("Expected single response"); - }; + // Query inflation rate for current epoch + let request = create_test_request("getInflationRate", None); + let result: RpcInflationRate = parse_success_result(rpc.handle_request_sync(request)); let inflation = bank.inflation(); let epoch = bank.epoch(); let slot_in_year = bank.slot_in_year_for_inflation(); - let expected_inflation_rate = RpcInflationRate { + let expected = RpcInflationRate { total: inflation.total(slot_in_year), validator: inflation.validator(slot_in_year), foundation: inflation.foundation(slot_in_year), epoch, }; - assert_eq!(inflation_rate, expected_inflation_rate); + assert_eq!(result, expected); } #[test] fn test_rpc_get_epoch_schedule() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getEpochSchedule"}"#; - let rep = io.handle_request_sync(req, meta); - let res: Response = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - - let epoch_schedule: EpochSchedule = if let Response::Single(res) = res { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success"); - } - } else { - panic!("Expected single response"); - }; - assert_eq!(epoch_schedule, *bank.epoch_schedule()); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let request = create_test_request("getEpochSchedule", None); + let result: EpochSchedule = parse_success_result(rpc.handle_request_sync(request)); + let expected = bank.epoch_schedule(); + assert_eq!(expected, &result); } #[test] fn test_rpc_get_leader_schedule() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); - for req in [ - r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [0]}"#, - r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule"}"#, - &format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [null, {{ "identity": "{}" }}]}}"#, - bank.collector_id() - ), - &format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [{{ "identity": "{}" }}]}}"#, - bank.collector_id() - ), - ] - .iter() - { - let rep = io.handle_request_sync(req, meta.clone()); - let res: Response = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - - let schedule: Option = if let Response::Single(res) = res { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success for {}", req); - } - } else { - panic!("Expected single response"); - }; - let schedule = schedule.expect("leader schedule"); - - let bob_schedule = schedule - .get(&bank.collector_id().to_string()) - .expect("leader not in the leader schedule"); - - assert_eq!( - bob_schedule.len(), - solana_ledger::leader_schedule_utils::leader_schedule(bank.epoch(), &bank) - .unwrap() - .get_slot_leaders() - .len() - ); + for params in [ + None, + Some(json!([0u64])), + Some(json!([null, {"identity": rpc.leader_pubkey().to_string()}])), + Some(json!([{"identity": rpc.leader_pubkey().to_string()}])), + ] { + let request = create_test_request("getLeaderSchedule", params); + let result: Option = + parse_success_result(rpc.handle_request_sync(request)); + let expected = Some(HashMap::from_iter(std::iter::once(( + rpc.leader_pubkey().to_string(), + Vec::from_iter(0..=128), + )))); + assert_eq!(result, expected); } - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [42424242]}"#; - let rep = io.handle_request_sync(req, meta.clone()); - let res: Response = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); + let request = create_test_request("getLeaderSchedule", Some(json!([42424242]))); + let result: Option = + parse_success_result(rpc.handle_request_sync(request)); + let expected: Option = None; + assert_eq!(result, expected); - let schedule: Option = if let Response::Single(res) = res { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success"); - } - } else { - panic!("Expected single response"); - }; - assert_eq!(schedule, None); - - // `bob` is not in the leader schedule, look for an empty response - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [{{ "identity": "{}"}}]}}"#, - bob_pubkey + let request = create_test_request( + "getLeaderSchedule", + Some(json!([{"identity": Pubkey::new_unique().to_string() }])), ); - - let rep = io.handle_request_sync(&req, meta); - let res: Response = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - - let schedule: Option = if let Response::Single(res) = res { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success"); - } - } else { - panic!("Expected single response"); - }; - assert_eq!(schedule, Some(HashMap::default())); + let result: Option = + parse_success_result(rpc.handle_request_sync(request)); + let expected = Some(HashMap::default()); + assert_eq!(result, expected); } #[test] fn test_rpc_get_slot_leaders() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); // Test that slot leaders will be returned across epochs let query_start = 0; let query_limit = 2 * bank.epoch_schedule().slots_per_epoch; - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getSlotLeaders", "params": [{}, {}]}}"#, - query_start, query_limit - ); - let rep = io.handle_request_sync(&req, meta.clone()); - let res: Response = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - - let slot_leaders: Vec = if let Response::Single(res) = res { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success for {} but received: {:?}", req, res); - } - } else { - panic!("Expected single response"); - }; - - assert_eq!(slot_leaders.len(), query_limit as usize); + let request = + create_test_request("getSlotLeaders", Some(json!([query_start, query_limit]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result.len(), query_limit as usize); // Test that invalid limit returns an error let query_start = 0; let query_limit = 5001; - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getSlotLeaders", "params": [{}, {}]}}"#, - query_start, query_limit + let request = + create_test_request("getSlotLeaders", Some(json!([query_start, query_limit]))); + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + ErrorCode::InvalidParams.code(), + String::from("Invalid limit; max 5000"), ); - let rep = io.handle_request_sync(&req, meta.clone()); - let res: Value = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - assert!(res.get("error").is_some()); + assert_eq!(response, expected); // Test that invalid epoch returns an error let query_start = 2 * bank.epoch_schedule().slots_per_epoch; let query_limit = 10; - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getSlotLeaders", "params": [{}, {}]}}"#, - query_start, query_limit + let request = + create_test_request("getSlotLeaders", Some(json!([query_start, query_limit]))); + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + ErrorCode::InvalidParams.code(), + String::from("Invalid slot range: leader schedule for epoch 2 is unavailable"), ); - let rep = io.handle_request_sync(&req, meta); - let res: Value = serde_json::from_str(&rep.expect("actual response")) - .expect("actual response deserialization"); - assert!(res.get("error").is_some()); + assert_eq!(response, expected); } #[test] fn test_rpc_get_account_info() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}"]}}"#, - bob_pubkey + let request = create_test_request( + "getAccountInfo", + Some(json!([rpc.mint_keypair.pubkey().to_string()])), ); - let res = io.handle_request_sync(&req, meta.clone()); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); let expected = json!({ - "jsonrpc": "2.0", - "result": { - "context":{"slot":0}, - "value":{ - "owner": "11111111111111111111111111111111", - "lamports": bank.get_minimum_balance_for_rent_exemption(0), - "data": "", - "executable": false, - "rentEpoch": 0 - }, + "context": {"slot": 0}, + "value":{ + "owner": "11111111111111111111111111111111", + "lamports": 1_000_000, + "data": "", + "executable": false, + "rentEpoch": 0 }, - "id": 1, }); - let expected: Response = - serde_json::from_value(expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); - let address = solana_sdk::pubkey::new_rand(); + let pubkey = Pubkey::new_unique(); + let address = pubkey.to_string(); let data = vec![1, 2, 3, 4, 5]; - let mut account = AccountSharedData::new(42, 5, &Pubkey::default()); - account.set_data(data.clone()); - bank.store_account(&address, &account); + let account = AccountSharedData::create(42, data.clone(), Pubkey::default(), false, 0); + bank.store_account(&pubkey, &account); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}", {{"encoding":"base64"}}]}}"#, - address - ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!( - result["result"]["value"]["data"], - json!([base64::encode(&data), "base64"]), + let request = create_test_request( + "getAccountInfo", + Some(json!([address, {"encoding": "base64"}])), ); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); + let expected = json!([base64::encode(&data), "base64"]); + assert_eq!(result["value"]["data"], expected); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}", {{"encoding":"base64", "dataSlice": {{"length": 2, "offset": 1}}}}]}}"#, - address - ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!( - result["result"]["value"]["data"], - json!([base64::encode(&data[1..3]), "base64"]), + let request = create_test_request( + "getAccountInfo", + Some(json!([address, {"encoding": "base64", "dataSlice": {"length": 2, "offset": 1}}])), ); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); + let expected = json!([base64::encode(&data[1..3]), "base64"]); + assert_eq!(result["value"]["data"], expected); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}", {{"encoding":"binary", "dataSlice": {{"length": 2, "offset": 1}}}}]}}"#, - address - ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!( - result["result"]["value"]["data"], - bs58::encode(&data[1..3]).into_string(), + let request = create_test_request( + "getAccountInfo", + Some(json!([address, {"encoding": "binary", "dataSlice": {"length": 2, "offset": 1}}])), ); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); + let expected = bs58::encode(&data[1..3]).into_string(); + assert_eq!(result["value"]["data"], expected); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getAccountInfo","params":["{}", {{"encoding":"jsonParsed", "dataSlice": {{"length": 2, "offset": 1}}}}]}}"#, - address + let request = create_test_request( + "getAccountInfo", + Some( + json!([address, {"encoding": "jsonParsed", "dataSlice": {"length": 2, "offset": 1}}]), + ), ); - let res = io.handle_request_sync(&req, meta); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - result["error"].as_object().unwrap(); + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + ErrorCode::InvalidRequest.code(), + String::from("Sliced account data can only be encoded using binary (base 58) or base64 encoding."), + ); + assert_eq!(response, expected); } #[test] fn test_rpc_get_multiple_accounts() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); - let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); - - let address = Pubkey::new(&[9; 32]); + let non_existent_pubkey = Pubkey::new_unique(); + let pubkey = Pubkey::new_unique(); + let address = pubkey.to_string(); let data = vec![1, 2, 3, 4, 5]; - let mut account = AccountSharedData::new(rent_exempt_amount + 1, 5, &Pubkey::default()); - account.set_data(data.clone()); - bank.store_account(&address, &account); + let account = AccountSharedData::create(42, data.clone(), Pubkey::default(), false, 0); + bank.store_account(&pubkey, &account); - let non_existent_address = Pubkey::new(&[8; 32]); - - // Test 3 accounts, one non-existent, and one with data - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getMultipleAccounts","params":[["{}", "{}", "{}"]]}}"#, - bob_pubkey, non_existent_address, address, + // Test 3 accounts, one empty, one non-existent, and one with data + let request = create_test_request( + "getMultipleAccounts", + Some(json!([[ + rpc.mint_keypair.pubkey().to_string(), + non_existent_pubkey.to_string(), + address, + ]])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let expected = json!({ - "jsonrpc": "2.0", - "result": { - "context":{"slot":0}, - "value":[{ - "owner": "11111111111111111111111111111111", - "lamports": rent_exempt_amount, - "data": ["", "base64"], - "executable": false, - "rentEpoch": 0 - }, - null, - { - "owner": "11111111111111111111111111111111", - "lamports": rent_exempt_amount + 1, - "data": [base64::encode(&data), "base64"], - "executable": false, - "rentEpoch": 0 - }], + let result: RpcResponse = parse_success_result(rpc.handle_request_sync(request)); + let expected = json!([ + { + "owner": "11111111111111111111111111111111", + "lamports": 1_000_000, + "data": ["", "base64"], + "executable": false, + "rentEpoch": 0 }, - "id": 1, - }); - let expected: Response = - serde_json::from_value(expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); + null, + { + "owner": "11111111111111111111111111111111", + "lamports": 42, + "data": [base64::encode(&data), "base64"], + "executable": false, + "rentEpoch": 0 + } + ]); + assert_eq!(result.value, expected); // Test config settings still work with multiple accounts - let req = format!( - r#"{{ - "jsonrpc":"2.0","id":1,"method":"getMultipleAccounts","params":[ - ["{}", "{}", "{}"], - {{"encoding":"base58"}} - ] - }}"#, - bob_pubkey, non_existent_address, address, - ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(result["result"]["value"].as_array().unwrap().len(), 3); - assert_eq!( - result["result"]["value"][2]["data"], - json!([bs58::encode(&data).into_string(), "base58"]), + let request = create_test_request( + "getMultipleAccounts", + Some(json!([ + [ + rpc.mint_keypair.pubkey().to_string(), + non_existent_pubkey.to_string(), + address, + ], + {"encoding": "base58"}, + ])), ); + let result: RpcResponse = parse_success_result(rpc.handle_request_sync(request)); + let expected = json!([ + { + "owner": "11111111111111111111111111111111", + "lamports": 1_000_000, + "data": ["", "base58"], + "executable": false, + "rentEpoch": 0 + }, + null, + { + "owner": "11111111111111111111111111111111", + "lamports": 42, + "data": [bs58::encode(&data).into_string(), "base58"], + "executable": false, + "rentEpoch": 0 + } + ]); + assert_eq!(result.value, expected); - let req = format!( - r#"{{ - "jsonrpc":"2.0","id":1,"method":"getMultipleAccounts","params":[ - ["{}", "{}", "{}"], - {{"encoding":"base64", "dataSlice": {{"length": 2, "offset": 1}}}} - ] - }}"#, - bob_pubkey, non_existent_address, address, + let request = create_test_request( + "getMultipleAccounts", + Some(json!([ + [ + rpc.mint_keypair.pubkey().to_string(), + non_existent_pubkey.to_string(), + address, + ], + {"encoding": "jsonParsed", "dataSlice": {"length": 2, "offset": 1}}, + ])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(result["result"]["value"].as_array().unwrap().len(), 3); - assert_eq!( - result["result"]["value"][2]["data"], - json!([base64::encode(&data[1..3]), "base64"]), + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + ErrorCode::InvalidRequest.code(), + String::from("Sliced account data can only be encoded using binary (base 58) or base64 encoding."), ); - - let req = format!( - r#"{{ - "jsonrpc":"2.0","id":1,"method":"getMultipleAccounts","params":[ - ["{}", "{}", "{}"], - {{"encoding":"binary", "dataSlice": {{"length": 2, "offset": 1}}}} - ] - }}"#, - bob_pubkey, non_existent_address, address, - ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(result["result"]["value"].as_array().unwrap().len(), 3); - assert_eq!( - result["result"]["value"][2]["data"], - bs58::encode(&data[1..3]).into_string(), - ); - - let req = format!( - r#"{{ - "jsonrpc":"2.0","id":1,"method":"getMultipleAccounts","params":[ - ["{}", "{}", "{}"], - {{"encoding":"jsonParsed", "dataSlice": {{"length": 2, "offset": 1}}}} - ] - }}"#, - bob_pubkey, non_existent_address, address, - ); - let res = io.handle_request_sync(&req, meta); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - result["error"].as_object().unwrap(); + assert_eq!(response, expected); } #[test] fn test_rpc_get_program_accounts() { - let bob = Keypair::new(); - let RpcHandler { - io, - meta, - bank, - blockhash, - alice, - .. - } = start_rpc_handler_with_tx(&bob.pubkey()); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); - let new_program_id = solana_sdk::pubkey::new_rand(); - let tx = system_transaction::assign(&bob, blockhash, &new_program_id); - bank.process_transaction(&tx).unwrap(); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getProgramAccounts","params":["{}"]}}"#, - new_program_id + let new_program_id = Pubkey::new_unique(); + let new_program_account_key = Pubkey::new_unique(); + let new_program_account = AccountSharedData::new(42, 0, &new_program_id); + bank.store_account(&new_program_account_key, &new_program_account); + + let request = create_test_request( + "getProgramAccounts", + Some(json!([new_program_id.to_string()])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let expected = format!( - r#"{{ - "jsonrpc":"2.0", - "result":[ - {{ - "pubkey": "{}", - "account": {{ - "owner": "{}", - "lamports": {}, - "data": "", - "executable": false, - "rentEpoch": 0 - }} - }} - ], - "id":1}} - "#, - bob.pubkey(), - new_program_id, - bank.get_minimum_balance_for_rent_exemption(0), - ); - let expected: Response = - serde_json::from_str(&expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + let expected_value = vec![RpcKeyedAccount { + pubkey: new_program_account_key.to_string(), + account: UiAccount::encode( + &new_program_account_key, + &new_program_account, + UiAccountEncoding::Binary, + None, + None, + ), + }]; + assert_eq!(result, expected_value); // Test returns context - let req = format!( - r#"{{ - "jsonrpc":"2.0", - "id":1, - "method":"getProgramAccounts", - "params":["{}",{{ - "withContext": true - }}] - }}"#, - system_program::id(), + let request = create_test_request( + "getProgramAccounts", + Some(json!([ + new_program_id.to_string(), + {"withContext": true}, + ])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")).unwrap(); - let contains_slot = result["result"]["context"] - .as_object() - .expect("must contain context") - .contains_key("slot"); - assert!(contains_slot); + let result: RpcResponse> = + parse_success_result(rpc.handle_request_sync(request)); + let expected = RpcResponse { + context: RpcResponseContext { slot: 0 }, + value: expected_value, + }; + assert_eq!(result, expected); // Set up nonce accounts to test filters - let nonce_keypair0 = Keypair::new(); - let instruction = system_instruction::create_nonce_account( - &alice.pubkey(), - &nonce_keypair0.pubkey(), - &bob.pubkey(), - 100_000, - ); - let message = Message::new(&instruction, Some(&alice.pubkey())); - let tx = Transaction::new(&[&alice, &nonce_keypair0], message, blockhash); - bank.process_transaction(&tx).unwrap(); - - let nonce_keypair1 = Keypair::new(); - let authority = solana_sdk::pubkey::new_rand(); - let instruction = system_instruction::create_nonce_account( - &alice.pubkey(), - &nonce_keypair1.pubkey(), - &authority, - 100_000, - ); - let message = Message::new(&instruction, Some(&alice.pubkey())); - let tx = Transaction::new(&[&alice, &nonce_keypair1], message, blockhash); - bank.process_transaction(&tx).unwrap(); + let nonce_authorities = (0..2) + .map(|_| { + let pubkey = Pubkey::new_unique(); + let authority = Pubkey::new_unique(); + let account = AccountSharedData::new_data( + 42, + &nonce::state::Versions::new_current(nonce::State::new_initialized( + &authority, + &Hash::default(), + 1000, + )), + &system_program::id(), + ) + .unwrap(); + bank.store_account(&pubkey, &account); + authority + }) + .collect::>(); // Test memcmp filter; filter on Initialized state - let req = format!( - r#"{{ - "jsonrpc":"2.0", - "id":1, - "method":"getProgramAccounts", - "params":["{}",{{"filters": [ - {{ - "memcmp": {{"offset": 4,"bytes": "{}"}} - }} - ]}}] - }}"#, - system_program::id(), - bs58::encode(vec![1]).into_string(), + let request = create_test_request( + "getProgramAccounts", + Some(json!([ + system_program::id().to_string(), + {"filters": [{ + "memcmp": { + "offset": 4, + "bytes": bs58::encode(vec![1, 0, 0, 0]).into_string(), + }, + }]}, + ])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let accounts: Vec = serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); - assert_eq!(accounts.len(), 2); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result.len(), 2); - let req = format!( - r#"{{ - "jsonrpc":"2.0", - "id":1, - "method":"getProgramAccounts", - "params":["{}",{{"filters": [ - {{ - "memcmp": {{"offset": 0,"bytes": "{}"}} - }} - ]}}] - }}"#, - system_program::id(), - bs58::encode(vec![1]).into_string(), + let request = create_test_request( + "getProgramAccounts", + Some(json!([ + system_program::id().to_string(), + {"filters": [{ + "memcmp": { + "offset": 0, + "bytes": bs58::encode(vec![1, 0, 0, 0]).into_string(), + }, + }]}, + ])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let accounts: Vec = serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); - assert_eq!(accounts.len(), 0); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result.len(), 0); // Test dataSize filter - let req = format!( - r#"{{ - "jsonrpc":"2.0", - "id":1, - "method":"getProgramAccounts", - "params":["{}",{{"filters": [ - {{ - "dataSize": {} - }} - ]}}] - }}"#, - system_program::id(), - nonce::State::size(), + let request = create_test_request( + "getProgramAccounts", + Some(json!([ + system_program::id().to_string(), + {"filters": [{"dataSize": nonce::State::size()}]}, + ])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let accounts: Vec = serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); - assert_eq!(accounts.len(), 2); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result.len(), 2); - let req = format!( - r#"{{ - "jsonrpc":"2.0", - "id":1, - "method":"getProgramAccounts", - "params":["{}",{{"filters": [ - {{ - "dataSize": 1 - }} - ]}}] - }}"#, - system_program::id(), + let request = create_test_request( + "getProgramAccounts", + Some(json!([ + system_program::id().to_string(), + {"filters": [{"dataSize": 1}]}, + ])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let accounts: Vec = serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); - assert_eq!(accounts.len(), 0); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result.len(), 0); // Test multiple filters - let req = format!( - r#"{{ - "jsonrpc":"2.0", - "id":1, - "method":"getProgramAccounts", - "params":["{}",{{"filters": [ - {{ - "memcmp": {{"offset": 4,"bytes": "{}"}} - }}, - {{ - "memcmp": {{"offset": 8,"bytes": "{}"}} - }} - ]}}] - }}"#, - system_program::id(), - bs58::encode(vec![1]).into_string(), - authority, - ); // Filter on Initialized and Nonce authority - let res = io.handle_request_sync(&req, meta.clone()); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let accounts: Vec = serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); - assert_eq!(accounts.len(), 1); + let request = create_test_request( + "getProgramAccounts", + Some(json!([ + system_program::id().to_string(), + {"filters": [{ + "memcmp": { + "offset": 4, + "bytes": bs58::encode(vec![1, 0, 0, 0]).into_string(), + }, + }, { + "memcmp": { + "offset": 8, + "bytes": nonce_authorities[0].to_string(), + }, + }]}, // Filter on Initialized and Nonce authority + ])), + ); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result.len(), 1); - let req = format!( - r#"{{ - "jsonrpc":"2.0", - "id":1, - "method":"getProgramAccounts", - "params":["{}",{{"filters": [ - {{ - "memcmp": {{"offset": 4,"bytes": "{}"}} - }}, - {{ - "dataSize": 1 - }} - ]}}] - }}"#, - system_program::id(), - bs58::encode(vec![1]).into_string(), - ); // Filter on Initialized and non-matching data size - let res = io.handle_request_sync(&req, meta); - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let accounts: Vec = serde_json::from_value(json["result"].clone()) - .expect("actual response deserialization"); - assert_eq!(accounts.len(), 0); + let request = create_test_request( + "getProgramAccounts", + Some(json!([ + system_program::id().to_string(), + {"filters": [{ + "memcmp": { + "offset": 4, + "bytes": bs58::encode(vec![1, 0, 0, 0]).into_string(), + }, + }, { + "dataSize": 1, + }]}, // Filter on Initialized and non-matching data size + ])), + ); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result.len(), 0); } #[test] fn test_rpc_simulate_transaction() { - let RpcHandler { - io, - meta, - blockhash, - alice, - bank, - .. - } = start_rpc_handler_with_tx(&solana_sdk::pubkey::new_rand()); - + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); let rent_exempt_amount = bank.get_minimum_balance_for_rent_exemption(0); + let recent_blockhash = bank.confirmed_last_blockhash(); + let RpcHandler { + ref meta, ref io, .. + } = rpc; + let bob_pubkey = solana_sdk::pubkey::new_rand(); - let mut tx = - system_transaction::transfer(&alice, &bob_pubkey, rent_exempt_amount, blockhash); + let mut tx = system_transaction::transfer( + &rpc.mint_keypair, + &bob_pubkey, + rent_exempt_amount, + recent_blockhash, + ); let tx_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string(); tx.signatures[0] = Signature::default(); let tx_badsig_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string(); tx.message.recent_blockhash = Hash::default(); let tx_invalid_recent_blockhash = bs58::encode(serialize(&tx).unwrap()).into_string(); - bank.freeze(); // Ensure the root bank is frozen, `start_rpc_handler_with_tx()` doesn't do this + // Simulation bank must be frozen + bank.freeze(); // Good signature with sigVerify=true let req = format!( @@ -5689,7 +5464,7 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); // Too many input accounts... let req = format!( @@ -5726,7 +5501,7 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); // Bad signature with sigVerify=true let req = format!( @@ -5747,7 +5522,7 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); // Bad signature with sigVerify=false let req = format!( @@ -5775,7 +5550,7 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); // Bad signature with default sigVerify setting (false) let req = format!( @@ -5803,7 +5578,7 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); // Enabled both sigVerify=true and replaceRecentBlockhash=true let req = format!( @@ -5827,7 +5602,7 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); // Bad recent blockhash with replaceRecentBlockhash=false let req = format!( @@ -5853,14 +5628,14 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); // Bad recent blockhash with replaceRecentBlockhash=true let req = format!( r#"{{"jsonrpc":"2.0","id":1,"method":"simulateTransaction","params":["{}", {{"replaceRecentBlockhash": true}}]}}"#, tx_invalid_recent_blockhash, ); - let res = io.handle_request_sync(&req, meta); + let res = io.handle_request_sync(&req, meta.clone()); let expected = json!({ "jsonrpc": "2.0", "result": { @@ -5882,23 +5657,24 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] - #[should_panic] + #[should_panic(expected = "simulation bank must be frozen")] fn test_rpc_simulate_transaction_panic_on_unfrozen_bank() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let recent_blockhash = bank.confirmed_last_blockhash(); let RpcHandler { - io, meta, - blockhash, - alice, - bank, + io, + mint_keypair, .. - } = start_rpc_handler_with_tx(&bob_pubkey); + } = rpc; - let tx = system_transaction::transfer(&alice, &bob_pubkey, 1234, blockhash); + let bob_pubkey = Pubkey::new_unique(); + let tx = system_transaction::transfer(&mint_keypair, &bob_pubkey, 1234, recent_blockhash); let tx_serialized_encoded = bs58::encode(serialize(&tx).unwrap()).into_string(); assert!(!bank.is_frozen()); @@ -5914,16 +5690,16 @@ pub mod tests { #[test] fn test_rpc_get_signature_statuses() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let recent_blockhash = bank.confirmed_last_blockhash(); + let confirmed_block_signatures = rpc.create_test_transactions_and_populate_blockstore(); let RpcHandler { - io, mut meta, - blockhash, - alice, - confirmed_block_signatures, - bank, + io, + mint_keypair, .. - } = start_rpc_handler_with_tx(&bob_pubkey); + } = rpc; let req = format!( r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses","params":[["{}"]]}}"#, @@ -5940,11 +5716,12 @@ pub mod tests { assert_eq!(None, result.confirmations); // Test getSignatureStatus request on unprocessed tx + let bob_pubkey = solana_sdk::pubkey::new_rand(); let tx = system_transaction::transfer( - &alice, + &mint_keypair, &bob_pubkey, bank.get_minimum_balance_for_rent_exemption(0) + 10, - blockhash, + recent_blockhash, ); let req = format!( r#"{{"jsonrpc":"2.0","id":1,"method":"getSignatureStatuses","params":[["{}"]]}}"#, @@ -5990,13 +5767,10 @@ pub mod tests { #[test] fn test_rpc_get_recent_blockhash() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, - meta, - blockhash, - .. - } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let recent_blockhash = bank.confirmed_last_blockhash(); + let RpcHandler { meta, io, .. } = rpc; let req = r#"{"jsonrpc":"2.0","id":1,"method":"getRecentBlockhash"}"#; let res = io.handle_request_sync(req, meta); @@ -6005,7 +5779,7 @@ pub mod tests { "result": { "context":{"slot":0}, "value":{ - "blockhash": blockhash.to_string(), + "blockhash": recent_blockhash.to_string(), "feeCalculator": { "lamportsPerSignature": 0, } @@ -6016,48 +5790,47 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] fn test_rpc_get_fees() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, - meta, - blockhash, - .. - } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let recent_blockhash = bank.confirmed_last_blockhash(); + let RpcHandler { meta, io, .. } = rpc; let req = r#"{"jsonrpc":"2.0","id":1,"method":"getFees"}"#; let res = io.handle_request_sync(req, meta); let expected = json!({ "jsonrpc": "2.0", "result": { - "context":{"slot":0}, - "value":{ - "blockhash": blockhash.to_string(), - "feeCalculator": { - "lamportsPerSignature": 0, + "context": {"slot": 0}, + "value": { + "blockhash": recent_blockhash.to_string(), + "feeCalculator": { + "lamportsPerSignature": 0, + }, + "lastValidSlot": MAX_RECENT_BLOCKHASHES, + "lastValidBlockHeight": MAX_RECENT_BLOCKHASHES, }, - "lastValidSlot": MAX_RECENT_BLOCKHASHES, - "lastValidBlockHeight": MAX_RECENT_BLOCKHASHES, - }}, + }, "id": 1 }); let expected: Response = serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] fn test_rpc_get_fee_calculator_for_blockhash() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let recent_blockhash = bank.confirmed_last_blockhash(); + let RpcHandler { meta, io, .. } = rpc; - let blockhash = bank.last_blockhash(); let lamports_per_signature = bank.get_lamports_per_signature(); let fee_calculator = RpcFeeCalculator { fee_calculator: FeeCalculator::new(lamports_per_signature), @@ -6065,7 +5838,7 @@ pub mod tests { let req = format!( r#"{{"jsonrpc":"2.0","id":1,"method":"getFeeCalculatorForBlockhash","params":["{:?}"]}}"#, - blockhash + recent_blockhash ); let res = io.handle_request_sync(&req, meta.clone()); let expected = json!({ @@ -6080,7 +5853,7 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); // Expired (non-existent) blockhash let req = format!( @@ -6100,13 +5873,12 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] fn test_rpc_get_fee_rate_governor() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let RpcHandler { meta, io, .. } = RpcHandler::start(); let req = r#"{"jsonrpc":"2.0","id":1,"method":"getFeeRateGovernor"}"#; let res = io.handle_request_sync(req, meta); @@ -6129,15 +5901,15 @@ pub mod tests { serde_json::from_value(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] fn test_rpc_fail_request_airdrop() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); + let RpcHandler { meta, io, .. } = RpcHandler::start(); // Expect internal error because no faucet is available + let bob_pubkey = solana_sdk::pubkey::new_rand(); let req = format!( r#"{{"jsonrpc":"2.0","id":1,"method":"requestAirdrop","params":["{}", 50]}}"#, bob_pubkey @@ -6149,7 +5921,7 @@ pub mod tests { serde_json::from_str(expected).expect("expected response deserialization"); let result: Response = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] @@ -6388,66 +6160,41 @@ pub mod tests { #[test] fn test_rpc_get_identity() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, meta, alice, .. - } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getIdentity"}"#; - let res = io.handle_request_sync(req, meta); - let expected = json!({ - "jsonrpc": "2.0", - "result": { - "identity": alice.pubkey().to_string() - }, - "id": 1 - }); - let expected: Response = - serde_json::from_value(expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); - } - - fn test_basic_slot(method: &str, expected: Slot) { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = format!("{{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"{}\"}}", method); - let res = io.handle_request_sync(&req, meta); - - let json: Value = serde_json::from_str(&res.unwrap()).unwrap(); - let slot: Slot = serde_json::from_value(json["result"].clone()).unwrap(); - assert_eq!(slot, expected); + let rpc = RpcHandler::start(); + let request = create_test_request("getIdentity", None); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); + let expected: Value = json!({ "identity": rpc.identity.to_string() }); + assert_eq!(result, expected); } #[test] fn test_rpc_get_max_slots() { - test_basic_slot("getMaxRetransmitSlot", 42); - test_basic_slot("getMaxShredInsertSlot", 43); + let rpc = RpcHandler::start(); + rpc.max_slots.retransmit.store(42, Ordering::Relaxed); + rpc.max_slots.shred_insert.store(43, Ordering::Relaxed); + + let request = create_test_request("getMaxRetransmitSlot", None); + let result: Slot = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, 42); + + let request = create_test_request("getMaxShredInsertSlot", None); + let result: Slot = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, 43); } #[test] fn test_rpc_get_version() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { io, meta, .. } = start_rpc_handler_with_tx(&bob_pubkey); - - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getVersion"}"#; - let res = io.handle_request_sync(req, meta); - let version = solana_version::Version::default(); - let expected = json!({ - "jsonrpc": "2.0", - "result": { + let rpc = RpcHandler::start(); + let request = create_test_request("getVersion", None); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); + let expected = { + let version = solana_version::Version::default(); + json!({ "solana-core": version.to_string(), "feature-set": version.feature_set, - }, - "id": 1 - }); - let expected: Response = - serde_json::from_value(expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); + }) + }; + assert_eq!(result, expected); } #[test] @@ -6529,76 +6276,47 @@ pub mod tests { #[test] fn test_rpc_get_block_commitment() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, - meta, - block_commitment_cache, - .. - } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlockCommitment","params":[0]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let RpcBlockCommitment { - commitment, - total_stake, - } = if let Response::Single(res) = result { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success"); - } - } else { - panic!("Expected single response"); - }; - assert_eq!( - commitment, - block_commitment_cache - .read() - .unwrap() - .get_block_commitment(0) - .map(|block_commitment| block_commitment.commitment) + let expected_total_stake = 42; + let mut block_0_commitment = BlockCommitment::default(); + block_0_commitment.increase_confirmation_stake(2, 9); + let _ = std::mem::replace( + &mut *rpc.block_commitment_cache.write().unwrap(), + BlockCommitmentCache::new( + HashMap::from_iter(std::iter::once((0, block_0_commitment.clone()))), + expected_total_stake, + CommitmentSlots::new_from_slot(0), + ), ); - assert_eq!(total_stake, 10); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlockCommitment","params":[2]}"#; - let res = io.handle_request_sync(req, meta); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let commitment_response: RpcBlockCommitment = - if let Response::Single(res) = result { - if let Output::Success(res) = res { - serde_json::from_value(res.result).unwrap() - } else { - panic!("Expected success"); - } - } else { - panic!("Expected single response"); - }; - assert_eq!(commitment_response.commitment, None); - assert_eq!(commitment_response.total_stake, 10); + let request = create_test_request("getBlockCommitment", Some(json!([0u64]))); + let result: RpcBlockCommitment<_> = parse_success_result(rpc.handle_request_sync(request)); + let expected = RpcBlockCommitment { + commitment: Some(block_0_commitment.commitment), + total_stake: expected_total_stake, + }; + assert_eq!(result, expected); + + let request = create_test_request("getBlockCommitment", Some(json!([1u64]))); + let result: Value = parse_success_result(rpc.handle_request_sync(request)); + let expected = json!({ + "commitment": null, + "totalStake": expected_total_stake, + }); + assert_eq!(result, expected); } #[test] fn test_get_block() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, - mut meta, - confirmed_block_signatures, - blockhash, - .. - } = start_rpc_handler_with_tx(&bob_pubkey); + let mut rpc = RpcHandler::start(); + let confirmed_block_signatures = rpc.create_test_transactions_and_populate_blockstore(); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlock","params":[0]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_block: Option = - serde_json::from_value(result["result"].clone()).unwrap(); - let confirmed_block = confirmed_block.unwrap(); + let request = create_test_request("getBlock", Some(json!([0u64]))); + let result: Option = + parse_success_result(rpc.handle_request_sync(request)); + + let confirmed_block = result.unwrap(); assert_eq!(confirmed_block.transactions.len(), 2); assert_eq!(confirmed_block.rewards, vec![]); @@ -6608,11 +6326,6 @@ pub mod tests { if let EncodedTransaction::Json(transaction) = transaction { if transaction.signatures[0] == confirmed_block_signatures[0].to_string() { let meta = meta.unwrap(); - let transaction_recent_blockhash = match transaction.message { - UiMessage::Parsed(message) => message.recent_blockhash, - UiMessage::Raw(message) => message.recent_blockhash, - }; - assert_eq!(transaction_recent_blockhash, blockhash.to_string()); assert_eq!(meta.status, Ok(())); assert_eq!(meta.err, None); } else if transaction.signatures[0] == confirmed_block_signatures[1].to_string() { @@ -6637,13 +6350,10 @@ pub mod tests { } } - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlock","params":[0,"binary"]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_block: Option = - serde_json::from_value(result["result"].clone()).unwrap(); - let confirmed_block = confirmed_block.unwrap(); + let request = create_test_request("getBlock", Some(json!([0u64, "binary"]))); + let result: Option = + parse_success_result(rpc.handle_request_sync(request)); + let confirmed_block = result.unwrap(); assert_eq!(confirmed_block.transactions.len(), 2); assert_eq!(confirmed_block.rewards, vec![]); @@ -6655,7 +6365,6 @@ pub mod tests { deserialize(&bs58::decode(&transaction).into_vec().unwrap()).unwrap(); if decoded_transaction.signatures[0] == confirmed_block_signatures[0] { let meta = meta.unwrap(); - assert_eq!(decoded_transaction.message.recent_blockhash, blockhash); assert_eq!(meta.status, Ok(())); assert_eq!(meta.err, None); } else if decoded_transaction.signatures[0] == confirmed_block_signatures[1] { @@ -6681,63 +6390,58 @@ pub mod tests { } // disable rpc-tx-history - meta.config.enable_rpc_transaction_history = false; - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlock","params":[0]}"#; - let res = io.handle_request_sync(req, meta); - assert_eq!( - res, - Some( - r#"{"jsonrpc":"2.0","error":{"code":-32011,"message":"Transaction history is not available from this node"},"id":1}"#.to_string(), - ) + rpc.meta.config.enable_rpc_transaction_history = false; + let request = create_test_request("getBlock", Some(json!([0u64]))); + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + JSON_RPC_SERVER_ERROR_TRANSACTION_HISTORY_NOT_AVAILABLE, + String::from("Transaction history is not available from this node"), ); + assert_eq!(response, expected); } #[test] fn test_get_block_config() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, - meta, - confirmed_block_signatures, - .. - } = start_rpc_handler_with_tx(&bob_pubkey); + let rpc = RpcHandler::start(); + let confirmed_block_signatures = rpc.create_test_transactions_and_populate_blockstore(); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlock","params":[0,{}]}}"#, - json!(RpcBlockConfig { - encoding: None, - transaction_details: Some(TransactionDetails::Signatures), - rewards: Some(false), - commitment: None, - }) + let request = create_test_request( + "getBlock", + Some(json!([ + 0u64, + RpcBlockConfig { + encoding: None, + transaction_details: Some(TransactionDetails::Signatures), + rewards: Some(false), + commitment: None, + }, + ])), ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_block: Option = - serde_json::from_value(result["result"].clone()).unwrap(); - let confirmed_block = confirmed_block.unwrap(); + let result: Option = + parse_success_result(rpc.handle_request_sync(request)); + + let confirmed_block = result.unwrap(); assert!(confirmed_block.transactions.is_none()); assert!(confirmed_block.rewards.is_none()); for (i, signature) in confirmed_block.signatures.unwrap()[..2].iter().enumerate() { assert_eq!(*signature, confirmed_block_signatures[i].to_string()); } - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlock","params":[0,{}]}}"#, - json!(RpcBlockConfig { - encoding: None, - transaction_details: Some(TransactionDetails::None), - rewards: Some(true), - commitment: None, - }) + let request = create_test_request( + "getBlock", + Some(json!([ + 0u64, + RpcBlockConfig { + encoding: None, + transaction_details: Some(TransactionDetails::None), + rewards: Some(true), + commitment: None, + }, + ])), ); - let res = io.handle_request_sync(&req, meta); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_block: Option = - serde_json::from_value(result["result"].clone()).unwrap(); - let confirmed_block = confirmed_block.unwrap(); + let result: Option = + parse_success_result(rpc.handle_request_sync(request)); + let confirmed_block = result.unwrap(); assert!(confirmed_block.transactions.is_none()); assert!(confirmed_block.signatures.is_none()); assert_eq!(confirmed_block.rewards.unwrap(), vec![]); @@ -6745,335 +6449,215 @@ pub mod tests { #[test] fn test_get_block_production() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let roots = vec![0, 1, 3, 4, 8]; - let RpcHandler { - io, - meta, - block_commitment_cache, - leader_pubkey, - .. - } = start_rpc_handler_with_tx_and_blockstore(&bob_pubkey, roots); - block_commitment_cache + let rpc = RpcHandler::start(); + rpc.add_roots_to_blockstore(vec![0, 1, 3, 4, 8]); + rpc.block_commitment_cache .write() .unwrap() .set_highest_confirmed_root(8); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlockProduction","params":[]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let block_production: RpcBlockProduction = - serde_json::from_value(result["result"]["value"].clone()).unwrap(); - assert_eq!( - block_production.by_identity.get(&leader_pubkey.to_string()), - Some(&(9, 5)) - ); - assert_eq!( - block_production.range, - RpcBlockProductionRange { + let request = create_test_request("getBlockProduction", Some(json!([]))); + let result: RpcResponse = + parse_success_result(rpc.handle_request_sync(request)); + let expected = RpcBlockProduction { + by_identity: HashMap::from_iter(std::iter::once(( + rpc.leader_pubkey().to_string(), + (9, 5), + ))), + range: RpcBlockProductionRange { first_slot: 0, - last_slot: 8 - } - ); + last_slot: 8, + }, + }; + assert_eq!(result.value, expected); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlockProduction","params":[{{"identity": "{}"}}]}}"#, - leader_pubkey - ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let block_production: RpcBlockProduction = - serde_json::from_value(result["result"]["value"].clone()).unwrap(); - assert_eq!( - block_production.by_identity.get(&leader_pubkey.to_string()), - Some(&(9, 5)) - ); - assert_eq!( - block_production.range, - RpcBlockProductionRange { - first_slot: 0, - last_slot: 8 - } + let request = create_test_request( + "getBlockProduction", + Some(json!([{ + "identity": rpc.leader_pubkey().to_string() + }])), ); + let result: RpcResponse = + parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result.value, expected); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlockProduction","params":[{{"range": {{"firstSlot": 0, "lastSlot": 4}}, "identity": "{}"}}]}}"#, - bob_pubkey + let request = create_test_request( + "getBlockProduction", + Some(json!([{ + "identity": Pubkey::new_unique().to_string(), + "range": { + "firstSlot": 0u64, + "lastSlot": 4u64, + }, + }])), ); - let res = io.handle_request_sync(&req, meta); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let block_production: RpcBlockProduction = - serde_json::from_value(result["result"]["value"].clone()).unwrap(); - assert_eq!( - block_production.by_identity.get(&leader_pubkey.to_string()), - None - ); - assert_eq!( - block_production.range, - RpcBlockProductionRange { + let result: RpcResponse = + parse_success_result(rpc.handle_request_sync(request)); + let expected = RpcBlockProduction { + by_identity: HashMap::new(), + range: RpcBlockProductionRange { first_slot: 0, - last_slot: 4 - } - ); + last_slot: 4, + }, + }; + assert_eq!(result.value, expected); } #[test] fn test_get_blocks() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let roots = vec![0, 1, 3, 4, 8]; - let RpcHandler { - io, - meta, - block_commitment_cache, - .. - } = start_rpc_handler_with_tx_and_blockstore(&bob_pubkey, roots.clone()); - block_commitment_cache + let rpc = RpcHandler::start(); + rpc.add_roots_to_blockstore(vec![0, 1, 3, 4, 8]); + rpc.block_commitment_cache .write() .unwrap() .set_highest_confirmed_root(8); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocks","params":[0]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, roots[1..].to_vec()); + let request = create_test_request("getBlocks", Some(json!([0u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, vec![1, 3, 4, 8]); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocks","params":[2]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, vec![3, 4, 8]); + let request = create_test_request("getBlocks", Some(json!([2u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, vec![3, 4, 8]); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocks","params":[0,4]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, vec![1, 3, 4]); + let request = create_test_request("getBlocks", Some(json!([0u64, 4u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, vec![1, 3, 4]); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocks","params":[0,7]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, vec![1, 3, 4]); + let request = create_test_request("getBlocks", Some(json!([0u64, 7u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, vec![1, 3, 4]); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocks","params":[9,11]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, Vec::::new()); + let request = create_test_request("getBlocks", Some(json!([9u64, 11u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, Vec::::new()); - block_commitment_cache + rpc.block_commitment_cache .write() .unwrap() .set_highest_confirmed_root(std::u64::MAX); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlocks","params":[0,{}]}}"#, - MAX_GET_CONFIRMED_BLOCKS_RANGE - ); - let res = io.handle_request_sync(&req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, vec![1, 3, 4, 8]); - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlocks","params":[0,{}]}}"#, - MAX_GET_CONFIRMED_BLOCKS_RANGE + 1 + let request = create_test_request( + "getBlocks", + Some(json!([0u64, MAX_GET_CONFIRMED_BLOCKS_RANGE])), ); - let res = io.handle_request_sync(&req, meta); - assert_eq!( - res, - Some( - r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Slot range too large; max 500000"},"id":1}"#.to_string(), - ) + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, vec![1, 3, 4, 8]); + + let request = create_test_request( + "getBlocks", + Some(json!([0u64, MAX_GET_CONFIRMED_BLOCKS_RANGE + 1])), ); + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + ErrorCode::InvalidParams.code(), + String::from("Slot range too large; max 500000"), + ); + assert_eq!(response, expected); } #[test] fn test_get_blocks_with_limit() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let roots = vec![0, 1, 3, 4, 8]; - let RpcHandler { - io, - meta, - block_commitment_cache, - .. - } = start_rpc_handler_with_tx_and_blockstore(&bob_pubkey, roots); - block_commitment_cache + let rpc = RpcHandler::start(); + rpc.add_roots_to_blockstore(vec![0, 1, 3, 4, 8]); + rpc.block_commitment_cache .write() .unwrap() .set_highest_confirmed_root(8); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocksWithLimit","params":[0,500001]}"#; - let res = io.handle_request_sync(req, meta.clone()); - assert_eq!( - res, - Some( - r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Limit too large; max 500000"},"id":1}"#.to_string(), - ) + let request = create_test_request("getBlocksWithLimit", Some(json!([0u64, 500_001u64]))); + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + ErrorCode::InvalidParams.code(), + String::from("Limit too large; max 500000"), ); + assert_eq!(response, expected); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocksWithLimit","params":[0,0]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert!(confirmed_blocks.is_empty()); + let request = create_test_request("getBlocksWithLimit", Some(json!([0u64, 0u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, Vec::::new()); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocksWithLimit","params":[2,2]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, vec![3, 4]); + let request = create_test_request("getBlocksWithLimit", Some(json!([2u64, 2u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, vec![3, 4]); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocksWithLimit","params":[2,3]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, vec![3, 4, 8]); + let request = create_test_request("getBlocksWithLimit", Some(json!([2u64, 3u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, vec![3, 4, 8]); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocksWithLimit","params":[2,500000]}"#; - let res = io.handle_request_sync(req, meta.clone()); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, vec![3, 4, 8]); + let request = create_test_request("getBlocksWithLimit", Some(json!([2u64, 500_000u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, vec![3, 4, 8]); - let req = r#"{"jsonrpc":"2.0","id":1,"method":"getBlocksWithLimit","params":[9,500000]}"#; - let res = io.handle_request_sync(req, meta); - let result: Value = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - let confirmed_blocks: Vec = serde_json::from_value(result["result"].clone()).unwrap(); - assert_eq!(confirmed_blocks, Vec::::new()); + let request = create_test_request("getBlocksWithLimit", Some(json!([9u64, 500_000u64]))); + let result: Vec = parse_success_result(rpc.handle_request_sync(request)); + assert_eq!(result, Vec::::new()); } #[test] fn test_get_block_time() { - let bob_pubkey = solana_sdk::pubkey::new_rand(); - let RpcHandler { - io, - meta, - bank, - block_commitment_cache, - bank_forks, - .. - } = start_rpc_handler_with_tx_and_blockstore(&bob_pubkey, vec![1, 2, 3, 4, 5, 6, 7]); - let base_timestamp = bank_forks + let rpc = RpcHandler::start(); + rpc.add_roots_to_blockstore(vec![1, 2, 3, 4, 5, 6, 7]); + + let base_timestamp = rpc + .bank_forks .read() .unwrap() .get(0) .unwrap() .unix_timestamp_from_genesis(); - block_commitment_cache + rpc.block_commitment_cache .write() .unwrap() .set_highest_confirmed_root(7); - let slot_duration = slot_duration_from_slots_per_year(bank.slots_per_year()); + let slot_duration = slot_duration_from_slots_per_year(rpc.working_bank().slots_per_year()); - let slot = 2; - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlockTime","params":[{}]}}"#, - slot - ); - let res = io.handle_request_sync(&req, meta.clone()); - let expected = format!(r#"{{"jsonrpc":"2.0","result":{},"id":1}}"#, base_timestamp); - let expected: Response = - serde_json::from_str(&expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); + let request = create_test_request("getBlockTime", Some(json!([2u64]))); + let result: Option = parse_success_result(rpc.handle_request_sync(request)); + let expected = Some(base_timestamp); + assert_eq!(result, expected); - let slot = 7; - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlockTime","params":[{}]}}"#, - slot - ); - let res = io.handle_request_sync(&req, meta.clone()); - let expected = format!( - r#"{{"jsonrpc":"2.0","result":{},"id":1}}"#, - base_timestamp + (7 * slot_duration).as_secs() as i64 - ); - let expected: Response = - serde_json::from_str(&expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); + let request = create_test_request("getBlockTime", Some(json!([7u64]))); + let result: Option = parse_success_result(rpc.handle_request_sync(request)); + let expected = Some(base_timestamp + (7 * slot_duration).as_secs() as i64); + assert_eq!(result, expected); - let slot = 12345; - let req = format!( - r#"{{"jsonrpc":"2.0","id":1,"method":"getBlockTime","params":[{}]}}"#, - slot + let request = create_test_request("getBlockTime", Some(json!([12345u64]))); + let response = parse_failure_response(rpc.handle_request_sync(request)); + let expected = ( + JSON_RPC_SERVER_ERROR_BLOCK_NOT_AVAILABLE, + String::from("Block not available for slot 12345"), ); - let res = io.handle_request_sync(&req, meta); - let expected = r#"{"jsonrpc":"2.0","error":{"code":-32004,"message":"Block not available for slot 12345"},"id":1}"#; - let expected: Response = - serde_json::from_str(expected).expect("expected response deserialization"); - let result: Response = serde_json::from_str(&res.expect("actual response")) - .expect("actual response deserialization"); - assert_eq!(expected, result); - } - - fn advance_block_commitment_cache( - block_commitment_cache: &Arc>, - bank_forks: &Arc>, - ) { - let mut new_block_commitment = BlockCommitmentCache::new( - HashMap::new(), - 0, - CommitmentSlots::new_from_slot(bank_forks.read().unwrap().highest_slot()), - ); - let mut w_block_commitment_cache = block_commitment_cache.write().unwrap(); - std::mem::swap(&mut *w_block_commitment_cache, &mut new_block_commitment); + assert_eq!(response, expected); } #[test] fn test_get_vote_accounts() { + let rpc = RpcHandler::start(); + let mut bank = rpc.working_bank(); let RpcHandler { - io, - meta, - mut bank, - bank_forks, - alice, - leader_vote_keypair, - block_commitment_cache, + ref io, + ref meta, + ref mint_keypair, + ref leader_vote_keypair, .. - } = start_rpc_handler_with_tx(&solana_sdk::pubkey::new_rand()); + } = rpc; assert_eq!(bank.vote_accounts().len(), 1); // Create a vote account with no stake. let alice_vote_keypair = Keypair::new(); - let instructions = vote_instruction::create_account( - &alice.pubkey(), - &alice_vote_keypair.pubkey(), + let alice_vote_state = VoteState::new( &VoteInit { - node_pubkey: alice.pubkey(), + node_pubkey: mint_keypair.pubkey(), authorized_voter: alice_vote_keypair.pubkey(), authorized_withdrawer: alice_vote_keypair.pubkey(), commission: 0, }, - bank.get_minimum_balance_for_rent_exemption(VoteState::size_of()), + &bank.get_sysvar_cache_for_tests().get_clock().unwrap(), ); - - let message = Message::new(&instructions, Some(&alice.pubkey())); - let transaction = Transaction::new( - &[&alice, &alice_vote_keypair], - message, - bank.last_blockhash(), - ); - bank.process_transaction(&transaction) - .expect("process transaction"); + rpc.store_vote_account(&alice_vote_keypair.pubkey(), alice_vote_state); assert_eq!(bank.vote_accounts().len(), 2); // Check getVoteAccounts: the bootstrap validator vote account will be delinquent as it has @@ -7119,17 +6703,12 @@ pub mod tests { ), ]; - bank = bank_forks.write().unwrap().insert(Bank::new_from_parent( - &bank, - &Pubkey::default(), - bank.slot() + 1, - )); - advance_block_commitment_cache(&block_commitment_cache, &bank_forks); + bank = rpc.advance_bank_to_confirmed_slot(bank.slot() + 1); let transaction = Transaction::new_signed_with_payer( &instructions, - Some(&alice.pubkey()), - &[&alice, &leader_vote_keypair, &alice_vote_keypair], + Some(&rpc.mint_keypair.pubkey()), + &[&rpc.mint_keypair, leader_vote_keypair, &alice_vote_keypair], bank.last_blockhash(), ); @@ -7228,13 +6807,7 @@ pub mod tests { .any(|x| x.epoch_credits.len() != MAX_RPC_EPOCH_CREDITS_HISTORY)); // Advance bank with no voting - bank.freeze(); - bank_forks.write().unwrap().insert(Bank::new_from_parent( - &bank, - &Pubkey::default(), - bank.slot() + TEST_SLOTS_PER_EPOCH, - )); - advance_block_commitment_cache(&block_commitment_cache, &bank_forks); + rpc.advance_bank_to_confirmed_slot(bank.slot() + TEST_SLOTS_PER_EPOCH); // The leader vote account should now be delinquent, and the other vote account disappears // because it's inactive with no stake @@ -7244,7 +6817,7 @@ pub mod tests { json!([CommitmentConfig::processed()]) ); - let res = io.handle_request_sync(&req, meta); + let res = io.handle_request_sync(&req, meta.clone()); let result: Value = serde_json::from_str(&res.expect("actual response")) .expect("actual response deserialization"); @@ -7256,7 +6829,7 @@ pub mod tests { for vote_account_info in vote_account_status.delinquent { assert_eq!( vote_account_info.vote_pubkey, - leader_vote_keypair.pubkey().to_string() + rpc.leader_vote_keypair.pubkey().to_string() ); } } @@ -7309,8 +6882,9 @@ pub mod tests { #[test] fn test_token_rpcs() { - let RpcHandler { io, meta, bank, .. } = - start_rpc_handler_with_tx(&solana_sdk::pubkey::new_rand()); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let RpcHandler { io, meta, .. } = rpc; let mut account_data = vec![0; TokenAccount::get_packed_len()]; let mint = SplTokenPubkey::new(&[2; 32]); @@ -7710,8 +7284,9 @@ pub mod tests { #[test] fn test_token_parsing() { - let RpcHandler { io, meta, bank, .. } = - start_rpc_handler_with_tx(&solana_sdk::pubkey::new_rand()); + let rpc = RpcHandler::start(); + let bank = rpc.working_bank(); + let RpcHandler { io, meta, .. } = rpc; let mut account_data = vec![0; TokenAccount::get_packed_len()]; let mint = SplTokenPubkey::new(&[2; 32]); diff --git a/rpc/src/rpc_pubsub.rs b/rpc/src/rpc_pubsub.rs index e0144017ac..550eca4ea4 100644 --- a/rpc/src/rpc_pubsub.rs +++ b/rpc/src/rpc_pubsub.rs @@ -808,7 +808,7 @@ mod tests { let expected: Response = serde_json::from_str(expected).unwrap(); let result: Response = serde_json::from_str(&res.unwrap()).unwrap(); - assert_eq!(expected, result); + assert_eq!(result, expected); // Test bad parameter let req = r#"{"jsonrpc":"2.0","id":1,"method":"signatureUnsubscribe","params":[1]}"#; @@ -817,7 +817,7 @@ mod tests { let expected: Response = serde_json::from_str(expected).unwrap(); let result: Response = serde_json::from_str(&res.unwrap()).unwrap(); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] @@ -1071,7 +1071,7 @@ mod tests { let expected: Response = serde_json::from_str(expected).unwrap(); let result: Response = serde_json::from_str(&res.unwrap()).unwrap(); - assert_eq!(expected, result); + assert_eq!(result, expected); // Test bad parameter let req = r#"{"jsonrpc":"2.0","id":1,"method":"accountUnsubscribe","params":[1]}"#; @@ -1080,7 +1080,7 @@ mod tests { let expected: Response = serde_json::from_str(expected).unwrap(); let result: Response = serde_json::from_str(&res.unwrap()).unwrap(); - assert_eq!(expected, result); + assert_eq!(result, expected); } #[test] diff --git a/runtime/src/bank/sysvar_cache.rs b/runtime/src/bank/sysvar_cache.rs index 66d28abebd..8a77e4280a 100644 --- a/runtime/src/bank/sysvar_cache.rs +++ b/runtime/src/bank/sysvar_cache.rs @@ -1,4 +1,4 @@ -use super::Bank; +use {super::Bank, solana_program_runtime::sysvar_cache::SysvarCache}; impl Bank { pub(crate) fn fill_missing_sysvar_cache_entries(&self) { @@ -10,6 +10,10 @@ impl Bank { let mut sysvar_cache = self.sysvar_cache.write().unwrap(); sysvar_cache.reset(); } + + pub fn get_sysvar_cache_for_tests(&self) -> SysvarCache { + self.sysvar_cache.read().unwrap().clone() + } } #[cfg(test)]