From 5c9f85f28da17a4532d488d27811e8eecb001d85 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 29 Mar 2021 19:53:17 +0000 Subject: [PATCH] Rpc: enable getConfirmedBlocks and getConfirmedBlocksWithLimit to return confirmed (not yet finalized) data (#16161) (#16198) * Add commitment config capabilities * Use rpc limit if no end_slot provided * Limit to actually finalized blocks * Support confirmed blocks in getConfirmedBlocks and getConfirmedBlocksWithLimit * Update docs * Add client plumbing * Rename config enum (cherry picked from commit 60ed8e28925601c75721849992490581f80eb2b1) Co-authored-by: Tyera Eulberg --- client/src/rpc_client.rs | 34 ++++++++ client/src/rpc_config.rs | 18 ++++- core/src/rpc.rs | 93 ++++++++++++++++++---- docs/src/developing/clients/jsonrpc-api.md | 2 + 4 files changed, 130 insertions(+), 17 deletions(-) diff --git a/client/src/rpc_client.rs b/client/src/rpc_client.rs index 23416d3137..a7f3aa4442 100644 --- a/client/src/rpc_client.rs +++ b/client/src/rpc_client.rs @@ -566,6 +566,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, @@ -577,6 +595,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 b336b1a848..63129fa595 100644 --- a/client/src/rpc_config.rs +++ b/client/src/rpc_config.rs @@ -2,7 +2,7 @@ use { crate::rpc_filter::RpcFilterType, solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig}, solana_sdk::{ - clock::Epoch, + clock::{Epoch, Slot}, commitment_config::{CommitmentConfig, CommitmentLevel}, }, solana_transaction_status::{TransactionDetails, UiTransactionEncoding}, @@ -175,3 +175,19 @@ impl EncodingConfig for RpcConfirmedTransactionConfig { } } } + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(untagged)] +pub enum RpcConfirmedBlocksConfig { + EndSlotOnly(Option), + CommitmentOnly(Option), +} + +impl RpcConfirmedBlocksConfig { + pub fn unzip(&self) -> (Option, Option) { + match &self { + RpcConfirmedBlocksConfig::EndSlotOnly(end_slot) => (*end_slot, None), + RpcConfirmedBlocksConfig::CommitmentOnly(commitment) => (None, *commitment), + } + } +} diff --git a/core/src/rpc.rs b/core/src/rpc.rs index b8e6a17248..5b69980e58 100644 --- a/core/src/rpc.rs +++ b/core/src/rpc.rs @@ -791,13 +791,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![]); @@ -812,7 +823,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 @@ -833,19 +845,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 {}", @@ -857,7 +888,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 @@ -866,12 +898,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> { @@ -2263,7 +2318,8 @@ pub mod rpc_full { &self, meta: Self::Metadata, start_slot: Slot, - end_slot: Option, + config: Option, + commitment: Option, ) -> Result>; #[rpc(meta, name = "getConfirmedBlocksWithLimit")] @@ -2272,6 +2328,7 @@ pub mod rpc_full { meta: Self::Metadata, start_slot: Slot, limit: usize, + commitment: Option, ) -> Result>; #[rpc(meta, name = "getConfirmedTransaction")] @@ -2919,13 +2976,16 @@ pub mod rpc_full { &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( @@ -2933,12 +2993,13 @@ pub mod rpc_full { 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( diff --git a/docs/src/developing/clients/jsonrpc-api.md b/docs/src/developing/clients/jsonrpc-api.md index b0b01d993b..f4b594cff1 100644 --- a/docs/src/developing/clients/jsonrpc-api.md +++ b/docs/src/developing/clients/jsonrpc-api.md @@ -687,6 +687,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: @@ -717,6 +718,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: