diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 956cefe7da..06d4b6894e 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -543,6 +543,24 @@ impl RpcClient { ) } + pub fn get_confirmed_blocks_with_commitment( + &self, + start_slot: Slot, + end_slot: Option, + commitment_config: CommitmentConfig, + ) -> ClientResult> { + let json = if end_slot.is_some() { + json!([ + start_slot, + end_slot, + self.maybe_map_commitment(commitment_config)? + ]) + } else { + json!([start_slot, self.maybe_map_commitment(commitment_config)?]) + }; + self.send(RpcRequest::GetConfirmedBlocks, json) + } + pub fn get_confirmed_blocks_with_limit( &self, start_slot: Slot, @@ -554,6 +572,22 @@ impl RpcClient { ) } + pub fn get_confirmed_blocks_with_limit_and_commitment( + &self, + start_slot: Slot, + limit: usize, + commitment_config: CommitmentConfig, + ) -> ClientResult> { + self.send( + RpcRequest::GetConfirmedBlocksWithLimit, + json!([ + start_slot, + limit, + self.maybe_map_commitment(commitment_config)? + ]), + ) + } + pub fn get_confirmed_signatures_for_address( &self, address: &Pubkey, diff --git a/client/src/rpc_config.rs b/client/src/rpc_config.rs index a90603de13..91c50b622b 100644 --- a/client/src/rpc_config.rs +++ b/client/src/rpc_config.rs @@ -1,7 +1,7 @@ use crate::rpc_filter::RpcFilterType; use solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}; use solana_sdk::{ - clock::Epoch, + clock::{Epoch, Slot}, commitment_config::{CommitmentConfig, CommitmentLevel}, }; use solana_transaction_status::{TransactionDetails, UiTransactionEncoding}; @@ -173,3 +173,19 @@ impl EncodingConfig for RpcConfirmedTransactionConfig { } } } + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum RpcConfirmedBlocksConfigWrapper { + EndSlotOnly(Option), + CommitmentOnly(Option), +} + +impl RpcConfirmedBlocksConfigWrapper { + pub fn unzip(&self) -> (Option, Option) { + match &self { + RpcConfirmedBlocksConfigWrapper::EndSlotOnly(end_slot) => (*end_slot, None), + RpcConfirmedBlocksConfigWrapper::CommitmentOnly(commitment) => (None, *commitment), + } + } +} diff --git a/core/src/rpc.rs b/core/src/rpc.rs index e4fde021a5..d1d689d423 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -811,13 +811,24 @@ impl JsonRpcRequestProcessor { &self, start_slot: Slot, end_slot: Option, + commitment: Option, ) -> Result> { + let commitment = commitment.unwrap_or_default(); + check_is_at_least_confirmed(commitment)?; + + let highest_confirmed_root = self + .block_commitment_cache + .read() + .unwrap() + .highest_confirmed_root(); + let end_slot = min( - end_slot.unwrap_or(std::u64::MAX), - self.block_commitment_cache - .read() - .unwrap() - .highest_confirmed_root(), + end_slot.unwrap_or_else(|| start_slot.saturating_add(MAX_GET_CONFIRMED_BLOCKS_RANGE)), + if commitment.is_finalized() { + highest_confirmed_root + } else { + self.bank(Some(CommitmentConfig::confirmed())).slot() + }, ); if end_slot < start_slot { return Ok(vec![]); @@ -832,7 +843,8 @@ impl JsonRpcRequestProcessor { let lowest_blockstore_slot = self.blockstore.lowest_slot(); if start_slot < lowest_blockstore_slot { // If the starting slot is lower than what's available in blockstore assume the entire - // [start_slot..end_slot] can be fetched from BigTable. + // [start_slot..end_slot] can be fetched from BigTable. This range should not ever run + // into unfinalized confirmed blocks due to MAX_GET_CONFIRMED_BLOCKS_RANGE if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage { return self .runtime_handle @@ -853,19 +865,38 @@ impl JsonRpcRequestProcessor { } } - Ok(self + // Finalized blocks + let mut blocks: Vec<_> = self .blockstore .rooted_slot_iterator(max(start_slot, lowest_blockstore_slot)) .map_err(|_| Error::internal_error())? - .filter(|&slot| slot <= end_slot) - .collect()) + .filter(|&slot| slot <= end_slot && slot <= highest_confirmed_root) + .collect(); + let last_element = blocks.last().cloned().unwrap_or_default(); + + // Maybe add confirmed blocks + if commitment.is_confirmed() && last_element < end_slot { + let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); + let mut confirmed_blocks = confirmed_bank + .status_cache_ancestors() + .into_iter() + .filter(|&slot| slot <= end_slot && slot > last_element) + .collect(); + blocks.append(&mut confirmed_blocks); + } + + Ok(blocks) } pub fn get_confirmed_blocks_with_limit( &self, start_slot: Slot, limit: usize, + commitment: Option, ) -> Result> { + let commitment = commitment.unwrap_or_default(); + check_is_at_least_confirmed(commitment)?; + if limit > MAX_GET_CONFIRMED_BLOCKS_RANGE as usize { return Err(Error::invalid_params(format!( "Limit too large; max {}", @@ -877,7 +908,8 @@ impl JsonRpcRequestProcessor { if start_slot < lowest_blockstore_slot { // If the starting slot is lower than what's available in blockstore assume the entire - // range can be fetched from BigTable. + // range can be fetched from BigTable. This range should not ever run into unfinalized + // confirmed blocks due to MAX_GET_CONFIRMED_BLOCKS_RANGE if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage { return Ok(self .runtime_handle @@ -886,12 +918,35 @@ impl JsonRpcRequestProcessor { } } - Ok(self + let highest_confirmed_root = self + .block_commitment_cache + .read() + .unwrap() + .highest_confirmed_root(); + + // Finalized blocks + let mut blocks: Vec<_> = self .blockstore .rooted_slot_iterator(max(start_slot, lowest_blockstore_slot)) .map_err(|_| Error::internal_error())? .take(limit) - .collect()) + .filter(|&slot| slot <= highest_confirmed_root) + .collect(); + + // Maybe add confirmed blocks + if commitment.is_confirmed() && blocks.len() < limit { + let last_element = blocks.last().cloned().unwrap_or_default(); + let confirmed_bank = self.bank(Some(CommitmentConfig::confirmed())); + let mut confirmed_blocks = confirmed_bank + .status_cache_ancestors() + .into_iter() + .filter(|&slot| slot > last_element) + .collect(); + blocks.append(&mut confirmed_blocks); + blocks.truncate(limit); + } + + Ok(blocks) } pub fn get_block_time(&self, slot: Slot) -> Result> { @@ -2121,7 +2176,8 @@ pub trait RpcSol { &self, meta: Self::Metadata, start_slot: Slot, - end_slot: Option, + config: Option, + commitment: Option, ) -> Result>; #[rpc(meta, name = "getConfirmedBlocksWithLimit")] @@ -2130,6 +2186,7 @@ pub trait RpcSol { meta: Self::Metadata, start_slot: Slot, limit: usize, + commitment: Option, ) -> Result>; #[rpc(meta, name = "getConfirmedTransaction")] @@ -2920,13 +2977,15 @@ impl RpcSol for RpcSolImpl { &self, meta: Self::Metadata, start_slot: Slot, - end_slot: Option, + config: Option, + commitment: Option, ) -> Result> { + let (end_slot, maybe_commitment) = config.map(|config| config.unzip()).unwrap_or_default(); debug!( "get_confirmed_blocks rpc request received: {}-{:?}", start_slot, end_slot ); - meta.get_confirmed_blocks(start_slot, end_slot) + meta.get_confirmed_blocks(start_slot, end_slot, commitment.or(maybe_commitment)) } fn get_confirmed_blocks_with_limit( @@ -2934,12 +2993,13 @@ impl RpcSol for RpcSolImpl { meta: Self::Metadata, start_slot: Slot, limit: usize, + commitment: Option, ) -> Result> { debug!( "get_confirmed_blocks_with_limit rpc request received: {}-{}", start_slot, limit, ); - meta.get_confirmed_blocks_with_limit(start_slot, limit) + meta.get_confirmed_blocks_with_limit(start_slot, limit, commitment) } fn get_block_time(&self, meta: Self::Metadata, slot: Slot) -> Result> { diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index 841475d997..74d1078f6e 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -688,6 +688,7 @@ Returns a list of confirmed blocks between two slots - `` - start_slot, as u64 integer - `` - (optional) end_slot, as u64 integer +- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". #### Results: @@ -718,6 +719,7 @@ Returns a list of confirmed blocks starting at the given slot - `` - start_slot, as u64 integer - `` - limit, as u64 integer +- (optional) [Commitment](jsonrpc-api.md#configuring-state-commitment); "processed" is not supported. If parameter not provided, the default is "finalized". #### Results: