getLeaderSchedule now supports filtered results based on validator identity

This commit is contained in:
Michael Vines 2021-04-21 14:13:41 -07:00
parent 3f92abedd5
commit 6004c0abf5
6 changed files with 120 additions and 24 deletions

View File

@ -9,11 +9,7 @@ use {
http_sender::HttpSender, http_sender::HttpSender,
mock_sender::{MockSender, Mocks}, mock_sender::{MockSender, Mocks},
rpc_config::RpcAccountInfoConfig, rpc_config::RpcAccountInfoConfig,
rpc_config::{ rpc_config::*,
RpcBlockConfig, RpcEpochConfig, RpcLargestAccountsConfig, RpcProgramAccountsConfig,
RpcRequestAirdropConfig, RpcSendTransactionConfig, RpcSignaturesForAddressConfig,
RpcSimulateTransactionConfig, RpcTokenAccountsFilter, RpcTransactionConfig,
},
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter}, rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
rpc_response::*, rpc_response::*,
rpc_sender::RpcSender, rpc_sender::RpcSender,
@ -880,7 +876,7 @@ impl RpcClient {
&self, &self,
slot: Option<Slot>, slot: Option<Slot>,
) -> ClientResult<Option<RpcLeaderSchedule>> { ) -> ClientResult<Option<RpcLeaderSchedule>> {
self.get_leader_schedule_with_commitment(slot, self.commitment_config) self.get_leader_schedule_with_config(slot, RpcLeaderScheduleConfig::default())
} }
pub fn get_leader_schedule_with_commitment( pub fn get_leader_schedule_with_commitment(
@ -890,10 +886,24 @@ impl RpcClient {
) -> ClientResult<Option<RpcLeaderSchedule>> { ) -> ClientResult<Option<RpcLeaderSchedule>> {
self.send( self.send(
RpcRequest::GetLeaderSchedule, RpcRequest::GetLeaderSchedule,
json!([slot, self.maybe_map_commitment(commitment_config)?]), json!([
slot,
RpcLeaderScheduleConfig {
commitment: Some(self.maybe_map_commitment(commitment_config)?),
..RpcLeaderScheduleConfig::default()
}
]),
) )
} }
pub fn get_leader_schedule_with_config(
&self,
slot: Option<Slot>,
config: RpcLeaderScheduleConfig,
) -> ClientResult<Option<RpcLeaderSchedule>> {
self.send(RpcRequest::GetLeaderSchedule, json!([slot, config]))
}
pub fn get_epoch_schedule(&self) -> ClientResult<EpochSchedule> { pub fn get_epoch_schedule(&self) -> ClientResult<EpochSchedule> {
self.send(RpcRequest::GetEpochSchedule, Value::Null) self.send(RpcRequest::GetEpochSchedule, Value::Null)
} }

View File

@ -41,6 +41,14 @@ pub struct RpcRequestAirdropConfig {
pub commitment: Option<CommitmentConfig>, pub commitment: Option<CommitmentConfig>,
} }
#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcLeaderScheduleConfig {
pub identity: Option<String>, // validator identity, as a base-58 encoded string
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum RpcLargestAccountsFilter { pub enum RpcLargestAccountsFilter {

View File

@ -254,10 +254,10 @@ pub struct RpcVoteAccountStatus {
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub struct RpcVoteAccountInfo { pub struct RpcVoteAccountInfo {
/// Vote account pubkey as base-58 encoded string /// Vote account address, as base-58 encoded string
pub vote_pubkey: String, pub vote_pubkey: String,
/// The pubkey of the node that votes using this account /// The validator identity, as base-58 encoded string
pub node_pubkey: String, pub node_pubkey: String,
/// The current stake, in lamports, delegated to this vote account /// The current stake, in lamports, delegated to this vote account

View File

@ -2110,7 +2110,7 @@ pub mod rpc_minimal {
&self, &self,
meta: Self::Metadata, meta: Self::Metadata,
slot: Option<Slot>, slot: Option<Slot>,
commitment: Option<CommitmentConfig>, config: Option<RpcLeaderScheduleConfig>,
) -> Result<Option<RpcLeaderSchedule>>; ) -> Result<Option<RpcLeaderSchedule>>;
} }
@ -2215,9 +2215,15 @@ pub mod rpc_minimal {
&self, &self,
meta: Self::Metadata, meta: Self::Metadata,
slot: Option<Slot>, slot: Option<Slot>,
commitment: Option<CommitmentConfig>, config: Option<RpcLeaderScheduleConfig>,
) -> Result<Option<RpcLeaderSchedule>> { ) -> Result<Option<RpcLeaderSchedule>> {
let bank = meta.bank(commitment); let config = config.unwrap_or_default();
if let Some(ref identity) = config.identity {
let _ = verify_pubkey(identity)?;
}
let bank = meta.bank(config.commitment);
let slot = slot.unwrap_or_else(|| bank.slot()); let slot = slot.unwrap_or_else(|| bank.slot());
let epoch = bank.epoch_schedule().get_epoch(slot); let epoch = bank.epoch_schedule().get_epoch(slot);
@ -2227,9 +2233,14 @@ pub mod rpc_minimal {
.leader_schedule_cache .leader_schedule_cache
.get_epoch_leader_schedule(epoch) .get_epoch_leader_schedule(epoch)
.map(|leader_schedule| { .map(|leader_schedule| {
solana_ledger::leader_schedule_utils::leader_schedule_by_identity( let mut schedule_by_identity =
leader_schedule.get_slot_leaders().iter().enumerate(), solana_ledger::leader_schedule_utils::leader_schedule_by_identity(
) leader_schedule.get_slot_leaders().iter().enumerate(),
);
if let Some(identity) = config.identity {
schedule_by_identity.retain(|k, _| *k == identity);
}
schedule_by_identity
})) }))
} }
} }
@ -4235,6 +4246,10 @@ pub mod tests {
for req in [ for req in [
r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [0]}"#, r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [0]}"#,
r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule"}"#, r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule"}"#,
&format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [null, {{ "identity": "{}" }}]}}"#,
bank.collector_id().to_string()
),
] ]
.iter() .iter()
{ {
@ -4267,7 +4282,7 @@ pub mod tests {
} }
let req = r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [42424242]}"#; let req = r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [42424242]}"#;
let rep = io.handle_request_sync(&req, meta); let rep = io.handle_request_sync(&req, meta.clone());
let res: Response = serde_json::from_str(&rep.expect("actual response")) let res: Response = serde_json::from_str(&rep.expect("actual response"))
.expect("actual response deserialization"); .expect("actual response deserialization");
@ -4281,6 +4296,27 @@ pub mod tests {
panic!("Expected single response"); panic!("Expected single response");
}; };
assert_eq!(schedule, None); 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": [null, {{ "identity": "{}"}}]}}"#,
bob_pubkey
);
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<RpcLeaderSchedule> = 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()));
} }
#[test] #[test]

