From 068d23f2988cf08828964372a38f281edaa921e0 Mon Sep 17 00:00:00 2001 From: sakridge Date: Sat, 8 Aug 2020 22:40:13 -0700 Subject: [PATCH] Add Binary64 option for account data (#11474) * Add Binary64 option for account data * Decode into binary64 * Reword docs --- Cargo.lock | 1 + account-decoder/Cargo.toml | 1 + account-decoder/src/lib.rs | 4 ++++ cli/src/offline/blockhash_query.rs | 2 +- client/src/rpc_client.rs | 20 ++++++++++++++++-- core/src/rpc.rs | 34 ++++++++++++++++++------------ core/tests/rpc.rs | 13 ++++++++++++ docs/src/apps/jsonrpc-api.md | 2 +- 8 files changed, 59 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7a561f5f2a..d53d445b01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3204,6 +3204,7 @@ name = "solana-account-decoder" version = "1.4.0" dependencies = [ "Inflector", + "base64 0.12.3", "bincode", "bs58", "lazy_static", diff --git a/account-decoder/Cargo.toml b/account-decoder/Cargo.toml index 789967529f..84852695db 100644 --- a/account-decoder/Cargo.toml +++ b/account-decoder/Cargo.toml @@ -9,6 +9,7 @@ license = "Apache-2.0" edition = "2018" [dependencies] +base64 = "0.12.3" bincode = "1.3.1" bs58 = "0.3.1" Inflector = "0.11.4" diff --git a/account-decoder/src/lib.rs b/account-decoder/src/lib.rs index 69ab1383a1..d9d6a10340 100644 --- a/account-decoder/src/lib.rs +++ b/account-decoder/src/lib.rs @@ -30,6 +30,7 @@ pub struct UiAccount { pub enum UiAccountData { Binary(String), Json(ParsedAccount), + Binary64(String), } impl From> for UiAccountData { @@ -43,6 +44,7 @@ impl From> for UiAccountData { pub enum UiAccountEncoding { Binary, JsonParsed, + Binary64, } impl UiAccount { @@ -53,6 +55,7 @@ impl UiAccount { ) -> Self { let data = match encoding { UiAccountEncoding::Binary => account.data.into(), + UiAccountEncoding::Binary64 => UiAccountData::Binary64(base64::encode(account.data)), UiAccountEncoding::JsonParsed => { if let Ok(parsed_data) = parse_account_data(&account.owner, &account.data, additional_data) @@ -76,6 +79,7 @@ impl UiAccount { let data = match &self.data { UiAccountData::Json(_) => None, UiAccountData::Binary(blob) => bs58::decode(blob).into_vec().ok(), + UiAccountData::Binary64(blob) => base64::decode(blob).ok(), }?; Some(Account { lamports: self.lamports, diff --git a/cli/src/offline/blockhash_query.rs b/cli/src/offline/blockhash_query.rs index fa8ceb999a..d2da79e091 100644 --- a/cli/src/offline/blockhash_query.rs +++ b/cli/src/offline/blockhash_query.rs @@ -350,7 +350,7 @@ mod tests { ) .unwrap(); let nonce_pubkey = Pubkey::new(&[4u8; 32]); - let rpc_nonce_account = UiAccount::encode(nonce_account, UiAccountEncoding::Binary, None); + let rpc_nonce_account = UiAccount::encode(nonce_account, UiAccountEncoding::Binary64, None); let get_account_response = json!(Response { context: RpcResponseContext { slot: 1 }, value: json!(Some(rpc_nonce_account)), diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index d5e6e4e188..2806f6dc1c 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -2,6 +2,7 @@ use crate::{ client_error::{ClientError, ClientErrorKind, Result as ClientResult}, http_sender::HttpSender, mock_sender::{MockSender, Mocks}, + rpc_config::RpcAccountInfoConfig, rpc_config::{ RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig, RpcSendTransactionConfig, RpcTokenAccountsFilter, @@ -20,6 +21,8 @@ use solana_account_decoder::{ UiTokenAmount, }, UiAccount, + UiAccountData::{Binary, Binary64}, + UiAccountEncoding, }; use solana_sdk::{ account::Account, @@ -466,9 +469,13 @@ impl RpcClient { pubkey: &Pubkey, commitment_config: CommitmentConfig, ) -> RpcResult> { + let config = RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Binary64), + commitment: Some(commitment_config), + }; let response = self.sender.send( RpcRequest::GetAccountInfo, - json!([pubkey.to_string(), commitment_config]), + json!([pubkey.to_string(), config]), ); response @@ -480,8 +487,17 @@ impl RpcClient { } let Response { context, - value: rpc_account, + value: mut rpc_account, } = serde_json::from_value::>>(result_json)?; + if let Some(ref mut account) = rpc_account { + if let Binary(_) = &account.data { + let tmp = Binary64(String::new()); + match std::mem::replace(&mut account.data, tmp) { + Binary(new_data) => account.data = Binary64(new_data), + _ => panic!("should have gotten binary here."), + } + } + } trace!("Response account {:?} {:?}", pubkey, rpc_account); let account = rpc_account.and_then(|rpc_account| rpc_account.decode()); Ok(Response { diff --git a/core/src/rpc.rs b/core/src/rpc.rs index 7b0f43016f..15621220ed 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -6,7 +6,7 @@ use crate::{ rpc_health::*, validator::ValidatorExit, }; use bincode::{config::Options, serialize}; -use jsonrpc_core::{Error, Metadata, Result}; +use jsonrpc_core::{types::error, Error, Metadata, Result}; use jsonrpc_derive::rpc; use solana_account_decoder::{ parse_account_data::AccountAdditionalData, @@ -240,21 +240,27 @@ impl JsonRpcRequestProcessor { &self, pubkey: &Pubkey, config: Option, - ) -> RpcResponse> { + ) -> Result>> { let config = config.unwrap_or_default(); let bank = self.bank(config.commitment); let encoding = config.encoding.unwrap_or(UiAccountEncoding::Binary); - new_response( - &bank, - bank.get_account(pubkey).and_then(|account| { - if account.owner == spl_token_id_v1_0() && encoding == UiAccountEncoding::JsonParsed - { - get_parsed_token_account(bank.clone(), account) - } else { - Some(UiAccount::encode(account, encoding, None)) - } - }), - ) + let mut response = None; + if let Some(account) = bank.get_account(pubkey) { + if account.owner == spl_token_id_v1_0() && encoding == UiAccountEncoding::JsonParsed { + response = get_parsed_token_account(bank.clone(), account); + } else if encoding == UiAccountEncoding::Binary && account.data.len() > 128 { + let message = "Encoded binary (base 58) data should be less than 128 bytes, please use Binary64 encoding.".to_string(); + return Err(error::Error { + code: error::ErrorCode::InvalidRequest, + message, + data: None, + }); + } else { + response = Some(UiAccount::encode(account, encoding, None)); + } + } + + Ok(new_response(&bank, response)) } pub fn get_minimum_balance_for_rent_exemption( @@ -1701,7 +1707,7 @@ impl RpcSol for RpcSolImpl { ) -> Result>> { debug!("get_account_info rpc request received: {:?}", pubkey_str); let pubkey = verify_pubkey(pubkey_str)?; - Ok(meta.get_account_info(&pubkey, config)) + meta.get_account_info(&pubkey, config) } fn get_minimum_balance_for_rent_exemption( diff --git a/core/tests/rpc.rs b/core/tests/rpc.rs index 9d11f1e16b..fa59d3b7fe 100644 --- a/core/tests/rpc.rs +++ b/core/tests/rpc.rs @@ -100,6 +100,19 @@ fn test_rpc_send_tx() { assert_eq!(confirmed_tx, true); + use solana_account_decoder::UiAccountEncoding; + use solana_client::rpc_config::RpcAccountInfoConfig; + let config = RpcAccountInfoConfig { + encoding: Some(UiAccountEncoding::Binary64), + commitment: None, + }; + let req = json_req!( + "getAccountInfo", + json!([bs58::encode(bob_pubkey).into_string(), config]) + ); + let json: Value = post_rpc(req, &leader_data); + info!("{:?}", json["result"]["value"]); + server.close().unwrap(); remove_dir_all(ledger_path).unwrap(); } diff --git a/docs/src/apps/jsonrpc-api.md b/docs/src/apps/jsonrpc-api.md index 249c98e2f2..8c330d7c0a 100644 --- a/docs/src/apps/jsonrpc-api.md +++ b/docs/src/apps/jsonrpc-api.md @@ -156,7 +156,7 @@ Returns all information associated with the account of provided Pubkey - `` - Pubkey of account to query, as base-58 encoded string - `` - (optional) Configuration object containing the following optional fields: - (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment) - - (optional) `encoding: ` - encoding for Account data, either "binary" or jsonParsed". If parameter not provided, the default encoding is binary. + - (optional) `encoding: ` - encoding for Account data, either "binary", "binary64", or jsonParsed". If parameter not provided, the default encoding is "binary". "binary" is base-58 encoded and limited to Account data of less than 128 bytes. "binary64" will return base64 encoded data for Account data of any size. Parsed-JSON encoding attempts to use program-specific state parsers to return more human-readable and explicit account state data. If parsed-JSON is requested but a parser cannot be found, the field falls back to binary encoding, detectable when the `data` field is type ``. **jsonParsed encoding is UNSTABLE** #### Results: