From adfb8ff2a1da65746a71d594377f75c0c9b9c72a Mon Sep 17 00:00:00 2001 From: Michael Vines Date: Fri, 19 Jul 2019 07:31:18 -0700 Subject: [PATCH] Add getEpochInfo() and getLeaderSchedule() RPC methods (#5189) * Add getLeaderSchedule() RPC method * Add getEpochInfo() RPC method * Add JSON RPC docs --- book/src/jsonrpc-api.md | 46 +++++++++++++++++++++++++++++++ core/src/leader_schedule.rs | 4 +++ core/src/leader_schedule_utils.rs | 8 +++--- core/src/rpc.rs | 45 ++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 4 deletions(-) diff --git a/book/src/jsonrpc-api.md b/book/src/jsonrpc-api.md index be72fc9389..2bd681a486 100644 --- a/book/src/jsonrpc-api.md +++ b/book/src/jsonrpc-api.md @@ -25,6 +25,8 @@ Methods * [getAccountInfo](#getaccountinfo) * [getBalance](#getbalance) * [getClusterNodes](#getclusternodes) +* [getEpochInfo](#getepochinfo) +* [getLeaderSchedule](#getleaderschedule) * [getProgramAccounts](#getprogramaccounts) * [getRecentBlockhash](#getrecentblockhash) * [getSignatureStatus](#getsignaturestatus) @@ -172,6 +174,50 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, " --- +### getEpochInfo +Returns information about the current epoch + +##### Parameters: +None + +##### Results: +The result field will be an object with the following fields: +* `epoch`, the current epoch +* `slotIndex`, the current slot relative to the start of the current epoch +* `slotsInEpoch`, the number of slots in this epoch + +##### Example: +```bash +// Request +curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochInfo"}' http://localhost:8899 + +// Result +{"jsonrpc":"2.0","result":{"epoch":3,"slotIndex":126,"slotsInEpoch":256},"id":1} +``` + +--- + +### getLeaderSchedule +Returns the leader schedule for the current epoch + +##### Parameters: +None + +##### Results: +The result field will be an array of leader public keys (as base-58 encoded +strings) for each slot in the current epoch + +##### Example: +```bash +// Request +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} +``` + +--- + ### getProgramAccounts Returns all accounts owned by the provided program Pubkey diff --git a/core/src/leader_schedule.rs b/core/src/leader_schedule.rs index c637f845d8..855df2d20a 100644 --- a/core/src/leader_schedule.rs +++ b/core/src/leader_schedule.rs @@ -29,6 +29,10 @@ impl LeaderSchedule { .collect(); Self { slot_leaders } } + + pub(crate) fn get_slot_leaders(&self) -> &[Pubkey] { + &self.slot_leaders + } } impl Index for LeaderSchedule { diff --git a/core/src/leader_schedule_utils.rs b/core/src/leader_schedule_utils.rs index 449e58abde..4954e9de66 100644 --- a/core/src/leader_schedule_utils.rs +++ b/core/src/leader_schedule_utils.rs @@ -5,16 +5,16 @@ use solana_sdk::pubkey::Pubkey; use solana_sdk::timing::NUM_CONSECUTIVE_LEADER_SLOTS; /// Return the leader schedule for the given epoch. -pub fn leader_schedule(epoch_height: u64, bank: &Bank) -> Option { - staking_utils::staked_nodes_at_epoch(bank, epoch_height).map(|stakes| { +pub fn leader_schedule(epoch: u64, bank: &Bank) -> Option { + staking_utils::staked_nodes_at_epoch(bank, epoch).map(|stakes| { let mut seed = [0u8; 32]; - seed[0..8].copy_from_slice(&epoch_height.to_le_bytes()); + seed[0..8].copy_from_slice(&epoch.to_le_bytes()); let mut stakes: Vec<_> = stakes.into_iter().collect(); sort_stakes(&mut stakes); LeaderSchedule::new( &stakes, seed, - bank.get_slots_in_epoch(epoch_height), + bank.get_slots_in_epoch(epoch), NUM_CONSECUTIVE_LEADER_SLOTS, ) }) diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 60792c0470..643d8e5641 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -220,6 +220,19 @@ pub struct RpcVoteAccountInfo { pub commission: u8, } +#[derive(Serialize, Deserialize, Clone, Debug)] +#[serde(rename_all = "camelCase")] +pub struct RpcEpochInfo { + /// The current epoch + pub epoch: u64, + + /// The number of slots in this epoch + pub slots_in_epoch: u64, + + /// The current slot, relative to the start of the current epoch + pub slot_index: u64, +} + #[rpc(server)] pub trait RpcSol { type Metadata; @@ -239,6 +252,12 @@ pub trait RpcSol { #[rpc(meta, name = "getClusterNodes")] fn get_cluster_nodes(&self, _: Self::Metadata) -> Result>; + #[rpc(meta, name = "getEpochInfo")] + fn get_epoch_info(&self, _: Self::Metadata) -> Result; + + #[rpc(meta, name = "getLeaderSchedule")] + fn get_leader_schedule(&self, _: Self::Metadata) -> Result>>; + #[rpc(meta, name = "getRecentBlockhash")] fn get_recent_blockhash(&self, _: Self::Metadata) -> Result<(String, FeeCalculator)>; @@ -369,6 +388,32 @@ impl RpcSol for RpcSolImpl { .collect()) } + fn get_epoch_info(&self, meta: Self::Metadata) -> Result { + let bank = meta.request_processor.read().unwrap().bank(); + let epoch_schedule = bank.epoch_schedule(); + let (epoch, slot_index) = epoch_schedule.get_epoch_and_slot_index(bank.slot()); + Ok(RpcEpochInfo { + epoch, + slots_in_epoch: epoch_schedule.get_slots_in_epoch(epoch), + slot_index, + }) + } + + fn get_leader_schedule(&self, meta: Self::Metadata) -> Result>> { + let bank = meta.request_processor.read().unwrap().bank(); + Ok( + crate::leader_schedule_utils::leader_schedule(bank.epoch(), &bank).map( + |leader_schedule| { + leader_schedule + .get_slot_leaders() + .iter() + .map(|pubkey| pubkey.to_string()) + .collect() + }, + ), + ) + } + fn get_recent_blockhash(&self, meta: Self::Metadata) -> Result<(String, FeeCalculator)> { debug!("get_recent_blockhash rpc request received"); Ok(meta