From 308186da794f0b23f53258f632986e9edc5e3280 Mon Sep 17 00:00:00 2001 From: Tyera Eulberg Date: Wed, 5 Aug 2020 00:59:10 -0600 Subject: [PATCH] Rework parsed account format (#11372) * Rework parsed account format * Serialize as type --- account-decoder/src/lib.rs | 5 ++--- account-decoder/src/parse_account_data.rs | 25 ++++++++++++++++------- account-decoder/src/parse_nonce.rs | 2 +- account-decoder/src/parse_token.rs | 2 +- account-decoder/src/parse_vote.rs | 18 ++++++++++++---- docs/src/apps/jsonrpc-api.md | 4 ++-- 6 files changed, 38 insertions(+), 18 deletions(-) diff --git a/account-decoder/src/lib.rs b/account-decoder/src/lib.rs index 45afe8adf..8b7db31f4 100644 --- a/account-decoder/src/lib.rs +++ b/account-decoder/src/lib.rs @@ -8,8 +8,7 @@ pub mod parse_nonce; pub mod parse_token; pub mod parse_vote; -use crate::parse_account_data::parse_account_data; -use serde_json::Value; +use crate::parse_account_data::{parse_account_data, ParsedAccount}; use solana_sdk::{account::Account, clock::Epoch, pubkey::Pubkey}; use std::str::FromStr; @@ -28,7 +27,7 @@ pub struct UiAccount { #[serde(rename_all = "camelCase", untagged)] pub enum UiAccountData { Binary(String), - Json(Value), + Json(ParsedAccount), } impl From> for UiAccountData { diff --git a/account-decoder/src/parse_account_data.rs b/account-decoder/src/parse_account_data.rs index e10743cad..213ec5523 100644 --- a/account-decoder/src/parse_account_data.rs +++ b/account-decoder/src/parse_account_data.rs @@ -4,7 +4,7 @@ use crate::{ parse_vote::parse_vote, }; use inflector::Inflector; -use serde_json::{json, Value}; +use serde_json::Value; use solana_sdk::{instruction::InstructionError, pubkey::Pubkey, system_program}; use std::collections::HashMap; use thiserror::Error; @@ -37,6 +37,13 @@ pub enum ParseAccountError { 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)] #[serde(rename_all = "camelCase")] pub enum ParsableAccount { @@ -45,7 +52,10 @@ pub enum ParsableAccount { Vote, } -pub fn parse_account_data(program_id: &Pubkey, data: &[u8]) -> Result { +pub fn parse_account_data( + program_id: &Pubkey, + data: &[u8], +) -> Result { let program_name = PARSABLE_PROGRAM_IDS .get(program_id) .ok_or_else(|| ParseAccountError::ProgramNotParsable)?; @@ -54,9 +64,10 @@ pub fn parse_account_data(program_id: &Pubkey, data: &[u8]) -> Result serde_json::to_value(parse_token(data)?)?, ParsableAccount::Vote => serde_json::to_value(parse_vote(data)?)?, }; - Ok(json!({ - format!("{:?}", program_name).to_kebab_case(): parsed_json - })) + Ok(ParsedAccount { + program: format!("{:?}", program_name).to_kebab_case(), + parsed: parsed_json, + }) } #[cfg(test)] @@ -79,11 +90,11 @@ mod test { let versioned = VoteStateVersions::Current(Box::new(vote_state)); VoteState::serialize(&versioned, &mut 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_account_data = bincode::serialize(&nonce_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()); } } diff --git a/account-decoder/src/parse_nonce.rs b/account-decoder/src/parse_nonce.rs index 5f82d0dba..6693350d4 100644 --- a/account-decoder/src/parse_nonce.rs +++ b/account-decoder/src/parse_nonce.rs @@ -21,7 +21,7 @@ pub fn parse_nonce(data: &[u8]) -> Result { /// A duplicate representation of NonceState for pretty JSON serialization #[derive(Debug, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", tag = "type", content = "info")] pub enum UiNonceState { Uninitialized, Initialized(UiNonceData), diff --git a/account-decoder/src/parse_token.rs b/account-decoder/src/parse_token.rs index 6a1637c22..3c6617a1e 100644 --- a/account-decoder/src/parse_token.rs +++ b/account-decoder/src/parse_token.rs @@ -74,7 +74,7 @@ pub fn parse_token(data: &[u8]) -> Result { } #[derive(Debug, Serialize, Deserialize, PartialEq)] -#[serde(rename_all = "camelCase")] +#[serde(rename_all = "camelCase", tag = "type", content = "info")] pub enum TokenAccountType { Account(UiTokenAccount), Mint(UiMint), diff --git a/account-decoder/src/parse_vote.rs b/account-decoder/src/parse_vote.rs index 051de6719..70cd691af 100644 --- a/account-decoder/src/parse_vote.rs +++ b/account-decoder/src/parse_vote.rs @@ -5,7 +5,7 @@ use solana_sdk::{ }; use solana_vote_program::vote_state::{BlockTimestamp, Lockout, VoteState}; -pub fn parse_vote(data: &[u8]) -> Result { +pub fn parse_vote(data: &[u8]) -> Result { let mut vote_state = VoteState::deserialize(data).map_err(ParseAccountError::from)?; let epoch_credits = vote_state .epoch_credits() @@ -45,7 +45,7 @@ pub fn parse_vote(data: &[u8]) -> Result { }, ) .collect(); - Ok(UiVoteState { + Ok(VoteAccountType::Vote(UiVoteState { node_pubkey: vote_state.node_pubkey.to_string(), authorized_withdrawer: vote_state.authorized_withdrawer.to_string(), commission: vote_state.commission, @@ -55,7 +55,14 @@ pub fn parse_vote(data: &[u8]) -> Result { prior_voters, epoch_credits, 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 @@ -126,7 +133,10 @@ mod test { let mut expected_vote_state = UiVoteState::default(); expected_vote_state.node_pubkey = 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]; assert!(parse_vote(&bad_data).is_err()); diff --git a/docs/src/apps/jsonrpc-api.md b/docs/src/apps/jsonrpc-api.md index 1bd046fc8..15af0725b 100644 --- a/docs/src/apps/jsonrpc-api.md +++ b/docs/src/apps/jsonrpc-api.md @@ -1079,7 +1079,7 @@ The result will be an RpcResponse JSON object with `value` equal to an array of // 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 // 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 @@ -1115,7 +1115,7 @@ The result will be an RpcResponse JSON object with `value` equal to an array of // 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 // 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