Add account-decoder crate and use to decode vote and system (nonce) accounts
This commit is contained in:
22
Cargo.lock
generated
22
Cargo.lock
generated
@ -3629,6 +3629,23 @@ dependencies = [
|
|||||||
"winapi 0.3.8",
|
"winapi 0.3.8",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "solana-account-decoder"
|
||||||
|
version = "1.3.0"
|
||||||
|
dependencies = [
|
||||||
|
"Inflector",
|
||||||
|
"bincode",
|
||||||
|
"bs58 0.3.1",
|
||||||
|
"lazy_static",
|
||||||
|
"serde",
|
||||||
|
"serde_derive",
|
||||||
|
"serde_json",
|
||||||
|
"solana-sdk 1.3.0",
|
||||||
|
"solana-vote-program",
|
||||||
|
"spl-memo",
|
||||||
|
"thiserror",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "solana-accounts-bench"
|
name = "solana-accounts-bench"
|
||||||
version = "1.3.0"
|
version = "1.3.0"
|
||||||
@ -3816,6 +3833,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"solana-account-decoder",
|
||||||
"solana-budget-program",
|
"solana-budget-program",
|
||||||
"solana-clap-utils",
|
"solana-clap-utils",
|
||||||
"solana-cli-config",
|
"solana-cli-config",
|
||||||
@ -3866,6 +3884,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
|
"solana-account-decoder",
|
||||||
"solana-logger",
|
"solana-logger",
|
||||||
"solana-net-utils",
|
"solana-net-utils",
|
||||||
"solana-sdk 1.3.0",
|
"solana-sdk 1.3.0",
|
||||||
@ -3925,6 +3944,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
"serial_test_derive",
|
"serial_test_derive",
|
||||||
|
"solana-account-decoder",
|
||||||
"solana-bpf-loader-program",
|
"solana-bpf-loader-program",
|
||||||
"solana-budget-program",
|
"solana-budget-program",
|
||||||
"solana-clap-utils",
|
"solana-clap-utils",
|
||||||
@ -4792,6 +4812,8 @@ dependencies = [
|
|||||||
"serde_derive",
|
"serde_derive",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"solana-sdk 1.3.0",
|
"solana-sdk 1.3.0",
|
||||||
|
"solana-stake-program",
|
||||||
|
"solana-vote-program",
|
||||||
"spl-memo",
|
"spl-memo",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -54,6 +54,7 @@ members = [
|
|||||||
"sys-tuner",
|
"sys-tuner",
|
||||||
"tokens",
|
"tokens",
|
||||||
"transaction-status",
|
"transaction-status",
|
||||||
|
"account-decoder",
|
||||||
"upload-perf",
|
"upload-perf",
|
||||||
"net-utils",
|
"net-utils",
|
||||||
"version",
|
"version",
|
||||||
|
25
account-decoder/Cargo.toml
Normal file
25
account-decoder/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "solana-account-decoder"
|
||||||
|
version = "1.3.0"
|
||||||
|
description = "Solana account decoder"
|
||||||
|
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
|
||||||
|
repository = "https://github.com/solana-labs/solana"
|
||||||
|
homepage = "https://solana.com/"
|
||||||
|
license = "Apache-2.0"
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
bincode = "1.2.1"
|
||||||
|
bs58 = "0.3.1"
|
||||||
|
Inflector = "0.11.4"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
|
solana-sdk = { path = "../sdk", version = "1.3.0" }
|
||||||
|
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
|
||||||
|
spl-memo = "1.0.0"
|
||||||
|
serde = "1.0.112"
|
||||||
|
serde_derive = "1.0.103"
|
||||||
|
serde_json = "1.0.54"
|
||||||
|
thiserror = "1.0"
|
||||||
|
|
||||||
|
[package.metadata.docs.rs]
|
||||||
|
targets = ["x86_64-unknown-linux-gnu"]
|
80
account-decoder/src/lib.rs
Normal file
80
account-decoder/src/lib.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate lazy_static;
|
||||||
|
#[macro_use]
|
||||||
|
extern crate serde_derive;
|
||||||
|
|
||||||
|
pub mod parse_account_data;
|
||||||
|
pub mod parse_nonce;
|
||||||
|
pub mod parse_vote;
|
||||||
|
|
||||||
|
use crate::parse_account_data::parse_account_data;
|
||||||
|
use serde_json::Value;
|
||||||
|
use solana_sdk::{account::Account, clock::Epoch, pubkey::Pubkey};
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
/// A duplicate representation of a Message for pretty JSON serialization
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcAccount {
|
||||||
|
pub lamports: u64,
|
||||||
|
pub data: EncodedAccount,
|
||||||
|
pub owner: String,
|
||||||
|
pub executable: bool,
|
||||||
|
pub rent_epoch: Epoch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
|
pub enum EncodedAccount {
|
||||||
|
Binary(String),
|
||||||
|
Json(Value),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Vec<u8>> for EncodedAccount {
|
||||||
|
fn from(data: Vec<u8>) -> Self {
|
||||||
|
Self::Binary(bs58::encode(data).into_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum AccountEncoding {
|
||||||
|
Binary,
|
||||||
|
Json,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RpcAccount {
|
||||||
|
pub fn encode(account: Account, encoding: AccountEncoding) -> Self {
|
||||||
|
let data = match encoding {
|
||||||
|
AccountEncoding::Binary => account.data.into(),
|
||||||
|
AccountEncoding::Json => {
|
||||||
|
if let Ok(parsed_data) = parse_account_data(&account.owner, &account.data) {
|
||||||
|
EncodedAccount::Json(parsed_data)
|
||||||
|
} else {
|
||||||
|
account.data.into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
RpcAccount {
|
||||||
|
lamports: account.lamports,
|
||||||
|
data,
|
||||||
|
owner: account.owner.to_string(),
|
||||||
|
executable: account.executable,
|
||||||
|
rent_epoch: account.rent_epoch,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decode(&self) -> Option<Account> {
|
||||||
|
let data = match &self.data {
|
||||||
|
EncodedAccount::Json(_) => None,
|
||||||
|
EncodedAccount::Binary(blob) => bs58::decode(blob).into_vec().ok(),
|
||||||
|
}?;
|
||||||
|
Some(Account {
|
||||||
|
lamports: self.lamports,
|
||||||
|
data,
|
||||||
|
owner: Pubkey::from_str(&self.owner).ok()?,
|
||||||
|
executable: self.executable,
|
||||||
|
rent_epoch: self.rent_epoch,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
51
account-decoder/src/parse_account_data.rs
Normal file
51
account-decoder/src/parse_account_data.rs
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
use crate::{parse_nonce::parse_nonce, parse_vote::parse_vote};
|
||||||
|
use inflector::Inflector;
|
||||||
|
use serde_json::{json, Value};
|
||||||
|
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program};
|
||||||
|
use std::{collections::HashMap, str::FromStr};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
|
lazy_static! {
|
||||||
|
static ref SYSTEM_PROGRAM_ID: Pubkey =
|
||||||
|
Pubkey::from_str(&system_program::id().to_string()).unwrap();
|
||||||
|
static ref VOTE_PROGRAM_ID: Pubkey =
|
||||||
|
Pubkey::from_str(&solana_vote_program::id().to_string()).unwrap();
|
||||||
|
pub static ref PARSABLE_PROGRAM_IDS: HashMap<Pubkey, ParsableAccount> = {
|
||||||
|
let mut m = HashMap::new();
|
||||||
|
m.insert(*SYSTEM_PROGRAM_ID, ParsableAccount::Nonce);
|
||||||
|
m.insert(*VOTE_PROGRAM_ID, ParsableAccount::Vote);
|
||||||
|
m
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Error, Debug)]
|
||||||
|
pub enum ParseAccountError {
|
||||||
|
#[error("Program not parsable")]
|
||||||
|
ProgramNotParsable,
|
||||||
|
|
||||||
|
#[error("Instruction error")]
|
||||||
|
InstructionError(#[from] InstructionError),
|
||||||
|
|
||||||
|
#[error("Serde json error")]
|
||||||
|
SerdeJsonError(#[from] serde_json::error::Error),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum ParsableAccount {
|
||||||
|
Nonce,
|
||||||
|
Vote,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_account_data(program_id: &Pubkey, data: &[u8]) -> Result<Value, ParseAccountError> {
|
||||||
|
let program_name = PARSABLE_PROGRAM_IDS
|
||||||
|
.get(program_id)
|
||||||
|
.ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
|
||||||
|
let parsed_json = match program_name {
|
||||||
|
ParsableAccount::Nonce => serde_json::to_value(parse_nonce(data)?)?,
|
||||||
|
ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
|
||||||
|
};
|
||||||
|
Ok(json!({
|
||||||
|
format!("{:?}", program_name).to_kebab_case(): parsed_json
|
||||||
|
}))
|
||||||
|
}
|
35
account-decoder/src/parse_nonce.rs
Normal file
35
account-decoder/src/parse_nonce.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
use crate::parse_account_data::ParseAccountError;
|
||||||
|
use solana_sdk::{
|
||||||
|
fee_calculator::FeeCalculator,
|
||||||
|
instruction::InstructionError,
|
||||||
|
nonce::{state::Versions, State},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parse_nonce(data: &[u8]) -> Result<RpcNonceState, ParseAccountError> {
|
||||||
|
let nonce_state: Versions = bincode::deserialize(data)
|
||||||
|
.map_err(|_| ParseAccountError::from(InstructionError::InvalidAccountData))?;
|
||||||
|
let nonce_state = nonce_state.convert_to_current();
|
||||||
|
match nonce_state {
|
||||||
|
State::Uninitialized => Ok(RpcNonceState::Uninitialized),
|
||||||
|
State::Initialized(data) => Ok(RpcNonceState::Initialized(RpcNonceData {
|
||||||
|
authority: data.authority.to_string(),
|
||||||
|
blockhash: data.blockhash.to_string(),
|
||||||
|
fee_calculator: data.fee_calculator,
|
||||||
|
})),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum RpcNonceState {
|
||||||
|
Uninitialized,
|
||||||
|
Initialized(RpcNonceData),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcNonceData {
|
||||||
|
pub authority: String,
|
||||||
|
pub blockhash: String,
|
||||||
|
pub fee_calculator: FeeCalculator,
|
||||||
|
}
|
113
account-decoder/src/parse_vote.rs
Normal file
113
account-decoder/src/parse_vote.rs
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
use crate::parse_account_data::ParseAccountError;
|
||||||
|
use solana_sdk::{
|
||||||
|
clock::{Epoch, Slot},
|
||||||
|
pubkey::Pubkey,
|
||||||
|
};
|
||||||
|
use solana_vote_program::vote_state::{BlockTimestamp, Lockout, VoteState};
|
||||||
|
|
||||||
|
pub fn parse_vote(data: &[u8]) -> Result<RpcVoteState, ParseAccountError> {
|
||||||
|
let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?;
|
||||||
|
let epoch_credits = vote_state
|
||||||
|
.epoch_credits()
|
||||||
|
.iter()
|
||||||
|
.map(|(epoch, credits, previous_credits)| RpcEpochCredits {
|
||||||
|
epoch: *epoch,
|
||||||
|
credits: *credits,
|
||||||
|
previous_credits: *previous_credits,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let votes = vote_state
|
||||||
|
.votes
|
||||||
|
.iter()
|
||||||
|
.map(|lockout| RpcLockout {
|
||||||
|
slot: lockout.slot,
|
||||||
|
confirmation_count: lockout.confirmation_count,
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let authorized_voters = vote_state
|
||||||
|
.authorized_voters()
|
||||||
|
.iter()
|
||||||
|
.map(|(epoch, authorized_voter)| RpcAuthorizedVoters {
|
||||||
|
epoch: *epoch,
|
||||||
|
authorized_voter: authorized_voter.to_string(),
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let prior_voters = vote_state
|
||||||
|
.prior_voters()
|
||||||
|
.buf()
|
||||||
|
.iter()
|
||||||
|
.filter(|(pubkey, _, _)| pubkey != &Pubkey::default())
|
||||||
|
.map(
|
||||||
|
|(authorized_pubkey, epoch_of_last_authorized_switch, target_epoch)| RpcPriorVoters {
|
||||||
|
authorized_pubkey: authorized_pubkey.to_string(),
|
||||||
|
epoch_of_last_authorized_switch: *epoch_of_last_authorized_switch,
|
||||||
|
target_epoch: *target_epoch,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.collect();
|
||||||
|
Ok(RpcVoteState {
|
||||||
|
node_pubkey: vote_state.node_pubkey.to_string(),
|
||||||
|
authorized_withdrawer: vote_state.authorized_withdrawer.to_string(),
|
||||||
|
commission: vote_state.commission,
|
||||||
|
votes,
|
||||||
|
root_slot: vote_state.root_slot,
|
||||||
|
authorized_voters,
|
||||||
|
prior_voters,
|
||||||
|
epoch_credits,
|
||||||
|
last_timestamp: vote_state.last_timestamp,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A duplicate representation of VoteState for pretty JSON serialization
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcVoteState {
|
||||||
|
node_pubkey: String,
|
||||||
|
authorized_withdrawer: String,
|
||||||
|
commission: u8,
|
||||||
|
votes: Vec<RpcLockout>,
|
||||||
|
root_slot: Option<Slot>,
|
||||||
|
authorized_voters: Vec<RpcAuthorizedVoters>,
|
||||||
|
prior_voters: Vec<RpcPriorVoters>,
|
||||||
|
epoch_credits: Vec<RpcEpochCredits>,
|
||||||
|
last_timestamp: BlockTimestamp,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct RpcLockout {
|
||||||
|
slot: Slot,
|
||||||
|
confirmation_count: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Lockout> for RpcLockout {
|
||||||
|
fn from(lockout: &Lockout) -> Self {
|
||||||
|
Self {
|
||||||
|
slot: lockout.slot,
|
||||||
|
confirmation_count: lockout.confirmation_count,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct RpcAuthorizedVoters {
|
||||||
|
epoch: Epoch,
|
||||||
|
authorized_voter: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct RpcPriorVoters {
|
||||||
|
authorized_pubkey: String,
|
||||||
|
epoch_of_last_authorized_switch: Epoch,
|
||||||
|
target_epoch: Epoch,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
struct RpcEpochCredits {
|
||||||
|
epoch: Epoch,
|
||||||
|
credits: u64,
|
||||||
|
previous_credits: u64,
|
||||||
|
}
|
@ -27,6 +27,7 @@ reqwest = { version = "0.10.6", default-features = false, features = ["blocking"
|
|||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.54"
|
serde_json = "1.0.54"
|
||||||
|
solana-account-decoder = { path = "../account-decoder", version = "1.3.0" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.3.0" }
|
solana-budget-program = { path = "../programs/budget", version = "1.3.0" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.3.0" }
|
||||||
solana-cli-config = { path = "../cli-config", version = "1.3.0" }
|
solana-cli-config = { path = "../cli-config", version = "1.3.0" }
|
||||||
|
@ -15,6 +15,7 @@ use clap::{App, AppSettings, Arg, ArgMatches, SubCommand};
|
|||||||
use log::*;
|
use log::*;
|
||||||
use num_traits::FromPrimitive;
|
use num_traits::FromPrimitive;
|
||||||
use serde_json::{self, json, Value};
|
use serde_json::{self, json, Value};
|
||||||
|
use solana_account_decoder::{AccountEncoding, RpcAccount};
|
||||||
use solana_budget_program::budget_instruction::{self, BudgetError};
|
use solana_budget_program::budget_instruction::{self, BudgetError};
|
||||||
use solana_clap_utils::{
|
use solana_clap_utils::{
|
||||||
commitment::commitment_arg_with_default, input_parsers::*, input_validators::*,
|
commitment::commitment_arg_with_default, input_parsers::*, input_validators::*,
|
||||||
@ -24,7 +25,7 @@ use solana_client::{
|
|||||||
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
client_error::{ClientError, ClientErrorKind, Result as ClientResult},
|
||||||
rpc_client::RpcClient,
|
rpc_client::RpcClient,
|
||||||
rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig},
|
rpc_config::{RpcLargestAccountsFilter, RpcSendTransactionConfig},
|
||||||
rpc_response::{RpcAccount, RpcKeyedAccount},
|
rpc_response::RpcKeyedAccount,
|
||||||
};
|
};
|
||||||
#[cfg(not(test))]
|
#[cfg(not(test))]
|
||||||
use solana_faucet::faucet::request_airdrop_transaction;
|
use solana_faucet::faucet::request_airdrop_transaction;
|
||||||
@ -1225,7 +1226,7 @@ fn process_show_account(
|
|||||||
let cli_account = CliAccount {
|
let cli_account = CliAccount {
|
||||||
keyed_account: RpcKeyedAccount {
|
keyed_account: RpcKeyedAccount {
|
||||||
pubkey: account_pubkey.to_string(),
|
pubkey: account_pubkey.to_string(),
|
||||||
account: RpcAccount::encode(account),
|
account: RpcAccount::encode(account, AccountEncoding::Binary),
|
||||||
},
|
},
|
||||||
use_lamports_unit,
|
use_lamports_unit,
|
||||||
};
|
};
|
||||||
|
@ -111,9 +111,10 @@ mod tests {
|
|||||||
use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery};
|
use crate::{nonce::nonce_arg, offline::blockhash_query::BlockhashQuery};
|
||||||
use clap::App;
|
use clap::App;
|
||||||
use serde_json::{self, json, Value};
|
use serde_json::{self, json, Value};
|
||||||
|
use solana_account_decoder::{AccountEncoding, RpcAccount};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
rpc_request::RpcRequest,
|
rpc_request::RpcRequest,
|
||||||
rpc_response::{Response, RpcAccount, RpcFeeCalculator, RpcResponseContext},
|
rpc_response::{Response, RpcFeeCalculator, RpcResponseContext},
|
||||||
};
|
};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account, fee_calculator::FeeCalculator, hash::hash, nonce, system_program,
|
account::Account, fee_calculator::FeeCalculator, hash::hash, nonce, system_program,
|
||||||
@ -349,7 +350,7 @@ mod tests {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
|
let nonce_pubkey = Pubkey::new(&[4u8; 32]);
|
||||||
let rpc_nonce_account = RpcAccount::encode(nonce_account);
|
let rpc_nonce_account = RpcAccount::encode(nonce_account, AccountEncoding::Binary);
|
||||||
let get_account_response = json!(Response {
|
let get_account_response = json!(Response {
|
||||||
context: RpcResponseContext { slot: 1 },
|
context: RpcResponseContext { slot: 1 },
|
||||||
value: json!(Some(rpc_nonce_account)),
|
value: json!(Some(rpc_nonce_account)),
|
||||||
|
@ -19,9 +19,10 @@ reqwest = { version = "0.10.6", default-features = false, features = ["blocking"
|
|||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.54"
|
serde_json = "1.0.54"
|
||||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.0" }
|
solana-account-decoder = { path = "../account-decoder", version = "1.3.0" }
|
||||||
solana-net-utils = { path = "../net-utils", version = "1.3.0" }
|
solana-net-utils = { path = "../net-utils", version = "1.3.0" }
|
||||||
solana-sdk = { path = "../sdk", version = "1.3.0" }
|
solana-sdk = { path = "../sdk", version = "1.3.0" }
|
||||||
|
solana-transaction-status = { path = "../transaction-status", version = "1.3.0" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
|
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tungstenite = "0.10.1"
|
tungstenite = "0.10.1"
|
||||||
|
@ -11,6 +11,7 @@ use bincode::serialize;
|
|||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
use log::*;
|
use log::*;
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
use solana_account_decoder::RpcAccount;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
account::Account,
|
||||||
clock::{
|
clock::{
|
||||||
@ -442,7 +443,7 @@ impl RpcClient {
|
|||||||
value: rpc_account,
|
value: rpc_account,
|
||||||
} = serde_json::from_value::<Response<Option<RpcAccount>>>(result_json)?;
|
} = serde_json::from_value::<Response<Option<RpcAccount>>>(result_json)?;
|
||||||
trace!("Response account {:?} {:?}", pubkey, rpc_account);
|
trace!("Response account {:?} {:?}", pubkey, rpc_account);
|
||||||
let account = rpc_account.and_then(|rpc_account| rpc_account.decode().ok());
|
let account = rpc_account.and_then(|rpc_account| rpc_account.decode());
|
||||||
Ok(Response {
|
Ok(Response {
|
||||||
context,
|
context,
|
||||||
value: account,
|
value: account,
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
use solana_account_decoder::AccountEncoding;
|
||||||
use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
|
use solana_sdk::{clock::Epoch, commitment_config::CommitmentConfig};
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||||
@ -40,3 +41,11 @@ pub struct RpcInflationConfig {
|
|||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub commitment: Option<CommitmentConfig>,
|
pub commitment: Option<CommitmentConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub struct RpcAccountInfoConfig {
|
||||||
|
pub encoding: Option<AccountEncoding>,
|
||||||
|
#[serde(flatten)]
|
||||||
|
pub commitment: Option<CommitmentConfig>,
|
||||||
|
}
|
||||||
|
@ -1,13 +1,12 @@
|
|||||||
use crate::{client_error, rpc_request::RpcError};
|
use crate::client_error;
|
||||||
|
use solana_account_decoder::RpcAccount;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
account::Account,
|
|
||||||
clock::{Epoch, Slot},
|
clock::{Epoch, Slot},
|
||||||
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
fee_calculator::{FeeCalculator, FeeRateGovernor},
|
||||||
inflation::Inflation,
|
inflation::Inflation,
|
||||||
pubkey::Pubkey,
|
|
||||||
transaction::{Result, TransactionError},
|
transaction::{Result, TransactionError},
|
||||||
};
|
};
|
||||||
use std::{collections::HashMap, net::SocketAddr, str::FromStr};
|
use std::{collections::HashMap, net::SocketAddr};
|
||||||
|
|
||||||
pub type RpcResult<T> = client_error::Result<Response<T>>;
|
pub type RpcResult<T> = client_error::Result<Response<T>>;
|
||||||
|
|
||||||
@ -100,43 +99,6 @@ pub struct RpcSignatureResult {
|
|||||||
pub err: Option<TransactionError>,
|
pub err: Option<TransactionError>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A duplicate representation of a Message for pretty JSON serialization
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
pub struct RpcAccount {
|
|
||||||
pub lamports: u64,
|
|
||||||
pub data: String,
|
|
||||||
pub owner: String,
|
|
||||||
pub executable: bool,
|
|
||||||
pub rent_epoch: Epoch,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RpcAccount {
|
|
||||||
pub fn encode(account: Account) -> Self {
|
|
||||||
RpcAccount {
|
|
||||||
lamports: account.lamports,
|
|
||||||
data: bs58::encode(account.data.clone()).into_string(),
|
|
||||||
owner: account.owner.to_string(),
|
|
||||||
executable: account.executable,
|
|
||||||
rent_epoch: account.rent_epoch,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decode(&self) -> std::result::Result<Account, RpcError> {
|
|
||||||
Ok(Account {
|
|
||||||
lamports: self.lamports,
|
|
||||||
data: bs58::decode(self.data.clone()).into_vec().map_err(|_| {
|
|
||||||
RpcError::RpcRequestError("Could not parse encoded account data".to_string())
|
|
||||||
})?,
|
|
||||||
owner: Pubkey::from_str(&self.owner).map_err(|_| {
|
|
||||||
RpcError::RpcRequestError("Could not parse encoded account owner".to_string())
|
|
||||||
})?,
|
|
||||||
executable: self.executable,
|
|
||||||
rent_epoch: self.rent_epoch,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct RpcContactInfo {
|
pub struct RpcContactInfo {
|
||||||
/// Pubkey of the node as a base-58 string
|
/// Pubkey of the node as a base-58 string
|
||||||
|
@ -42,11 +42,11 @@ regex = "1.3.9"
|
|||||||
serde = "1.0.112"
|
serde = "1.0.112"
|
||||||
serde_derive = "1.0.103"
|
serde_derive = "1.0.103"
|
||||||
serde_json = "1.0.54"
|
serde_json = "1.0.54"
|
||||||
|
solana-account-decoder = { path = "../account-decoder", version = "1.3.0" }
|
||||||
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.3.0" }
|
solana-bpf-loader-program = { path = "../programs/bpf_loader", version = "1.3.0" }
|
||||||
solana-budget-program = { path = "../programs/budget", version = "1.3.0" }
|
solana-budget-program = { path = "../programs/budget", version = "1.3.0" }
|
||||||
solana-clap-utils = { path = "../clap-utils", version = "1.3.0" }
|
solana-clap-utils = { path = "../clap-utils", version = "1.3.0" }
|
||||||
solana-client = { path = "../client", version = "1.3.0" }
|
solana-client = { path = "../client", version = "1.3.0" }
|
||||||
solana-transaction-status = { path = "../transaction-status", version = "1.3.0" }
|
|
||||||
solana-faucet = { path = "../faucet", version = "1.3.0" }
|
solana-faucet = { path = "../faucet", version = "1.3.0" }
|
||||||
solana-genesis-programs = { path = "../genesis-programs", version = "1.3.0" }
|
solana-genesis-programs = { path = "../genesis-programs", version = "1.3.0" }
|
||||||
solana-ledger = { path = "../ledger", version = "1.3.0" }
|
solana-ledger = { path = "../ledger", version = "1.3.0" }
|
||||||
@ -60,10 +60,11 @@ solana-runtime = { path = "../runtime", version = "1.3.0" }
|
|||||||
solana-sdk = { path = "../sdk", version = "1.3.0" }
|
solana-sdk = { path = "../sdk", version = "1.3.0" }
|
||||||
solana-stake-program = { path = "../programs/stake", version = "1.3.0" }
|
solana-stake-program = { path = "../programs/stake", version = "1.3.0" }
|
||||||
solana-streamer = { path = "../streamer", version = "1.3.0" }
|
solana-streamer = { path = "../streamer", version = "1.3.0" }
|
||||||
|
solana-sys-tuner = { path = "../sys-tuner", version = "1.3.0" }
|
||||||
|
solana-transaction-status = { path = "../transaction-status", version = "1.3.0" }
|
||||||
solana-version = { path = "../version", version = "1.3.0" }
|
solana-version = { path = "../version", version = "1.3.0" }
|
||||||
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
|
solana-vote-program = { path = "../programs/vote", version = "1.3.0" }
|
||||||
solana-vote-signer = { path = "../vote-signer", version = "1.3.0" }
|
solana-vote-signer = { path = "../vote-signer", version = "1.3.0" }
|
||||||
solana-sys-tuner = { path = "../sys-tuner", version = "1.3.0" }
|
|
||||||
tempfile = "3.1.0"
|
tempfile = "3.1.0"
|
||||||
thiserror = "1.0"
|
thiserror = "1.0"
|
||||||
tokio = "0.1"
|
tokio = "0.1"
|
||||||
|
@ -8,6 +8,7 @@ use crate::{
|
|||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use jsonrpc_core::{Error, Metadata, Result};
|
use jsonrpc_core::{Error, Metadata, Result};
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
|
use solana_account_decoder::{AccountEncoding, RpcAccount};
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
rpc_config::*,
|
rpc_config::*,
|
||||||
rpc_request::{
|
rpc_request::{
|
||||||
@ -205,10 +206,16 @@ impl JsonRpcRequestProcessor {
|
|||||||
pub fn get_account_info(
|
pub fn get_account_info(
|
||||||
&self,
|
&self,
|
||||||
pubkey: &Pubkey,
|
pubkey: &Pubkey,
|
||||||
commitment: Option<CommitmentConfig>,
|
config: Option<RpcAccountInfoConfig>,
|
||||||
) -> Result<RpcResponse<Option<RpcAccount>>> {
|
) -> Result<RpcResponse<Option<RpcAccount>>> {
|
||||||
let bank = self.bank(commitment)?;
|
let config = config.unwrap_or_default();
|
||||||
new_response(&bank, bank.get_account(pubkey).map(RpcAccount::encode))
|
let bank = self.bank(config.commitment)?;
|
||||||
|
let encoding = config.encoding.unwrap_or(AccountEncoding::Binary);
|
||||||
|
new_response(
|
||||||
|
&bank,
|
||||||
|
bank.get_account(pubkey)
|
||||||
|
.map(|account| RpcAccount::encode(account, encoding)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_minimum_balance_for_rent_exemption(
|
pub fn get_minimum_balance_for_rent_exemption(
|
||||||
@ -224,15 +231,17 @@ impl JsonRpcRequestProcessor {
|
|||||||
pub fn get_program_accounts(
|
pub fn get_program_accounts(
|
||||||
&self,
|
&self,
|
||||||
program_id: &Pubkey,
|
program_id: &Pubkey,
|
||||||
commitment: Option<CommitmentConfig>,
|
config: Option<RpcAccountInfoConfig>,
|
||||||
) -> Result<Vec<RpcKeyedAccount>> {
|
) -> Result<Vec<RpcKeyedAccount>> {
|
||||||
Ok(self
|
let config = config.unwrap_or_default();
|
||||||
.bank(commitment)?
|
let bank = self.bank(config.commitment)?;
|
||||||
|
let encoding = config.encoding.unwrap_or(AccountEncoding::Binary);
|
||||||
|
Ok(bank
|
||||||
.get_program_accounts(Some(&program_id))
|
.get_program_accounts(Some(&program_id))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(pubkey, account)| RpcKeyedAccount {
|
.map(|(pubkey, account)| RpcKeyedAccount {
|
||||||
pubkey: pubkey.to_string(),
|
pubkey: pubkey.to_string(),
|
||||||
account: RpcAccount::encode(account),
|
account: RpcAccount::encode(account, encoding.clone()),
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
@ -823,7 +832,7 @@ pub trait RpcSol {
|
|||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
pubkey_str: String,
|
pubkey_str: String,
|
||||||
commitment: Option<CommitmentConfig>,
|
config: Option<RpcAccountInfoConfig>,
|
||||||
) -> Result<RpcResponse<Option<RpcAccount>>>;
|
) -> Result<RpcResponse<Option<RpcAccount>>>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getProgramAccounts")]
|
#[rpc(meta, name = "getProgramAccounts")]
|
||||||
@ -831,7 +840,7 @@ pub trait RpcSol {
|
|||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
program_id_str: String,
|
program_id_str: String,
|
||||||
commitment: Option<CommitmentConfig>,
|
config: Option<RpcAccountInfoConfig>,
|
||||||
) -> Result<Vec<RpcKeyedAccount>>;
|
) -> Result<Vec<RpcKeyedAccount>>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getMinimumBalanceForRentExemption")]
|
#[rpc(meta, name = "getMinimumBalanceForRentExemption")]
|
||||||
@ -1076,11 +1085,11 @@ impl RpcSol for RpcSolImpl {
|
|||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
pubkey_str: String,
|
pubkey_str: String,
|
||||||
commitment: Option<CommitmentConfig>,
|
config: Option<RpcAccountInfoConfig>,
|
||||||
) -> Result<RpcResponse<Option<RpcAccount>>> {
|
) -> Result<RpcResponse<Option<RpcAccount>>> {
|
||||||
debug!("get_account_info rpc request received: {:?}", pubkey_str);
|
debug!("get_account_info rpc request received: {:?}", pubkey_str);
|
||||||
let pubkey = verify_pubkey(pubkey_str)?;
|
let pubkey = verify_pubkey(pubkey_str)?;
|
||||||
meta.get_account_info(&pubkey, commitment)
|
meta.get_account_info(&pubkey, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_minimum_balance_for_rent_exemption(
|
fn get_minimum_balance_for_rent_exemption(
|
||||||
@ -1100,14 +1109,14 @@ impl RpcSol for RpcSolImpl {
|
|||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
program_id_str: String,
|
program_id_str: String,
|
||||||
commitment: Option<CommitmentConfig>,
|
config: Option<RpcAccountInfoConfig>,
|
||||||
) -> Result<Vec<RpcKeyedAccount>> {
|
) -> Result<Vec<RpcKeyedAccount>> {
|
||||||
debug!(
|
debug!(
|
||||||
"get_program_accounts rpc request received: {:?}",
|
"get_program_accounts rpc request received: {:?}",
|
||||||
program_id_str
|
program_id_str
|
||||||
);
|
);
|
||||||
let program_id = verify_pubkey(program_id_str)?;
|
let program_id = verify_pubkey(program_id_str)?;
|
||||||
meta.get_program_accounts(&program_id, commitment)
|
meta.get_program_accounts(&program_id, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_inflation_governor(
|
fn get_inflation_governor(
|
||||||
|
@ -4,9 +4,8 @@ use crate::rpc_subscriptions::{RpcSubscriptions, RpcVote, SlotInfo};
|
|||||||
use jsonrpc_core::{Error, ErrorCode, Result};
|
use jsonrpc_core::{Error, ErrorCode, Result};
|
||||||
use jsonrpc_derive::rpc;
|
use jsonrpc_derive::rpc;
|
||||||
use jsonrpc_pubsub::{typed::Subscriber, Session, SubscriptionId};
|
use jsonrpc_pubsub::{typed::Subscriber, Session, SubscriptionId};
|
||||||
use solana_client::rpc_response::{
|
use solana_account_decoder::RpcAccount;
|
||||||
Response as RpcResponse, RpcAccount, RpcKeyedAccount, RpcSignatureResult,
|
use solana_client::rpc_response::{Response as RpcResponse, RpcKeyedAccount, RpcSignatureResult};
|
||||||
};
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
use solana_runtime::bank_forks::BankForks;
|
use solana_runtime::bank_forks::BankForks;
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
|
@ -7,8 +7,9 @@ use jsonrpc_pubsub::{
|
|||||||
SubscriptionId,
|
SubscriptionId,
|
||||||
};
|
};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
|
use solana_account_decoder::{AccountEncoding, RpcAccount};
|
||||||
use solana_client::rpc_response::{
|
use solana_client::rpc_response::{
|
||||||
Response, RpcAccount, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult,
|
Response, RpcKeyedAccount, RpcResponseContext, RpcSignatureResult,
|
||||||
};
|
};
|
||||||
use solana_runtime::{bank::Bank, bank_forks::BankForks, commitment::BlockCommitmentCache};
|
use solana_runtime::{bank::Bank, bank_forks::BankForks, commitment::BlockCommitmentCache};
|
||||||
use solana_sdk::{
|
use solana_sdk::{
|
||||||
@ -230,7 +231,13 @@ fn filter_account_result(
|
|||||||
// If fork < last_notified_slot this means that we last notified for a fork
|
// If fork < last_notified_slot this means that we last notified for a fork
|
||||||
// and should notify that the account state has been reverted.
|
// and should notify that the account state has been reverted.
|
||||||
if fork != last_notified_slot {
|
if fork != last_notified_slot {
|
||||||
return (Box::new(iter::once(RpcAccount::encode(account))), fork);
|
return (
|
||||||
|
Box::new(iter::once(RpcAccount::encode(
|
||||||
|
account,
|
||||||
|
AccountEncoding::Binary,
|
||||||
|
))),
|
||||||
|
fork,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
(Box::new(iter::empty()), last_notified_slot)
|
(Box::new(iter::empty()), last_notified_slot)
|
||||||
@ -260,7 +267,7 @@ fn filter_program_results(
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(pubkey, account)| RpcKeyedAccount {
|
.map(|(pubkey, account)| RpcKeyedAccount {
|
||||||
pubkey: pubkey.to_string(),
|
pubkey: pubkey.to_string(),
|
||||||
account: RpcAccount::encode(account),
|
account: RpcAccount::encode(account, AccountEncoding::Binary),
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
last_notified_slot,
|
last_notified_slot,
|
||||||
|
@ -7,9 +7,10 @@ use jsonrpc_core_client::transports::ws;
|
|||||||
use log::*;
|
use log::*;
|
||||||
use reqwest::{self, header::CONTENT_TYPE};
|
use reqwest::{self, header::CONTENT_TYPE};
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
|
use solana_account_decoder::RpcAccount;
|
||||||
use solana_client::{
|
use solana_client::{
|
||||||
rpc_client::{get_rpc_request_str, RpcClient},
|
rpc_client::{get_rpc_request_str, RpcClient},
|
||||||
rpc_response::{Response, RpcAccount, RpcSignatureResult},
|
rpc_response::{Response, RpcSignatureResult},
|
||||||
};
|
};
|
||||||
use solana_core::contact_info::ContactInfo;
|
use solana_core::contact_info::ContactInfo;
|
||||||
use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, validator::TestValidator};
|
use solana_core::{rpc_pubsub::gen_client::Client as PubsubClient, validator::TestValidator};
|
||||||
|
Reference in New Issue
Block a user