From 5fbe5aa22d18c9d037c596ae9dc224297aa007cf Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 18 Dec 2019 15:45:20 -0800 Subject: [PATCH] Speed up getLeaderSchedule RPC call by reducing pubkey duplication (#7556) automerge --- book/src/api-reference/jsonrpc-api.md | 6 +++-- client/src/rpc_client.rs | 9 ++++--- client/src/rpc_request.rs | 5 +++- core/src/rpc.rs | 35 ++++++++++++++++++--------- 4 files changed, 37 insertions(+), 18 deletions(-) diff --git a/book/src/api-reference/jsonrpc-api.md b/book/src/api-reference/jsonrpc-api.md index 9b7b0b44ed..cd5f9907ee 100644 --- a/book/src/api-reference/jsonrpc-api.md +++ b/book/src/api-reference/jsonrpc-api.md @@ -382,7 +382,9 @@ Returns the leader schedule for an epoch #### Results: -The result field will be an array of leader public keys \(as base-58 encoded strings\) for each slot in the epoch +The result field will be a dictionary of leader public keys \(as base-58 encoded +strings\) and their corresponding leader slot indices as values (indices are to +the first slot in the requested epoch) #### Example: @@ -391,7 +393,7 @@ The result field will be an array of leader public keys \(as base-58 encoded str curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getLeaderSchedule"}' http://localhost:8899 // Result -{"jsonrpc":"2.0","result":[...],"id":1} +{"jsonrpc":"2.0","result":{"4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63]},"id":1} ``` ### getMinimumBalanceForRentExemption diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 77e5ed1017..8aedca350f 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -4,7 +4,10 @@ use crate::{ generic_rpc_client_request::GenericRpcClientRequest, mock_rpc_client_request::MockRpcClientRequest, rpc_client_request::RpcClientRequest, - rpc_request::{RpcContactInfo, RpcEpochInfo, RpcRequest, RpcVersionInfo, RpcVoteAccountStatus}, + rpc_request::{ + RpcContactInfo, RpcEpochInfo, RpcLeaderSchedule, RpcRequest, RpcVersionInfo, + RpcVoteAccountStatus, + }, }; use bincode::serialize; use log::*; @@ -248,7 +251,7 @@ impl RpcClient { }) } - pub fn get_leader_schedule(&self, slot: Option) -> io::Result>> { + pub fn get_leader_schedule(&self, slot: Option) -> io::Result> { self.get_leader_schedule_with_commitment(slot, CommitmentConfig::default()) } @@ -256,7 +259,7 @@ impl RpcClient { &self, slot: Option, commitment_config: CommitmentConfig, - ) -> io::Result>> { + ) -> io::Result> { let params = slot.map(|slot| json!(slot)); let response = self .client diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 37ce08dd7d..4e4f2ca972 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -6,7 +6,7 @@ use solana_sdk::{ hash::Hash, transaction::{Result, Transaction}, }; -use std::{error, fmt, io, net::SocketAddr}; +use std::{collections::HashMap, error, fmt, io, net::SocketAddr}; pub type RpcResponseIn = JsonResult>; pub type RpcResponse = io::Result>; @@ -49,6 +49,9 @@ pub struct RpcContactInfo { pub rpc: Option, } +/// Map of leader base58 identity pubkeys to the slot indices relative to the first epoch slot +pub type RpcLeaderSchedule = HashMap>; + #[derive(Serialize, Deserialize, Clone, Debug)] #[serde(rename_all = "camelCase")] pub struct RpcEpochInfo { diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 618426c35d..ef4f7b8750 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -12,8 +12,8 @@ use bincode::serialize; use jsonrpc_core::{Error, Metadata, Result}; use jsonrpc_derive::rpc; use solana_client::rpc_request::{ - Response, RpcConfirmedBlock, RpcContactInfo, RpcEpochInfo, RpcResponseContext, RpcVersionInfo, - RpcVoteAccountInfo, RpcVoteAccountStatus, + Response, RpcConfirmedBlock, RpcContactInfo, RpcEpochInfo, RpcLeaderSchedule, + RpcResponseContext, RpcVersionInfo, RpcVoteAccountInfo, RpcVoteAccountStatus, }; use solana_drone::drone::request_airdrop_transaction; use solana_ledger::{bank_forks::BankForks, blocktree::Blocktree}; @@ -33,6 +33,7 @@ use solana_sdk::{ }; use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY}; use std::{ + collections::HashMap, net::{SocketAddr, UdpSocket}, sync::{Arc, RwLock}, thread::sleep, @@ -430,7 +431,7 @@ pub trait RpcSol { meta: Self::Metadata, slot: Option, commitment: Option, - ) -> Result>>; + ) -> Result>; #[rpc(meta, name = "getRecentBlockhash")] fn get_recent_blockhash( @@ -712,18 +713,23 @@ impl RpcSol for RpcSolImpl { meta: Self::Metadata, slot: Option, commitment: Option, - ) -> Result>> { + ) -> Result> { let bank = meta.request_processor.read().unwrap().bank(commitment); let slot = slot.unwrap_or_else(|| bank.slot()); let epoch = bank.epoch_schedule().get_epoch(slot); + Ok( solana_ledger::leader_schedule_utils::leader_schedule(epoch, &bank).map( |leader_schedule| { - leader_schedule - .get_slot_leaders() - .iter() - .map(|pubkey| pubkey.to_string()) - .collect() + let mut map = HashMap::new(); + + for (slot_index, pubkey) in + leader_schedule.get_slot_leaders().iter().enumerate() + { + let pubkey = pubkey.to_string(); + map.entry(pubkey).or_insert_with(|| vec![]).push(slot_index); + } + map }, ), ) @@ -1357,7 +1363,7 @@ pub mod tests { let res: Response = serde_json::from_str(&rep.expect("actual response")) .expect("actual response deserialization"); - let schedule: Option> = if let Response::Single(res) = res { + let schedule: Option = if let Response::Single(res) = res { if let Output::Success(res) = res { serde_json::from_value(res.result).unwrap() } else { @@ -1366,9 +1372,14 @@ pub mod tests { } 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!( - schedule.unwrap().len(), + bob_schedule.len(), solana_ledger::leader_schedule_utils::leader_schedule(bank.epoch(), &bank) .unwrap() .get_slot_leaders() @@ -1381,7 +1392,7 @@ pub mod tests { let res: Response = serde_json::from_str(&rep.expect("actual response")) .expect("actual response deserialization"); - let schedule: Option> = if let Response::Single(res) = res { + let schedule: Option = if let Response::Single(res) = res { if let Output::Success(res) = res { serde_json::from_value(res.result).unwrap() } else {