View File

@ -1366,14 +1366,17 @@ Returns the leader schedule for an epoch
#### Parameters: #### Parameters:
- `<u64>` - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot. If unspecified, the leader schedule for the current epoch is fetched - `<u64>` - (optional) Fetch the leader schedule for the epoch that corresponds to the provided slot.
- `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) If unspecified, the leader schedule for the current epoch is fetched
- `<object>` - (optional) Configuration object containing the following field:
- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
- (optional) `identity: <string>` - Only return results for this validator identity (base-58 encoded)
#### Results: #### Results:
- `<null>` - if requested epoch is not found - `<null>` - if requested epoch is not found
- `<object>` - otherwise, the result field will be a dictionary of leader public keys - `<object>` - otherwise, the result field will be a dictionary of validator identities,
\(as base-58 encoded strings\) and their corresponding leader slot indices as values as base-58 encoded strings, and their corresponding leader slot indices as values
(indices are relative to the first slot in the requested epoch) (indices are relative to the first slot in the requested epoch)
#### Example: #### Example:
@ -1396,6 +1399,36 @@ Result:
} }
``` ```
#### Example:
Request:
```bash
curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d '
{
"jsonrpc": "2.0",
"id": 1,
"method": "getLeaderSchedule",
"params": [
null,
{
"identity": "4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F"
}
]
}
'
```
Result:
```json
{
"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
}
```
### getMaxRetransmitSlot ### getMaxRetransmitSlot
Get the max slot seen from retransmit stage. Get the max slot seen from retransmit stage.
@ -2820,8 +2853,8 @@ Returns the account info and associated stake for all the voting accounts in the
The result field will be a JSON object of `current` and `delinquent` accounts, each containing an array of JSON objects with the following sub fields: The result field will be a JSON object of `current` and `delinquent` accounts, each containing an array of JSON objects with the following sub fields:
- `votePubkey: <string>` - Vote account public key, as base-58 encoded string - `votePubkey: <string>` - Vote account address, as base-58 encoded string
- `nodePubkey: <string>` - Node public key, as base-58 encoded string - `nodePubkey: <string>` - Validator identity, as base-58 encoded string
- `activatedStake: <u64>` - the stake, in lamports, delegated to this vote account and active in this epoch - `activatedStake: <u64>` - the stake, in lamports, delegated to this vote account and active in this epoch
- `epochVoteAccount: <bool>` - bool, whether the vote account is staked for this epoch - `epochVoteAccount: <bool>` - bool, whether the vote account is staked for this epoch
- `commission: <number>`, percentage (0-100) of rewards payout owed to the vote account - `commission: <number>`, percentage (0-100) of rewards payout owed to the vote account

View File

@ -16,7 +16,10 @@ use {
}, },
keypair::SKIP_SEED_PHRASE_VALIDATION_ARG, keypair::SKIP_SEED_PHRASE_VALIDATION_ARG,
}, },
solana_client::{rpc_client::RpcClient, rpc_request::MAX_MULTIPLE_ACCOUNTS}, solana_client::{
rpc_client::RpcClient, rpc_config::RpcLeaderScheduleConfig,
rpc_request::MAX_MULTIPLE_ACCOUNTS,
},
solana_core::ledger_cleanup_service::{ solana_core::ledger_cleanup_service::{
DEFAULT_MAX_LEDGER_SHREDS, DEFAULT_MIN_MAX_LEDGER_SHREDS, DEFAULT_MAX_LEDGER_SHREDS, DEFAULT_MIN_MAX_LEDGER_SHREDS,
}, },
@ -154,7 +157,13 @@ fn wait_for_restart_window(
)); ));
let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index; let first_slot_in_epoch = epoch_info.absolute_slot - epoch_info.slot_index;
leader_schedule = rpc_client leader_schedule = rpc_client
.get_leader_schedule(Some(first_slot_in_epoch))? .get_leader_schedule_with_config(
Some(first_slot_in_epoch),
RpcLeaderScheduleConfig {
identity: Some(identity.to_string()),
..RpcLeaderScheduleConfig::default()
},
)?
.ok_or_else(|| { .ok_or_else(|| {
format!( format!(
"Unable to get leader schedule from slot {}", "Unable to get leader schedule from slot {}",