diff --git a/book/src/api-reference/jsonrpc-api.md b/book/src/api-reference/jsonrpc-api.md index 39678c2e84..9b7b0b44ed 100644 --- a/book/src/api-reference/jsonrpc-api.md +++ b/book/src/api-reference/jsonrpc-api.md @@ -373,15 +373,16 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m ### getLeaderSchedule -Returns the leader schedule for the current epoch +Returns the leader schedule for an epoch #### Parameters: +* `slot` - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot. If unspecified, the leader schedule for the current epoch is fetch * `object` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) #### Results: -The result field will be an array of leader public keys \(as base-58 encoded strings\) for each slot in the current epoch +The result field will be an array of leader public keys \(as base-58 encoded strings\) for each slot in the epoch #### Example: diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 4154c4e6ba..77e5ed1017 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -248,6 +248,39 @@ impl RpcClient { }) } + pub fn get_leader_schedule(&self, slot: Option) -> io::Result>> { + self.get_leader_schedule_with_commitment(slot, CommitmentConfig::default()) + } + + pub fn get_leader_schedule_with_commitment( + &self, + slot: Option, + commitment_config: CommitmentConfig, + ) -> io::Result>> { + let params = slot.map(|slot| json!(slot)); + let response = self + .client + .send( + &RpcRequest::GetLeaderSchedule, + params, + 0, + commitment_config.ok(), + ) + .map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("GetLeaderSchedule request failure: {:?}", err), + ) + })?; + + serde_json::from_value(response).map_err(|err| { + io::Error::new( + io::ErrorKind::Other, + format!("GetLeaderSchedule failure: {}", err), + ) + }) + } + pub fn get_epoch_schedule(&self) -> io::Result { let response = self .client diff --git a/client/src/rpc_request.rs b/client/src/rpc_request.rs index 105fa61b52..37ce08dd7d 100644 --- a/client/src/rpc_request.rs +++ b/client/src/rpc_request.rs @@ -121,6 +121,7 @@ pub enum RpcRequest { GetEpochSchedule, GetGenesisHash, GetInflation, + GetLeaderSchedule, GetNumBlocksSinceSignatureConfirmation, GetProgramAccounts, GetRecentBlockhash, @@ -161,6 +162,7 @@ impl RpcRequest { RpcRequest::GetEpochSchedule => "getEpochSchedule", RpcRequest::GetGenesisHash => "getGenesisHash", RpcRequest::GetInflation => "getInflation", + RpcRequest::GetLeaderSchedule => "getLeaderSchedule", RpcRequest::GetNumBlocksSinceSignatureConfirmation => { "getNumBlocksSinceSignatureConfirmation" } diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 19650337b6..618426c35d 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -418,7 +418,7 @@ pub trait RpcSol { fn get_block_commitment( &self, meta: Self::Metadata, - block: u64, + block: Slot, ) -> Result<(Option, u64)>; #[rpc(meta, name = "getGenesisHash")] @@ -428,6 +428,7 @@ pub trait RpcSol { fn get_leader_schedule( &self, meta: Self::Metadata, + slot: Option, commitment: Option, ) -> Result>>; @@ -678,8 +679,9 @@ impl RpcSol for RpcSolImpl { ) -> Result { let bank = meta.request_processor.read().unwrap().bank(commitment); let epoch_schedule = bank.epoch_schedule(); - let (epoch, slot_index) = epoch_schedule.get_epoch_and_slot_index(bank.slot()); + let slot = bank.slot(); + let (epoch, slot_index) = epoch_schedule.get_epoch_and_slot_index(slot); Ok(RpcEpochInfo { epoch, slot_index, @@ -708,11 +710,14 @@ impl RpcSol for RpcSolImpl { fn get_leader_schedule( &self, meta: Self::Metadata, + slot: Option, commitment: Option, ) -> 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(bank.epoch(), &bank).map( + solana_ledger::leader_schedule_utils::leader_schedule(epoch, &bank).map( |leader_schedule| { leader_schedule .get_slot_leaders() @@ -1337,6 +1342,57 @@ pub mod tests { assert_eq!(epoch_schedule, *bank.epoch_schedule()); } + #[test] + fn test_rpc_get_leader_schedule() { + let bob_pubkey = Pubkey::new_rand(); + let RpcHandler { io, meta, bank, .. } = start_rpc_handler_with_tx(&bob_pubkey); + + for req in [ + r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [0]}"#, + r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule"}"#, + ] + .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"); + }; + + assert_eq!( + schedule.unwrap().len(), + solana_ledger::leader_schedule_utils::leader_schedule(bank.epoch(), &bank) + .unwrap() + .get_slot_leaders() + .len() + ); + } + + let req = r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [42424242]}"#; + 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, None); + } + #[test] fn test_rpc_get_account_info() { let bob_pubkey = Pubkey::new_rand();