Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Add getVoteAccounts RPC method parameter to restrict results to a single vote account (backport #16855) #16859

Merged
merged 1 commit into from
Apr 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -495,10 +495,17 @@ impl RpcClient {
&self,
commitment_config: CommitmentConfig,
) -> ClientResult<RpcVoteAccountStatus> {
self.send(
RpcRequest::GetVoteAccounts,
json!([self.maybe_map_commitment(commitment_config)?]),
)
self.get_vote_accounts_with_config(RpcGetVoteAccountsConfig {
commitment: Some(self.maybe_map_commitment(commitment_config)?),
..RpcGetVoteAccountsConfig::default()
})
}

pub fn get_vote_accounts_with_config(
&self,
config: RpcGetVoteAccountsConfig,
) -> ClientResult<RpcVoteAccountStatus> {
self.send(RpcRequest::GetVoteAccounts, json!([config]))
}

pub fn wait_for_max_stake(
Expand Down Expand Up @@ -735,15 +742,12 @@ impl RpcClient {
slot: Option<Slot>,
commitment_config: CommitmentConfig,
) -> ClientResult<Option<RpcLeaderSchedule>> {
self.send(
RpcRequest::GetLeaderSchedule,
json!([
slot,
RpcLeaderScheduleConfig {
commitment: Some(self.maybe_map_commitment(commitment_config)?),
..RpcLeaderScheduleConfig::default()
}
]),
self.get_leader_schedule_with_config(
slot,
RpcLeaderScheduleConfig {
commitment: Some(self.maybe_map_commitment(commitment_config)?),
..RpcLeaderScheduleConfig::default()
},
)
}

Expand Down
8 changes: 8 additions & 0 deletions client/src/rpc_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ pub struct RpcLeaderScheduleConfig {
pub commitment: Option<CommitmentConfig>,
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RpcGetVoteAccountsConfig {
pub vote_pubkey: Option<String>, // validator vote address, as a base-58 encoded string
#[serde(flatten)]
pub commitment: Option<CommitmentConfig>,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum RpcLeaderScheduleConfigWrapper {
Expand Down
61 changes: 51 additions & 10 deletions core/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,17 @@ impl JsonRpcRequestProcessor {

fn get_vote_accounts(
&self,
commitment: Option<CommitmentConfig>,
config: Option<RpcGetVoteAccountsConfig>,
) -> Result<RpcVoteAccountStatus> {
let bank = self.bank(commitment);
let config = config.unwrap_or_default();

let filter_by_vote_pubkey = if let Some(ref vote_pubkey) = config.vote_pubkey {
Some(verify_pubkey(vote_pubkey)?)
} else {
None
};

let bank = self.bank(config.commitment);
let vote_accounts = bank.vote_accounts();
let epoch_vote_accounts = bank
.epoch_vote_accounts(bank.get_epoch_and_slot_index(bank.slot()).0)
Expand All @@ -714,7 +722,13 @@ impl JsonRpcRequestProcessor {
Vec<RpcVoteAccountInfo>,
) = vote_accounts
.iter()
.map(|(pubkey, (activated_stake, account))| {
.filter_map(|(vote_pubkey, (activated_stake, account))| {
if let Some(filter_by_vote_pubkey) = filter_by_vote_pubkey {
if *vote_pubkey != filter_by_vote_pubkey {
return None;
}
}

let vote_state = account.vote_state();
let vote_state = vote_state.as_ref().unwrap_or(&default_vote_state);
let last_vote = if let Some(vote) = vote_state.votes.iter().last() {
Expand All @@ -734,16 +748,16 @@ impl JsonRpcRequestProcessor {
epoch_credits.clone()
};

RpcVoteAccountInfo {
vote_pubkey: (pubkey).to_string(),
Some(RpcVoteAccountInfo {
vote_pubkey: vote_pubkey.to_string(),
node_pubkey: vote_state.node_pubkey.to_string(),
activated_stake: *activated_stake,
commission: vote_state.commission,
root_slot: vote_state.root_slot.unwrap_or(0),
epoch_credits,
epoch_vote_account: epoch_vote_accounts.contains_key(pubkey),
epoch_vote_account: epoch_vote_accounts.contains_key(vote_pubkey),
last_vote,
}
})
})
.partition(|vote_account_info| {
if bank.slot() >= DELINQUENT_VALIDATOR_SLOT_DISTANCE as u64 {
Expand Down Expand Up @@ -2099,7 +2113,7 @@ pub mod rpc_minimal {
fn get_vote_accounts(
&self,
meta: Self::Metadata,
commitment: Option<CommitmentConfig>,
config: Option<RpcGetVoteAccountsConfig>,
) -> Result<RpcVoteAccountStatus>;

// TODO: Refactor `solana-validator wait-for-restart-window` to not require this method, so
Expand Down Expand Up @@ -2202,10 +2216,10 @@ pub mod rpc_minimal {
fn get_vote_accounts(
&self,
meta: Self::Metadata,
commitment: Option<CommitmentConfig>,
config: Option<RpcGetVoteAccountsConfig>,
) -> Result<RpcVoteAccountStatus> {
debug!("get_vote_accounts rpc request received");
meta.get_vote_accounts(commitment)
meta.get_vote_accounts(config)
}

// TODO: Refactor `solana-validator wait-for-restart-window` to not require this method, so
Expand Down Expand Up @@ -6023,6 +6037,33 @@ pub mod tests {
]
);

// Filter request based on the leader:
{
let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getVoteAccounts","params":{}}}"#,
json!([RpcGetVoteAccountsConfig {
vote_pubkey: Some(leader_vote_keypair.pubkey().to_string()),
commitment: Some(CommitmentConfig::processed())
}])
);

let res = io.handle_request_sync(&req, meta.clone());
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");

let vote_account_status: RpcVoteAccountStatus =
serde_json::from_value(result["result"].clone()).unwrap();

assert_eq!(vote_account_status.current.len(), 1);
assert_eq!(vote_account_status.delinquent.len(), 0);
for vote_account_info in vote_account_status.current {
assert_eq!(
vote_account_info.vote_pubkey,
leader_vote_keypair.pubkey().to_string()
);
}
}

// Overflow the epoch credits history and ensure only `MAX_RPC_EPOCH_CREDITS_HISTORY`
// results are returned
for _ in 0..(TEST_SLOTS_PER_EPOCH * (MAX_RPC_EPOCH_CREDITS_HISTORY) as u64) {
Expand Down
50 changes: 48 additions & 2 deletions docs/src/developing/clients/jsonrpc-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -2893,11 +2893,14 @@ Returns the account info and associated stake for all the voting accounts in the

#### Parameters:

- `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
- `<object>` - (optional) Configuration object containing the following field:
- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
- (optional) `votePubkey: <string>` - Only return results for this validator vote address (base-58 encoded)

#### Results:

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 address, as base-58 encoded string
- `nodePubkey: <string>` - Validator identity, as base-58 encoded string
Expand Down Expand Up @@ -2950,6 +2953,49 @@ Result:
}
```

#### Example: Restrict results to a single validator vote account

Request:
```bash
curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d '
{
"jsonrpc": "2.0",
"id": 1,
"method": "getVoteAccounts",
"params": [
{
"votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"
}
]
}
'
```

Result:
```json
{
"jsonrpc": "2.0",
"result": {
"current": [
{
"commission": 0,
"epochVoteAccount": true,
"epochCredits": [
[ 1, 64, 0 ],
[ 2, 192, 64 ]
],
"nodePubkey": "B97CCUW3AEZFGy6uUg6zUdnNYvnVq5VG8PUtb2HayTDD",
"lastVote": 147,
"activatedStake": 42,
"votePubkey": "3ZT31jkAGhUaw8jsy4bTknwBMP8i4Eueh52By4zXcsVw"
}
],
"delinquent": []
},
"id": 1
}
```

### minimumLedgerSlot

Returns the lowest slot that the node has information about in its ledger. This
Expand Down