parent
3aedb81d48
commit
f5b0d13f08
|
@ -192,3 +192,10 @@ pub struct RpcStorageTurn {
|
|||
pub blockhash: String,
|
||||
pub slot: Slot,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct RpcAccountBalance {
|
||||
pub address: String,
|
||||
pub lamports: u64,
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ use solana_client::{
|
|||
use solana_faucet::faucet::request_airdrop_transaction;
|
||||
use solana_ledger::{bank_forks::BankForks, blockstore::Blockstore};
|
||||
use solana_perf::packet::PACKET_DATA_SIZE;
|
||||
use solana_runtime::bank::Bank;
|
||||
use solana_runtime::{accounts::AccountAddressFilter, bank::Bank};
|
||||
use solana_sdk::{
|
||||
clock::{Slot, UnixTimestamp},
|
||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||
|
@ -37,7 +37,7 @@ use solana_transaction_status::{
|
|||
use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
|
||||
use std::{
|
||||
cmp::max,
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
net::{SocketAddr, UdpSocket},
|
||||
str::FromStr,
|
||||
sync::{Arc, RwLock},
|
||||
|
@ -46,6 +46,7 @@ use std::{
|
|||
};
|
||||
|
||||
const JSON_RPC_SERVER_ERROR_0: i64 = -32000;
|
||||
const NUM_LARGEST_ACCOUNTS: usize = 20;
|
||||
|
||||
type RpcResponse<T> = Result<Response<T>>;
|
||||
|
||||
|
@ -280,6 +281,27 @@ impl JsonRpcRequestProcessor {
|
|||
Ok(self.bank(commitment)?.capitalization())
|
||||
}
|
||||
|
||||
fn get_largest_accounts(
|
||||
&self,
|
||||
commitment: Option<CommitmentConfig>,
|
||||
) -> RpcResponse<Vec<RpcAccountBalance>> {
|
||||
let bank = self.bank(commitment)?;
|
||||
new_response(
|
||||
&bank,
|
||||
bank.get_largest_accounts(
|
||||
NUM_LARGEST_ACCOUNTS,
|
||||
&HashSet::new(),
|
||||
AccountAddressFilter::Exclude,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|(address, lamports)| RpcAccountBalance {
|
||||
address: address.to_string(),
|
||||
lamports,
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn get_vote_accounts(
|
||||
&self,
|
||||
commitment: Option<CommitmentConfig>,
|
||||
|
@ -730,6 +752,13 @@ pub trait RpcSol {
|
|||
commitment: Option<CommitmentConfig>,
|
||||
) -> Result<u64>;
|
||||
|
||||
#[rpc(meta, name = "getLargestAccounts")]
|
||||
fn get_largest_accounts(
|
||||
&self,
|
||||
meta: Self::Metadata,
|
||||
commitment: Option<CommitmentConfig>,
|
||||
) -> RpcResponse<Vec<RpcAccountBalance>>;
|
||||
|
||||
#[rpc(meta, name = "requestAirdrop")]
|
||||
fn request_airdrop(
|
||||
&self,
|
||||
|
@ -1131,6 +1160,18 @@ impl RpcSol for RpcSolImpl {
|
|||
.get_total_supply(commitment)
|
||||
}
|
||||
|
||||
fn get_largest_accounts(
|
||||
&self,
|
||||
meta: Self::Metadata,
|
||||
commitment: Option<CommitmentConfig>,
|
||||
) -> RpcResponse<Vec<RpcAccountBalance>> {
|
||||
debug!("get_largest_accounts rpc request received");
|
||||
meta.request_processor
|
||||
.read()
|
||||
.unwrap()
|
||||
.get_largest_accounts(commitment)
|
||||
}
|
||||
|
||||
fn request_airdrop(
|
||||
&self,
|
||||
meta: Self::Metadata,
|
||||
|
@ -1771,6 +1812,49 @@ pub mod tests {
|
|||
assert!(supply >= TEST_MINT_LAMPORTS);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_largest_accounts() {
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
let RpcHandler {
|
||||
io, meta, alice, ..
|
||||
} = start_rpc_handler_with_tx(&bob_pubkey);
|
||||
let req = format!(r#"{{"jsonrpc":"2.0","id":1,"method":"getLargestAccounts"}}"#);
|
||||
let res = io.handle_request_sync(&req, meta.clone());
|
||||
let json: Value = serde_json::from_str(&res.unwrap()).unwrap();
|
||||
let largest_accounts: Vec<RpcAccountBalance> =
|
||||
serde_json::from_value(json["result"]["value"].clone())
|
||||
.expect("actual response deserialization");
|
||||
assert_eq!(largest_accounts.len(), 18);
|
||||
|
||||
// Get Alice balance
|
||||
let req = format!(
|
||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getBalance","params":["{}"]}}"#,
|
||||
alice.pubkey()
|
||||
);
|
||||
let res = io.handle_request_sync(&req, meta.clone());
|
||||
let json: Value = serde_json::from_str(&res.unwrap()).unwrap();
|
||||
let alice_balance: u64 = serde_json::from_value(json["result"]["value"].clone())
|
||||
.expect("actual response deserialization");
|
||||
assert!(largest_accounts.contains(&RpcAccountBalance {
|
||||
address: alice.pubkey().to_string(),
|
||||
lamports: alice_balance,
|
||||
}));
|
||||
|
||||
// Get Bob balance
|
||||
let req = format!(
|
||||
r#"{{"jsonrpc":"2.0","id":1,"method":"getBalance","params":["{}"]}}"#,
|
||||
bob_pubkey
|
||||
);
|
||||
let res = io.handle_request_sync(&req, meta);
|
||||
let json: Value = serde_json::from_str(&res.unwrap()).unwrap();
|
||||
let bob_balance: u64 = serde_json::from_value(json["result"]["value"].clone())
|
||||
.expect("actual response deserialization");
|
||||
assert!(largest_accounts.contains(&RpcAccountBalance {
|
||||
address: bob_pubkey.to_string(),
|
||||
lamports: bob_balance,
|
||||
}));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rpc_get_minimum_balance_for_rent_exemption() {
|
||||
let bob_pubkey = Pubkey::new_rand();
|
||||
|
|
|
@ -31,6 +31,7 @@ To interact with a Solana node inside a JavaScript application, use the [solana-
|
|||
* [getGenesisHash](jsonrpc-api.md#getgenesishash)
|
||||
* [getIdentity](jsonrpc-api.md#getidentity)
|
||||
* [getInflation](jsonrpc-api.md#getinflation)
|
||||
* [getLargestAccounts](jsonrpc-api.md#getlargestaccounts)
|
||||
* [getLeaderSchedule](jsonrpc-api.md#getleaderschedule)
|
||||
* [getMinimumBalanceForRentExemption](jsonrpc-api.md#getminimumbalanceforrentexemption)
|
||||
* [getProgramAccounts](jsonrpc-api.md#getprogramaccounts)
|
||||
|
@ -634,6 +635,32 @@ curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "m
|
|||
{"jsonrpc":"2.0","result":{"foundation":0.05,"foundationTerm":7.0,"initial":0.15,"storage":0.1,"taper":0.15,"terminal":0.015},"id":1}
|
||||
```
|
||||
|
||||
### getLargestAccounts
|
||||
|
||||
Returns the 20 largest accounts, by lamport balance
|
||||
|
||||
#### Parameters:
|
||||
|
||||
* `<object>` - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment)
|
||||
|
||||
#### Results:
|
||||
|
||||
The result will be an RpcResponse JSON object with `value` equal to an array of:
|
||||
|
||||
* `<object>` - otherwise, a JSON object containing:
|
||||
* `address: <string>`, base-58 encoded address of the account
|
||||
* `lamports: <u64>`, number of lamports in the account, as a u64
|
||||
|
||||
#### Example:
|
||||
|
||||
```bash
|
||||
// Request
|
||||
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0","id":1, "method":"getInflation"}' http://localhost:8899
|
||||
|
||||
// Result
|
||||
{"jsonrpc":"2.0","result":{"context":{"slot":54},"value":[{"lamports":999974,"address":"99P8ZgtJYe1buSK8JXkvpLh8xPsCFuLYhz9hQFNw93WJ"},{"lamports":42,"address":"uPwWLo16MVehpyWqsLkK3Ka8nLowWvAHbBChqv2FZeL"},{"lamports":42,"address":"aYJCgU7REfu3XF8b3QhkqgqQvLizx8zxuLBHA25PzDS"},{"lamports":42,"address":"CTvHVtQ4gd4gUcw3bdVgZJJqApXE9nCbbbP4VTS5wE1D"},{"lamports":20,"address":"4fq3xJ6kfrh9RkJQsmVd5gNMvJbuSHfErywvEjNQDPxu"},{"lamports":4,"address":"AXJADheGVp9cruP8WYu46oNkRbeASngN5fPCMVGQqNHa"},{"lamports":2,"address":"8NT8yS6LiwNprgW4yM1jPPow7CwRUotddBVkrkWgYp24"},{"lamports":1,"address":"SysvarEpochSchedu1e111111111111111111111111"},{"lamports":1,"address":"11111111111111111111111111111111"},{"lamports":1,"address":"Stake11111111111111111111111111111111111111"},{"lamports":1,"address":"SysvarC1ock11111111111111111111111111111111"},{"lamports":1,"address":"StakeConfig11111111111111111111111111111111"},{"lamports":1,"address":"SysvarRent111111111111111111111111111111111"},{"lamports":1,"address":"Config1111111111111111111111111111111111111"},{"lamports":1,"address":"SysvarStakeHistory1111111111111111111111111"},{"lamports":1,"address":"SysvarRecentB1ockHashes11111111111111111111"},{"lamports":1,"address":"SysvarFees111111111111111111111111111111111"},{"lamports":1,"address":"Vote111111111111111111111111111111111111111"}]},"id":1}
|
||||
```
|
||||
|
||||
### getLeaderSchedule
|
||||
|
||||
Returns the leader schedule for an epoch
|
||||
|
|
|
@ -59,6 +59,11 @@ pub type TransactionLoaders = Vec<Vec<(Pubkey, Account)>>;
|
|||
|
||||
pub type TransactionLoadResult = (TransactionAccounts, TransactionLoaders, TransactionRent);
|
||||
|
||||
pub enum AccountAddressFilter {
|
||||
Exclude, // exclude all addresses matching the fiter
|
||||
Include, // only include addresses matching the filter
|
||||
}
|
||||
|
||||
impl Accounts {
|
||||
pub fn new(paths: Vec<PathBuf>) -> Self {
|
||||
Self::new_with_frozen_accounts(paths, &HashMap::default(), &[])
|
||||
|
@ -407,6 +412,39 @@ impl Accounts {
|
|||
})
|
||||
}
|
||||
|
||||
pub fn load_largest_accounts(
|
||||
&self,
|
||||
ancestors: &Ancestors,
|
||||
num: usize,
|
||||
filter_by_address: &HashSet<Pubkey>,
|
||||
filter: AccountAddressFilter,
|
||||
) -> Vec<(Pubkey, u64)> {
|
||||
let mut accounts_balances = self.accounts_db.scan_accounts(
|
||||
ancestors,
|
||||
|collector: &mut Vec<(Pubkey, u64)>, option| {
|
||||
if let Some(data) = option
|
||||
.filter(|(pubkey, account, _)| {
|
||||
let should_include_pubkey = match filter {
|
||||
AccountAddressFilter::Exclude => !filter_by_address.contains(&pubkey),
|
||||
AccountAddressFilter::Include => filter_by_address.contains(&pubkey),
|
||||
};
|
||||
should_include_pubkey
|
||||
&& account.lamports != 0
|
||||
&& !(account.lamports == std::u64::MAX
|
||||
&& account.owner == solana_storage_program::id())
|
||||
})
|
||||
.map(|(pubkey, account, _slot)| (*pubkey, account.lamports))
|
||||
{
|
||||
collector.push(data)
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
accounts_balances.sort_by(|a, b| a.1.cmp(&b.1).reverse());
|
||||
accounts_balances.truncate(num);
|
||||
accounts_balances
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn verify_bank_hash(&self, slot: Slot, ancestors: &Ancestors) -> bool {
|
||||
if let Err(err) = self.accounts_db.verify_bank_hash(slot, ancestors) {
|
||||
|
|
|
@ -3,7 +3,10 @@
|
|||
//! on behalf of the caller, and a low-level API for when they have
|
||||
//! already been signed and verified.
|
||||
use crate::{
|
||||
accounts::{Accounts, TransactionAccounts, TransactionLoadResult, TransactionLoaders},
|
||||
accounts::{
|
||||
AccountAddressFilter, Accounts, TransactionAccounts, TransactionLoadResult,
|
||||
TransactionLoaders,
|
||||
},
|
||||
accounts_db::{AccountsDBSerialize, ErrorCounters, SnapshotStorage, SnapshotStorages},
|
||||
accounts_index::Ancestors,
|
||||
blockhash_queue::BlockhashQueue,
|
||||
|
@ -58,7 +61,7 @@ use solana_stake_program::stake_state::{self, Delegation};
|
|||
use solana_vote_program::vote_state::VoteState;
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
collections::{HashMap, HashSet},
|
||||
io::{BufReader, Cursor, Error as IOError, Read},
|
||||
path::{Path, PathBuf},
|
||||
rc::Rc,
|
||||
|
@ -1844,6 +1847,17 @@ impl Bank {
|
|||
None
|
||||
}
|
||||
|
||||
pub fn get_largest_accounts(
|
||||
&self,
|
||||
num: usize,
|
||||
filter_by_address: &HashSet<Pubkey>,
|
||||
filter: AccountAddressFilter,
|
||||
) -> Vec<(Pubkey, u64)> {
|
||||
self.rc
|
||||
.accounts
|
||||
.load_largest_accounts(&self.ancestors, num, filter_by_address, filter)
|
||||
}
|
||||
|
||||
pub fn transaction_count(&self) -> u64 {
|
||||
self.transaction_count.load(Ordering::Relaxed)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue