Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

rpc: Add config options limiting getConfirmedBlock response data #15970

Merged
merged 6 commits into from
Mar 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions cli/src/stake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ use solana_client::{
client_error::{ClientError, ClientErrorKind},
nonce_utils,
rpc_client::RpcClient,
rpc_config::RpcConfirmedBlockConfig,
rpc_custom_error,
rpc_request::{self, DELINQUENT_VALIDATOR_SLOT_DISTANCE},
};
Expand Down Expand Up @@ -1674,9 +1675,9 @@ pub(crate) fn fetch_epoch_rewards(
.get(0)
.ok_or_else(|| format!("Unable to fetch first confirmed block for epoch {}", epoch))?;

let first_confirmed_block = match rpc_client.get_confirmed_block_with_encoding(
let first_confirmed_block = match rpc_client.get_configured_confirmed_block(
first_confirmed_block_in_epoch,
solana_transaction_status::UiTransactionEncoding::Base64,
RpcConfirmedBlockConfig::rewards_only(),
) {
Ok(first_confirmed_block) => first_confirmed_block,
Err(ClientError {
Expand All @@ -1702,7 +1703,7 @@ pub(crate) fn fetch_epoch_rewards(
};

// Rewards for the previous epoch are found in the first confirmed block of the current epoch
let previous_epoch_rewards = first_confirmed_block.rewards;
let previous_epoch_rewards = first_confirmed_block.rewards.unwrap_or_default();

if let Some((effective_slot, epoch_end_time, epoch_rewards)) = epoch_info {
let wallclock_epoch_duration = if epoch_end_time > epoch_start_time {
Expand Down
17 changes: 13 additions & 4 deletions client/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ use crate::{
mock_sender::{MockSender, Mocks},
rpc_config::RpcAccountInfoConfig,
rpc_config::{
RpcGetConfirmedSignaturesForAddress2Config, RpcLargestAccountsConfig,
RpcProgramAccountsConfig, RpcSendTransactionConfig, RpcSimulateTransactionConfig,
RpcTokenAccountsFilter,
RpcConfirmedBlockConfig, RpcGetConfirmedSignaturesForAddress2Config,
RpcLargestAccountsConfig, RpcProgramAccountsConfig, RpcSendTransactionConfig,
RpcSimulateTransactionConfig, RpcTokenAccountsFilter,
},
rpc_request::{RpcError, RpcRequest, RpcResponseErrorData, TokenAccountsFilter},
rpc_response::*,
Expand All @@ -33,7 +33,8 @@ use solana_sdk::{
transaction::{self, uses_durable_nonce, Transaction},
};
use solana_transaction_status::{
EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionStatus, UiTransactionEncoding,
EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionStatus, UiConfirmedBlock,
UiTransactionEncoding,
};
use solana_vote_program::vote_state::MAX_LOCKOUT_HISTORY;
use std::{
Expand Down Expand Up @@ -507,6 +508,14 @@ impl RpcClient {
self.send(RpcRequest::GetConfirmedBlock, json!([slot, encoding]))
}

pub fn get_configured_confirmed_block(
&self,
slot: Slot,
config: RpcConfirmedBlockConfig,
) -> ClientResult<UiConfirmedBlock> {
self.send(RpcRequest::GetConfirmedBlock, json!([slot, config]))
}

pub fn get_confirmed_blocks(
&self,
start_slot: Slot,
Expand Down
14 changes: 13 additions & 1 deletion client/src/rpc_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use solana_sdk::{
clock::Epoch,
commitment_config::{CommitmentConfig, CommitmentLevel},
};
use solana_transaction_status::UiTransactionEncoding;
use solana_transaction_status::{TransactionDetails, UiTransactionEncoding};

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
Expand Down Expand Up @@ -133,12 +133,24 @@ pub trait EncodingConfig {
#[serde(rename_all = "camelCase")]
pub struct RpcConfirmedBlockConfig {
pub encoding: Option<UiTransactionEncoding>,
pub transaction_details: Option<TransactionDetails>,
pub rewards: Option<bool>,
}

impl EncodingConfig for RpcConfirmedBlockConfig {
fn new_with_encoding(encoding: &Option<UiTransactionEncoding>) -> Self {
Self {
encoding: *encoding,
..Self::default()
}
}
}

impl RpcConfirmedBlockConfig {
pub fn rewards_only() -> Self {
Self {
transaction_details: Some(TransactionDetails::None),
..Self::default()
}
}
}
Expand Down
78 changes: 66 additions & 12 deletions core/src/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ use solana_sdk::{
};
use solana_stake_program::stake_state::StakeState;
use solana_transaction_status::{
EncodedConfirmedBlock, EncodedConfirmedTransaction, TransactionConfirmationStatus,
TransactionStatus, UiTransactionEncoding,
EncodedConfirmedTransaction, TransactionConfirmationStatus, TransactionStatus,
UiConfirmedBlock, UiTransactionEncoding,
};
use solana_vote_program::vote_state::{VoteState, MAX_LOCKOUT_HISTORY};
use spl_token_v2_0::{
Expand Down Expand Up @@ -720,11 +720,13 @@ impl JsonRpcRequestProcessor {
&self,
slot: Slot,
config: Option<RpcEncodingConfigWrapper<RpcConfirmedBlockConfig>>,
) -> Result<Option<EncodedConfirmedBlock>> {
) -> Result<Option<UiConfirmedBlock>> {
let config = config
.map(|config| config.convert_to_current())
.unwrap_or_default();
let encoding = config.encoding.unwrap_or(UiTransactionEncoding::Json);
let transaction_details = config.transaction_details.unwrap_or_default();
let show_rewards = config.rewards.unwrap_or(true);
if self.config.enable_rpc_transaction_history
&& slot
<= self
Expand All @@ -741,15 +743,15 @@ impl JsonRpcRequestProcessor {
.runtime
.block_on(bigtable_ledger_storage.get_confirmed_block(slot));
self.check_bigtable_result(&bigtable_result)?;
return Ok(bigtable_result
.ok()
.map(|confirmed_block| confirmed_block.encode(encoding)));
return Ok(bigtable_result.ok().map(|confirmed_block| {
confirmed_block.configure(encoding, transaction_details, show_rewards)
}));
}
}
self.check_slot_cleaned_up(&result, slot)?;
Ok(result
.ok()
.map(|confirmed_block| confirmed_block.encode(encoding)))
Ok(result.ok().map(|confirmed_block| {
confirmed_block.configure(encoding, transaction_details, show_rewards)
}))
} else {
Err(RpcCustomError::BlockNotAvailable { slot }.into())
}
Expand Down Expand Up @@ -2185,7 +2187,7 @@ pub mod rpc_full {
meta: Self::Metadata,
slot: Slot,
config: Option<RpcEncodingConfigWrapper<RpcConfirmedBlockConfig>>,
) -> Result<Option<EncodedConfirmedBlock>>;
) -> Result<Option<UiConfirmedBlock>>;

#[rpc(meta, name = "getBlockTime")]
fn get_block_time(&self, meta: Self::Metadata, slot: Slot)
Expand Down Expand Up @@ -2793,7 +2795,7 @@ pub mod rpc_full {
meta: Self::Metadata,
slot: Slot,
config: Option<RpcEncodingConfigWrapper<RpcConfirmedBlockConfig>>,
) -> Result<Option<EncodedConfirmedBlock>> {
) -> Result<Option<UiConfirmedBlock>> {
debug!("get_confirmed_block rpc request received: {:?}", slot);
meta.get_confirmed_block(slot, config)
}
Expand Down Expand Up @@ -3119,7 +3121,8 @@ pub mod tests {
transaction::{self, TransactionError},
};
use solana_transaction_status::{
EncodedTransaction, EncodedTransactionWithStatusMeta, UiMessage,
EncodedConfirmedBlock, EncodedTransaction, EncodedTransactionWithStatusMeta,
TransactionDetails, UiMessage,
};
use solana_vote_program::{
vote_instruction,
Expand Down Expand Up @@ -5077,6 +5080,7 @@ pub mod tests {
serde_json::from_value(result["result"].clone()).unwrap();
let confirmed_block = confirmed_block.unwrap();
assert_eq!(confirmed_block.transactions.len(), 3);
assert_eq!(confirmed_block.rewards, vec![]);

for EncodedTransactionWithStatusMeta { transaction, meta } in
confirmed_block.transactions.into_iter()
Expand Down Expand Up @@ -5121,6 +5125,7 @@ pub mod tests {
serde_json::from_value(result["result"].clone()).unwrap();
let confirmed_block = confirmed_block.unwrap();
assert_eq!(confirmed_block.transactions.len(), 3);
assert_eq!(confirmed_block.rewards, vec![]);

for EncodedTransactionWithStatusMeta { transaction, meta } in
confirmed_block.transactions.into_iter()
Expand Down Expand Up @@ -5156,6 +5161,55 @@ pub mod tests {
}
}

#[test]
fn test_get_confirmed_block_config() {
let bob_pubkey = solana_sdk::pubkey::new_rand();
let RpcHandler {
io,
meta,
confirmed_block_signatures,
..
} = start_rpc_handler_with_tx(&bob_pubkey);

let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlock","params":[0,{}]}}"#,
json!(RpcConfirmedBlockConfig {
encoding: None,
transaction_details: Some(TransactionDetails::Signatures),
rewards: Some(false),
})
);
let res = io.handle_request_sync(&req, meta.clone());
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
let confirmed_block: Option<UiConfirmedBlock> =
serde_json::from_value(result["result"].clone()).unwrap();
let confirmed_block = confirmed_block.unwrap();
assert!(confirmed_block.transactions.is_none());
assert!(confirmed_block.rewards.is_none());
for (i, signature) in confirmed_block.signatures.unwrap()[..2].iter().enumerate() {
assert_eq!(*signature, confirmed_block_signatures[i].to_string());
}

let req = format!(
r#"{{"jsonrpc":"2.0","id":1,"method":"getConfirmedBlock","params":[0,{}]}}"#,
json!(RpcConfirmedBlockConfig {
encoding: None,
transaction_details: Some(TransactionDetails::None),
rewards: Some(true),
})
);
let res = io.handle_request_sync(&req, meta);
let result: Value = serde_json::from_str(&res.expect("actual response"))
.expect("actual response deserialization");
let confirmed_block: Option<UiConfirmedBlock> =
serde_json::from_value(result["result"].clone()).unwrap();
let confirmed_block = confirmed_block.unwrap();
assert!(confirmed_block.transactions.is_none());
assert!(confirmed_block.signatures.is_none());
assert_eq!(confirmed_block.rewards.unwrap(), vec![]);
}

#[test]
fn test_get_confirmed_blocks() {
let bob_pubkey = solana_sdk::pubkey::new_rand();
Expand Down
10 changes: 6 additions & 4 deletions docs/src/developing/clients/jsonrpc-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,8 @@ Returns identity and transaction information about a confirmed block in the ledg
- `<object>` - (optional) Configuration object containing the following optional fields:
- (optional) `encoding: <string>` - encoding for each returned Transaction, either "json", "jsonParsed", "base58" (*slow*), "base64". If parameter not provided, the default encoding is "json".
"jsonParsed" encoding attempts to use program-specific instruction parsers to return more human-readable and explicit data in the `transaction.message.instructions` list. If "jsonParsed" is requested but a parser cannot be found, the instruction falls back to regular JSON encoding (`accounts`, `data`, and `programIdIndex` fields).
- (optional) `transactionDetails: <string>` - level of transaction detail to return, either "full", "signatures", or "none". If parameter not provided, the default detail level is "full".
- (optional) `rewards: bool` - whether to populate the `rewards` array. If parameter not provided, the default includes rewards.

#### Results:

Expand All @@ -470,7 +472,7 @@ The result field will be an object with the following fields:
- `blockhash: <string>` - the blockhash of this block, as base-58 encoded string
- `previousBlockhash: <string>` - the blockhash of this block's parent, as base-58 encoded string; if the parent block is not available due to ledger cleanup, this field will return "11111111111111111111111111111111"
- `parentSlot: <u64>` - the slot index of this block's parent
- `transactions: <array>` - an array of JSON objects containing:
- `transactions: <array>` - present if "full" transaction details are requested; an array of JSON objects containing:
- `transaction: <object|[string,encoding]>` - [Transaction](#transaction-structure) object, either in JSON format or encoded binary data, depending on encoding parameter
- `meta: <object>` - transaction status metadata object, containing `null` or:
- `err: <object | null>` - Error if transaction failed, null if transaction succeeded. [TransactionError definitions](https://github.com/solana-labs/solana/blob/master/sdk/src/transaction.rs#L24)
Expand All @@ -484,7 +486,8 @@ The result field will be an object with the following fields:
- DEPRECATED: `status: <object>` - Transaction status
- `"Ok": <null>` - Transaction was successful
- `"Err": <ERR>` - Transaction failed with TransactionError
- `rewards: <array>` - an array of JSON objects containing:
- `signatures: <array>` - present if "signatures" are requested for transaction details; an array of signatures strings, corresponding to the transaction order in the block
- `rewards: <array>` - present if rewards are requested; an array of JSON objects containing:
- `pubkey: <string>` - The public key, as base-58 encoded string, of the account that received the reward
- `lamports: <i64>`- number of reward lamports credited or debited by the account, as a i64
- `postBalance: <u64>` - account balance in lamports after the reward was applied
Expand All @@ -496,7 +499,7 @@ The result field will be an object with the following fields:
Request:
```bash
curl http://localhost:8899 -X POST -H "Content-Type: application/json" -d '
{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, {"encoding": "json"}]}
{"jsonrpc": "2.0","id":1,"method":"getConfirmedBlock","params":[430, {"encoding": "json","transactionDetails":"full","rewards":false}]}
'
```

Expand All @@ -509,7 +512,6 @@ Result:
"blockhash": "3Eq21vXNB5s86c62bVuUfTeaMif1N2kUqRPBmGRJhyTA",
"parentSlot": 429,
"previousBlockhash": "mfcyqEXB3DnHXki6KjjmZck6YjmZLvpAByy2fj4nh6B",
"rewards": [],
"transactions": [
{
"meta": {
Expand Down
85 changes: 85 additions & 0 deletions transaction-status/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,48 @@ impl ConfirmedBlock {
block_time: self.block_time,
}
}

pub fn configure(
self,
encoding: UiTransactionEncoding,
transaction_details: TransactionDetails,
show_rewards: bool,
) -> UiConfirmedBlock {
let (transactions, signatures) = match transaction_details {
TransactionDetails::Full => (
Some(
self.transactions
.into_iter()
.map(|tx| tx.encode(encoding))
.collect(),
),
None,
),
TransactionDetails::Signatures => (
None,
Some(
self.transactions
.into_iter()
.map(|tx| tx.transaction.signatures[0].to_string())
.collect(),
),
),
TransactionDetails::None => (None, None),
};
UiConfirmedBlock {
previous_blockhash: self.previous_blockhash,
blockhash: self.blockhash,
parent_slot: self.parent_slot,
transactions,
signatures,
rewards: if show_rewards {
Some(self.rewards)
} else {
None
},
block_time: self.block_time,
}
}
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
Expand All @@ -375,6 +417,49 @@ pub struct EncodedConfirmedBlock {
pub block_time: Option<UnixTimestamp>,
}

#[derive(Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct UiConfirmedBlock {
pub previous_blockhash: String,
pub blockhash: String,
pub parent_slot: Slot,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub transactions: Option<Vec<EncodedTransactionWithStatusMeta>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub signatures: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rewards: Option<Rewards>,
pub block_time: Option<UnixTimestamp>,
}

impl From<EncodedConfirmedBlock> for UiConfirmedBlock {
fn from(block: EncodedConfirmedBlock) -> Self {
Self {
previous_blockhash: block.previous_blockhash,
blockhash: block.blockhash,
parent_slot: block.parent_slot,
transactions: Some(block.transactions),
signatures: None,
rewards: Some(block.rewards),
block_time: block.block_time,
}
}
}

#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum TransactionDetails {
Full,
Signatures,
None,
}

impl Default for TransactionDetails {
fn default() -> Self {
Self::Full
}
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ConfirmedTransaction {
Expand Down