add get_epoch_vote_accounts rpc (#4317)

* add get_epoch_vote_accounts rpc

* fixups

* documentation and type updates
This commit is contained in:
Rob Walker 2019-05-20 22:21:13 -07:00 committed by GitHub
parent 1acfcf3acf
commit ead15d294e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 123 additions and 100 deletions

View File

@ -30,6 +30,7 @@ Methods
* [getSlotLeader](#getslotleader)
* [getNumBlocksSinceSignatureConfirmation](#getnumblockssincesignatureconfirmation)
* [getTransactionCount](#gettransactioncount)
* [getEpochVoteAccounts](#getepochvoteaccounts)
* [requestAirdrop](#requestairdrop)
* [sendTransaction](#sendtransaction)
* [startSubscriptionChannel](#startsubscriptionchannel)
@ -274,6 +275,39 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
---
### getEpochVoteAccounts
Returns the account info and associated stake for all the voting accounts in the current epoch.
##### Parameters:
None
##### Results:
An array consisting of vote accounts:
* `string` - the vote account's Pubkey as base-58 encoded string
* `integer` - the stake, in lamports, delegated to this vote account
* `VoteState` - the vote account's state
Each VoteState will be a JSON object with the following sub fields:
* `votes`, array of most recent vote lockouts
* `node_id`, the pubkey of the node that votes using this account
* `authorized_voter_id`, the pubkey of the authorized vote signer for this account
* `commission`, a 32-bit integer used as a fraction (commission/MAX_U32) for rewards payout
* `root_slot`, the most recent slot this account has achieved maximum lockout
* `credits`, credits accrued by this account for reaching lockouts
##### Example:
```bash
// Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getEpochVoteAccounts"}' http://localhost:8899
// Result
{"jsonrpc":"2.0","result":[[[84,115,89,23,41,83,221,72,58,23,53,245,195,188,140,161,242,189,200,164,139,214,12,180,84,161,28,151,24,243,159,125],10000000,{"authorized_voter_id":[84,115,89,23,41,83,221,72,58,23,53,245,195,188,140,161,242,189,200,164,139,214,12,180,84,161,28,151,24,243,159,125],"commission":0,"credits":0,"node_id":[49,139,227,211,47,39,69,86,131,244,160,144,228,169,84,143,142,253,83,81,212,110,254,12,242,71,219,135,30,60,157,213],"root_slot":null,"votes":[{"confirmation_count":1,"slot":0}]}]],"id":1}
```
---
### requestAirdrop
Requests an airdrop of lamports to a Pubkey

View File

@ -13,6 +13,7 @@ pub enum RpcRequest {
GetRecentBlockhash,
GetSignatureStatus,
GetSlotLeader,
GetEpochVoteAccounts,
GetStorageBlockhash,
GetStorageSlot,
GetStoragePubkeysForSlot,
@ -39,6 +40,7 @@ impl RpcRequest {
RpcRequest::GetRecentBlockhash => "getRecentBlockhash",
RpcRequest::GetSignatureStatus => "getSignatureStatus",
RpcRequest::GetSlotLeader => "getSlotLeader",
RpcRequest::GetEpochVoteAccounts => "getEpochVoteAccounts",
RpcRequest::GetStorageBlockhash => "getStorageBlockhash",
RpcRequest::GetStorageSlot => "getStorageSlot",
RpcRequest::GetStoragePubkeysForSlot => "getStoragePubkeysForSlot",

View File

@ -6,7 +6,6 @@ use crate::contact_info::ContactInfo;
use crate::packet::PACKET_DATA_SIZE;
use crate::storage_stage::StorageState;
use bincode::{deserialize, serialize};
use bs58;
use jsonrpc_core::{Error, Metadata, Result};
use jsonrpc_derive::rpc;
use solana_drone::drone::request_airdrop_transaction;
@ -16,7 +15,7 @@ use solana_sdk::fee_calculator::FeeCalculator;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature;
use solana_sdk::transaction::{self, Transaction};
use std::mem;
use solana_vote_api::vote_state::VoteState;
use std::net::{SocketAddr, UdpSocket};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::{Arc, RwLock};
@ -76,9 +75,8 @@ impl JsonRpcRequestProcessor {
}
fn get_recent_blockhash(&self) -> (String, FeeCalculator) {
let id = self.bank().confirmed_last_blockhash();
(
bs58::encode(id).into_string(),
self.bank().confirmed_last_blockhash().to_string(),
self.bank().fee_calculator.clone(),
)
}
@ -104,14 +102,22 @@ impl JsonRpcRequestProcessor {
Ok(self.bank().transaction_count() as u64)
}
fn get_epoch_vote_accounts(&self) -> Result<Vec<(Pubkey, u64, VoteState)>> {
let bank = self.bank();
Ok(bank
.epoch_vote_accounts(bank.get_stakers_epoch(bank.slot()))
.ok_or_else(Error::invalid_request)?
.iter()
.map(|(k, (s, a))| (*k, *s, VoteState::from(a).unwrap_or_default()))
.collect::<Vec<_>>())
}
fn get_storage_blockhash(&self) -> Result<String> {
let hash = self.storage_state.get_storage_blockhash();
Ok(bs58::encode(hash).into_string())
Ok(self.storage_state.get_storage_blockhash().to_string())
}
fn get_storage_slot(&self) -> Result<u64> {
let slot = self.storage_state.get_slot();
Ok(slot)
Ok(self.storage_state.get_slot())
}
fn get_storage_pubkeys_for_slot(&self, slot: u64) -> Result<Vec<Pubkey>> {
@ -136,35 +142,11 @@ fn get_tpu_addr(cluster_info: &Arc<RwLock<ClusterInfo>>) -> Result<SocketAddr> {
}
fn verify_pubkey(input: String) -> Result<Pubkey> {
let pubkey_vec = bs58::decode(input).into_vec().map_err(|err| {
info!("verify_pubkey: invalid input: {:?}", err);
Error::invalid_request()
})?;
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
info!(
"verify_pubkey: invalid pubkey_vec length: {}",
pubkey_vec.len()
);
Err(Error::invalid_request())
} else {
Ok(Pubkey::new(&pubkey_vec))
}
input.parse().map_err(|_e| Error::invalid_request())
}
fn verify_signature(input: &str) -> Result<Signature> {
let signature_vec = bs58::decode(input).into_vec().map_err(|err| {
info!("verify_signature: invalid input: {}: {:?}", input, err);
Error::invalid_request()
})?;
if signature_vec.len() != mem::size_of::<Signature>() {
info!(
"verify_signature: invalid signature_vec length: {}",
signature_vec.len()
);
Err(Error::invalid_request())
} else {
Ok(Signature::new(&signature_vec))
}
input.parse().map_err(|_e| Error::invalid_request())
}
#[derive(Clone)]
@ -224,6 +206,9 @@ pub trait RpcSol {
#[rpc(meta, name = "getSlotLeader")]
fn get_slot_leader(&self, _: Self::Metadata) -> Result<String>;
#[rpc(meta, name = "getEpochVoteAccounts")]
fn get_epoch_vote_accounts(&self, _: Self::Metadata) -> Result<Vec<(Pubkey, u64, VoteState)>>;
#[rpc(meta, name = "getStorageBlockhash")]
fn get_storage_blockhash(&self, _: Self::Metadata) -> Result<String>;
@ -406,7 +391,7 @@ impl RpcSol for RpcSolImpl {
if signature_status == Some(Ok(())) {
info!("airdrop signature ok");
return Ok(bs58::encode(signature).into_string());
return Ok(signature.to_string());
} else if now.elapsed().as_secs() > 5 {
info!("airdrop signature timeout");
return Err(Error::internal_error());
@ -437,7 +422,7 @@ impl RpcSol for RpcSolImpl {
info!("send_transaction: send_to error: {:?}", err);
Error::internal_error()
})?;
let signature = bs58::encode(tx.signatures[0]).into_string();
let signature = tx.signatures[0].to_string();
trace!(
"send_transaction: sent {} bytes, signature={}",
data.len(),
@ -455,6 +440,16 @@ impl RpcSol for RpcSolImpl {
.to_string())
}
fn get_epoch_vote_accounts(
&self,
meta: Self::Metadata,
) -> Result<Vec<(Pubkey, u64, VoteState)>> {
meta.request_processor
.read()
.unwrap()
.get_epoch_vote_accounts()
}
fn get_storage_blockhash(&self, meta: Self::Metadata) -> Result<String> {
meta.request_processor
.read()

View File

@ -1,7 +1,6 @@
//! The `pubsub` module implements a threaded subscription service on client RPC request
use crate::rpc_subscriptions::{Confirmations, RpcSubscriptions};
use bs58;
use jsonrpc_core::{Error, ErrorCode, Result};
use jsonrpc_derive::rpc;
use jsonrpc_pubsub::typed::Subscriber;
@ -10,7 +9,6 @@ use solana_sdk::account::Account;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::signature::Signature;
use solana_sdk::transaction;
use std::mem;
use std::sync::{atomic, Arc};
#[rpc(server)]
@ -100,6 +98,16 @@ impl RpcSolPubSubImpl {
}
}
use std::str::FromStr;
fn param<T: FromStr>(param_str: &str, thing: &str) -> Result<T> {
param_str.parse::<T>().map_err(|_e| Error {
code: ErrorCode::InvalidParams,
message: format!("Invalid Request: Invalid {} provided", thing),
data: None,
})
}
impl RpcSolPubSub for RpcSolPubSubImpl {
type Metadata = Arc<Session>;
@ -110,26 +118,18 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
pubkey_str: String,
confirmations: Option<Confirmations>,
) {
let pubkey_vec = bs58::decode(pubkey_str).into_vec().unwrap();
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
subscriber
.reject(Error {
code: ErrorCode::InvalidParams,
message: "Invalid Request: Invalid pubkey provided".into(),
data: None,
})
.unwrap();
return;
match param::<Pubkey>(&pubkey_str, "pubkey") {
Ok(pubkey) => {
let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
let sub_id = SubscriptionId::Number(id as u64);
info!("account_subscribe: account={:?} id={:?}", pubkey, sub_id);
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
self.subscriptions
.add_account_subscription(&pubkey, confirmations, &sub_id, &sink)
}
Err(e) => subscriber.reject(e).unwrap(),
}
let pubkey = Pubkey::new(&pubkey_vec);
let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
let sub_id = SubscriptionId::Number(id as u64);
info!("account_subscribe: account={:?} id={:?}", pubkey, sub_id);
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
self.subscriptions
.add_account_subscription(&pubkey, confirmations, &sub_id, &sink)
}
fn account_unsubscribe(
@ -156,26 +156,18 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
pubkey_str: String,
confirmations: Option<Confirmations>,
) {
let pubkey_vec = bs58::decode(pubkey_str).into_vec().unwrap();
if pubkey_vec.len() != mem::size_of::<Pubkey>() {
subscriber
.reject(Error {
code: ErrorCode::InvalidParams,
message: "Invalid Request: Invalid pubkey provided".into(),
data: None,
})
.unwrap();
return;
match param::<Pubkey>(&pubkey_str, "pubkey") {
Ok(pubkey) => {
let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
let sub_id = SubscriptionId::Number(id as u64);
info!("program_subscribe: account={:?} id={:?}", pubkey, sub_id);
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
self.subscriptions
.add_program_subscription(&pubkey, confirmations, &sub_id, &sink)
}
Err(e) => subscriber.reject(e).unwrap(),
}
let pubkey = Pubkey::new(&pubkey_vec);
let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
let sub_id = SubscriptionId::Number(id as u64);
info!("program_subscribe: account={:?} id={:?}", pubkey, sub_id);
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
self.subscriptions
.add_program_subscription(&pubkey, confirmations, &sub_id, &sink)
}
fn program_unsubscribe(
@ -203,29 +195,25 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
confirmations: Option<Confirmations>,
) {
info!("signature_subscribe");
let signature_vec = bs58::decode(signature_str).into_vec().unwrap();
if signature_vec.len() != mem::size_of::<Signature>() {
subscriber
.reject(Error {
code: ErrorCode::InvalidParams,
message: "Invalid Request: Invalid signature provided".into(),
data: None,
})
.unwrap();
return;
match param::<Signature>(&signature_str, "signature") {
Ok(signature) => {
let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
let sub_id = SubscriptionId::Number(id as u64);
info!(
"signature_subscribe: signature={:?} id={:?}",
signature, sub_id
);
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
self.subscriptions.add_signature_subscription(
&signature,
confirmations,
&sub_id,
&sink,
);
}
Err(e) => subscriber.reject(e).unwrap(),
}
let signature = Signature::new(&signature_vec);
let id = self.uid.fetch_add(1, atomic::Ordering::SeqCst);
let sub_id = SubscriptionId::Number(id as u64);
info!(
"signature_subscribe: signature={:?} id={:?}",
signature, sub_id
);
let sink = subscriber.assign_id(sub_id.clone()).unwrap();
self.subscriptions
.add_signature_subscription(&signature, confirmations, &sub_id, &sink);
}
fn signature_unsubscribe(

View File

@ -1,7 +1,6 @@
//! The `pubsub` module implements a threaded subscription service on client RPC request
use crate::bank_forks::BankForks;
use bs58;
use core::hash::Hash;
use jsonrpc_core::futures::Future;
use jsonrpc_pubsub::typed::Sink;
@ -144,7 +143,7 @@ where
fn notify_program(accounts: Vec<(Pubkey, Account)>, sink: &Sink<(String, Account)>, _root: u64) {
for (pubkey, account) in accounts.iter() {
sink.notify(Ok((bs58::encode(pubkey).into_string(), account.clone())))
sink.notify(Ok((pubkey.to_string(), account.clone())))
.wait()
.unwrap();
}

View File

@ -91,6 +91,11 @@ impl VoteState {
serialized_size(&vote_state).unwrap() as usize
}
// utility function, used by Stakes, tests
pub fn from(account: &Account) -> Option<VoteState> {
account.state().ok()
}
pub fn deserialize(input: &[u8]) -> Result<Self, InstructionError> {
deserialize(input).map_err(|_| InstructionError::InvalidAccountData)
}