Rework parsed account format (#11372)

* Rework parsed account format

* Serialize as type
This commit is contained in:
Tyera Eulberg
2020-08-05 00:59:10 -06:00
committed by GitHub
parent 9d4f9be1fe
commit 308186da79
6 changed files with 38 additions and 18 deletions

View File

@ -8,8 +8,7 @@ pub mod parse_nonce;
pub mod parse_token; pub mod parse_token;
pub mod parse_vote; pub mod parse_vote;
use crate::parse_account_data::parse_account_data; use crate::parse_account_data::{parse_account_data, ParsedAccount};
use serde_json::Value;
use solana_sdk::{account::Account, clock::Epoch, pubkey::Pubkey}; use solana_sdk::{account::Account, clock::Epoch, pubkey::Pubkey};
use std::str::FromStr; use std::str::FromStr;
@ -28,7 +27,7 @@ pub struct UiAccount {
#[serde(rename_all = "camelCase", untagged)] #[serde(rename_all = "camelCase", untagged)]
pub enum UiAccountData { pub enum UiAccountData {
Binary(String), Binary(String),
Json(Value), Json(ParsedAccount),
} }
impl From<Vec<u8>> for UiAccountData { impl From<Vec<u8>> for UiAccountData {

View File

@ -4,7 +4,7 @@ use crate::{
parse_vote::parse_vote, parse_vote::parse_vote,
}; };
use inflector::Inflector; use inflector::Inflector;
use serde_json::{json, Value}; use serde_json::Value;
use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program}; use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program};
use std::collections::HashMap; use std::collections::HashMap;
use thiserror::Error; use thiserror::Error;
@ -37,6 +37,13 @@ pub enum ParseAccountError {
SerdeJsonError(#[from] serde_json::error::Error), SerdeJsonError(#[from] serde_json::error::Error),
} }
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ParsedAccount {
pub program: String,
pub parsed: Value,
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase")]
pub enum ParsableAccount { pub enum ParsableAccount {
@ -45,7 +52,10 @@ pub enum ParsableAccount {
Vote, Vote,
} }
pub fn parse_account_data(program_id: &Pubkey, data: &[u8]) -> Result<Value, ParseAccountError> { pub fn parse_account_data(
program_id: &Pubkey,
data: &[u8],
) -> Result<ParsedAccount, ParseAccountError> {
let program_name = PARSABLE_PROGRAM_IDS let program_name = PARSABLE_PROGRAM_IDS
.get(program_id) .get(program_id)
.ok_or_else(|| ParseAccountError::ProgramNotParsable)?; .ok_or_else(|| ParseAccountError::ProgramNotParsable)?;
@ -54,9 +64,10 @@ pub fn parse_account_data(program_id: &Pubkey, data: &[u8]) -> Result<Value, Par
ParsableAccount::SplToken => serde_json::to_value(parse_token(data)?)?, ParsableAccount::SplToken => serde_json::to_value(parse_token(data)?)?,
ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?, ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?,
}; };
Ok(json!({ Ok(ParsedAccount {
format!("{:?}", program_name).to_kebab_case(): parsed_json program: format!("{:?}", program_name).to_kebab_case(),
})) parsed: parsed_json,
})
} }
#[cfg(test)] #[cfg(test)]
@ -79,11 +90,11 @@ mod test {
let versioned = VoteStateVersions::Current(Box::new(vote_state)); let versioned = VoteStateVersions::Current(Box::new(vote_state));
VoteState::serialize(&versioned, &mut vote_account_data).unwrap(); VoteState::serialize(&versioned, &mut vote_account_data).unwrap();
let parsed = parse_account_data(&solana_vote_program::id(), &vote_account_data).unwrap(); let parsed = parse_account_data(&solana_vote_program::id(), &vote_account_data).unwrap();
assert!(parsed.as_object().unwrap().contains_key("vote")); assert_eq!(parsed.program, "vote".to_string());
let nonce_data = Versions::new_current(State::Initialized(Data::default())); let nonce_data = Versions::new_current(State::Initialized(Data::default()));
let nonce_account_data = bincode::serialize(&nonce_data).unwrap(); let nonce_account_data = bincode::serialize(&nonce_data).unwrap();
let parsed = parse_account_data(&system_program::id(), &nonce_account_data).unwrap(); let parsed = parse_account_data(&system_program::id(), &nonce_account_data).unwrap();
assert!(parsed.as_object().unwrap().contains_key("nonce")); assert_eq!(parsed.program, "nonce".to_string());
} }
} }

View File

