diff --git a/book/src/api-reference/jsonrpc-api.md b/book/src/api-reference/jsonrpc-api.md index 9861fdd91..6de00c200 100644 --- a/book/src/api-reference/jsonrpc-api.md +++ b/book/src/api-reference/jsonrpc-api.md @@ -384,7 +384,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: @@ -393,7 +395,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 cad3b0149..99ca70eaa 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 02c8e60ed..02feca999 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>; @@ -52,6 +52,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 cfc0f3c3a..5d2ab7765 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_faucet::faucet::request_airdrop_transaction; use solana_ledger::{bank_forks::BankForks, blocktree::Blocktree}; @@ -431,7 +431,7 @@ pub trait RpcSol { meta: Self::Metadata, slot: Option, commitment: Option, - ) -> Result>>; + ) -> Result>; #[rpc(meta, name = "getRecentBlockhash")] fn get_recent_blockhash( @@ -713,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 }, ), ) @@ -1394,7 +1399,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 { @@ -1403,9 +1408,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() @@ -1418,7 +1428,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 {