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,
mock_sender::{MockSender, Mocks},
rpc_config::RpcAccountInfoConfig,
rpc_config::{
RpcBlockConfig, RpcEpochConfig, RpcLargestAccountsConfig, RpcProgramAccountsConfig,
RpcRequestAirdropConfig, RpcSendTransactionConfig, RpcSignaturesForAddressConfig,
RpcSimulateTransactionConfig, RpcTokenAccountsFilter, RpcTransactionConfig,
},
rpc_config::*,
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
rpc_response::*,
rpc_sender::RpcSender,
@ -880,7 +876,7 @@ impl RpcClient {
&self,
slot: Option<Slot>,
) -> 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(
@ -890,10 +886,24 @@ impl RpcClient {
) -> ClientResult<Option<RpcLeaderSchedule>> {
self.send(
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> {
self.send(RpcRequest::GetEpochSchedule, Value::Null)
}

View File

@ -41,6 +41,14 @@ pub struct RpcRequestAirdropConfig {
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)]
#[serde(rename_all = "camelCase")]
pub enum RpcLargestAccountsFilter {

View File

@ -254,10 +254,10 @@ pub struct RpcVoteAccountStatus {
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(rename_all = "camelCase")]
pub struct RpcVoteAccountInfo {
/// Vote account pubkey as base-58 encoded string
/// Vote account address, as base-58 encoded 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,
/// The current stake, in lamports, delegated to this vote account

View File

@ -2110,7 +2110,7 @@ pub mod rpc_minimal {
&self,
meta: Self::Metadata,
slot: Option<Slot>,
commitment: Option<CommitmentConfig>,
config: Option<RpcLeaderScheduleConfig>,
) -> Result<Option<RpcLeaderSchedule>>;
}
@ -2215,9 +2215,15 @@ pub mod rpc_minimal {
&self,
meta: Self::Metadata,
slot: Option<Slot>,
commitment: Option<CommitmentConfig>,
config: Option<RpcLeaderScheduleConfig>,
) -> 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 epoch = bank.epoch_schedule().get_epoch(slot);
@ -2227,9 +2233,14 @@ pub mod rpc_minimal {
.leader_schedule_cache
.get_epoch_leader_schedule(epoch)
.map(|leader_schedule| {
solana_ledger::leader_schedule_utils::leader_schedule_by_identity(
leader_schedule.get_slot_leaders().iter().enumerate(),
)
let mut schedule_by_identity =
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 [
r#"{"jsonrpc":"2.0","id":1,"method":"getLeaderSchedule", "params": [0]}"#,
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()
{
@ -4267,7 +4282,7 @@ pub mod tests {
}
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"))
.expect("actual response deserialization");
@ -4281,6 +4296,27 @@ pub mod tests {
panic!("Expected single response");
};
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]

View File

@ -1366,14 +1366,17 @@ Returns the leader schedule for an epoch
#### 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
- `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
- `<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
- `<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:
- `<null>` - if requested epoch is not found
- `<object>` - otherwise, the result field will be a dictionary of leader public keys
\(as base-58 encoded strings\) and their corresponding leader slot indices as values
- `<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
(indices are relative to the first slot in the requested epoch)
#### 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
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:
- `votePubkey: <string>` - Vote account public key, as base-58 encoded string
- `nodePubkey: <string>` - Node public key, as base-58 encoded string
- `votePubkey: <string>` - Vote account address, 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
- `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

View File

@ -16,7 +16,10 @@ use {
},
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::{
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;
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(|| {
format!(
"Unable to get leader schedule from slot {}",