@ -21,7 +21,7 @@ pub fn parse_nonce(data: &[u8]) -> Result<UiNonceState, ParseAccountError> {
/// A duplicate representation of NonceState for pretty JSON serialization /// A duplicate representation of NonceState for pretty JSON serialization
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum UiNonceState { pub enum UiNonceState {
Uninitialized, Uninitialized,
Initialized(UiNonceData), Initialized(UiNonceData),

View File

@ -74,7 +74,7 @@ pub fn parse_token(data: &[u8]) -> Result<TokenAccountType, ParseAccountError> {
} }
#[derive(Debug, Serialize, Deserialize, PartialEq)] #[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")] #[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum TokenAccountType { pub enum TokenAccountType {
Account(UiTokenAccount), Account(UiTokenAccount),
Mint(UiMint), Mint(UiMint),

View File

@ -5,7 +5,7 @@ use solana_sdk::{
}; };
use solana_vote_program::vote_state::{BlockTimestamp, Lockout, VoteState}; use solana_vote_program::vote_state::{BlockTimestamp, Lockout, VoteState};
pub fn parse_vote(data: &[u8]) -> Result<UiVoteState, ParseAccountError> { pub fn parse_vote(data: &[u8]) -> Result<VoteAccountType, ParseAccountError> {
let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?; let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?;
let epoch_credits = vote_state let epoch_credits = vote_state
.epoch_credits() .epoch_credits()
@ -45,7 +45,7 @@ pub fn parse_vote(data: &[u8]) -> Result<UiVoteState, ParseAccountError> {
}, },
) )
.collect(); .collect();
Ok(UiVoteState { Ok(VoteAccountType::Vote(UiVoteState {
node_pubkey: vote_state.node_pubkey.to_string(), node_pubkey: vote_state.node_pubkey.to_string(),
authorized_withdrawer: vote_state.authorized_withdrawer.to_string(), authorized_withdrawer: vote_state.authorized_withdrawer.to_string(),
commission: vote_state.commission, commission: vote_state.commission,
@ -55,7 +55,14 @@ pub fn parse_vote(data: &[u8]) -> Result<UiVoteState, ParseAccountError> {
prior_voters, prior_voters,
epoch_credits, epoch_credits,
last_timestamp: vote_state.last_timestamp, last_timestamp: vote_state.last_timestamp,
}) }))
}
/// A wrapper enum for consistency across programs
#[derive(Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase", tag = "type", content = "info")]
pub enum VoteAccountType {
Vote(UiVoteState),
} }
/// A duplicate representation of VoteState for pretty JSON serialization /// A duplicate representation of VoteState for pretty JSON serialization
@ -126,7 +133,10 @@ mod test {
let mut expected_vote_state = UiVoteState::default(); let mut expected_vote_state = UiVoteState::default();
expected_vote_state.node_pubkey = Pubkey::default().to_string(); expected_vote_state.node_pubkey = Pubkey::default().to_string();
expected_vote_state.authorized_withdrawer = Pubkey::default().to_string(); expected_vote_state.authorized_withdrawer = Pubkey::default().to_string();
assert_eq!(parse_vote(&vote_account_data).unwrap(), expected_vote_state,); assert_eq!(
parse_vote(&vote_account_data).unwrap(),
VoteAccountType::Vote(expected_vote_state)
);
let bad_data = vec![0; 4]; let bad_data = vec![0; 4];
assert!(parse_vote(&bad_data).is_err()); assert!(parse_vote(&bad_data).is_err());

View File

@ -1079,7 +1079,7 @@ The result will be an RpcResponse JSON object with `value` equal to an array of
// Request // Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountsByDelegate", "params": ["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", {"programId": "TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"}, {"encoding": "jsonParsed"}]}' http://localhost:8899 curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountsByDelegate", "params": ["4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T", {"programId": "TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"}, {"encoding": "jsonParsed"}]}' http://localhost:8899
// Result // Result
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":[{"data":{"token":{"account":{"amount":1,"delegate":"4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T","delegatedAmount":1,"isInitialized":true,"isNative":false,"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E","owner":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}}},"executable":false,"lamports":1726080,"owner":"TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o","rentEpoch":4},"pubkey":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}],"id":1} {"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":[{"data":{"program":"spl-token","parsed":{"accountType":"account","info":{"amount":1,"delegate":"4Nd1mBQtrMJVYVfKf2PJy9NZUZdTAsp7D4xWLs4gDB4T","delegatedAmount":1,"isInitialized":true,"isNative":false,"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E","owner":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}}},"executable":false,"lamports":1726080,"owner":"TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o","rentEpoch":4},"pubkey":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}],"id":1}
``` ```
### getTokenAccountsByOwner ### getTokenAccountsByOwner
@ -1115,7 +1115,7 @@ The result will be an RpcResponse JSON object with `value` equal to an array of
// Request // Request
curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountsByOwner", "params": ["4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F", {"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E"}, {"encoding": "jsonParsed"}]}' http://localhost:8899 curl -X POST -H "Content-Type: application/json" -d '{"jsonrpc":"2.0", "id":1, "method":"getTokenAccountsByOwner", "params": ["4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F", {"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E"}, {"encoding": "jsonParsed"}]}' http://localhost:8899
// Result // Result
{"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":[{"data":{"token":{"account":{"amount":1,"delegate":null,"delegatedAmount":1,"isInitialized":true,"isNative":false,"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E","owner":"4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F"}}},"executable":false,"lamports":1726080,"owner":"TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o","rentEpoch":4},"pubkey":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}],"id":1} {"jsonrpc":"2.0","result":{"context":{"slot":1114},"value":[{"data":{"program":"spl-token","parsed":{"accountType":"account","info":{"amount":1,"delegate":null,"delegatedAmount":1,"isInitialized":true,"isNative":false,"mint":"3wyAj7Rt1TWVPZVteFJPLa26JmLvdb1CAKEFZm3NY75E","owner":"4Qkev8aNZcqFNSRhQzwyLMFSsi94jHqE8WNVTJzTP99F"}}},"executable":false,"lamports":1726080,"owner":"TokenSVp5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o","rentEpoch":4},"pubkey":"CnPoSPKXu7wJqxe59Fs72tkBeALovhsCxYeFwPCQH9TD"}],"id":1}
``` ```
### getTokenSupply ### getTokenSupply