Rpc: enable getConfirmedBlocks and getConfirmedBlocksWithLimit to return confirmed (not yet finalized) data (#16161)
* 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
This commit is contained in:
@ -566,6 +566,24 @@ impl RpcClient {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_confirmed_blocks_with_commitment(
|
||||||
|
&self,
|
||||||
|
start_slot: Slot,
|
||||||
|
end_slot: Option<Slot>,
|
||||||
|
commitment_config: CommitmentConfig,
|
||||||
|
) -> ClientResult<Vec<Slot>> {
|
||||||
|
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(
|
pub fn get_confirmed_blocks_with_limit(
|
||||||
&self,
|
&self,
|
||||||
start_slot: Slot,
|
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<Vec<Slot>> {
|
||||||
|
self.send(
|
||||||
|
RpcRequest::GetConfirmedBlocksWithLimit,
|
||||||
|
json!([
|
||||||
|
start_slot,
|
||||||
|
limit,
|
||||||
|
self.maybe_map_commitment(commitment_config)?
|
||||||
|
]),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_confirmed_signatures_for_address(
|
pub fn get_confirmed_signatures_for_address(
|
||||||
&self,
|
&self,
|
||||||
address: &Pubkey,
|
address: &Pubkey,
|
||||||
|
@ -2,7 +2,7 @@ use {
|
|||||||
crate::rpc_filter::RpcFilterType,
|
crate::rpc_filter::RpcFilterType,
|
||||||
solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
|
solana_account_decoder::{UiAccountEncoding, UiDataSliceConfig},
|
||||||
solana_sdk::{
|
solana_sdk::{
|
||||||
clock::Epoch,
|
clock::{Epoch, Slot},
|
||||||
commitment_config::{CommitmentConfig, CommitmentLevel},
|
commitment_config::{CommitmentConfig, CommitmentLevel},
|
||||||
},
|
},
|
||||||
solana_transaction_status::{TransactionDetails, UiTransactionEncoding},
|
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<Slot>),
|
||||||
|
CommitmentOnly(Option<CommitmentConfig>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RpcConfirmedBlocksConfig {
|
||||||
|
pub fn unzip(&self) -> (Option<Slot>, Option<CommitmentConfig>) {
|
||||||
|
match &self {
|
||||||
|
RpcConfirmedBlocksConfig::EndSlotOnly(end_slot) => (*end_slot, None),
|
||||||
|
RpcConfirmedBlocksConfig::CommitmentOnly(commitment) => (None, *commitment),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -791,13 +791,24 @@ impl JsonRpcRequestProcessor {
|
|||||||
&self,
|
&self,
|
||||||
start_slot: Slot,
|
start_slot: Slot,
|
||||||
end_slot: Option<Slot>,
|
end_slot: Option<Slot>,
|
||||||
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> Result<Vec<Slot>> {
|
) -> Result<Vec<Slot>> {
|
||||||
|
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(
|
let end_slot = min(
|
||||||
end_slot.unwrap_or(std::u64::MAX),
|
end_slot.unwrap_or_else(|| start_slot.saturating_add(MAX_GET_CONFIRMED_BLOCKS_RANGE)),
|
||||||
self.block_commitment_cache
|
if commitment.is_finalized() {
|
||||||
.read()
|
highest_confirmed_root
|
||||||
.unwrap()
|
} else {
|
||||||
.highest_confirmed_root(),
|
self.bank(Some(CommitmentConfig::confirmed())).slot()
|
||||||
|
},
|
||||||
);
|
);
|
||||||
if end_slot < start_slot {
|
if end_slot < start_slot {
|
||||||
return Ok(vec![]);
|
return Ok(vec![]);
|
||||||
@ -812,7 +823,8 @@ impl JsonRpcRequestProcessor {
|
|||||||
let lowest_blockstore_slot = self.blockstore.lowest_slot();
|
let lowest_blockstore_slot = self.blockstore.lowest_slot();
|
||||||
if start_slot < lowest_blockstore_slot {
|
if start_slot < lowest_blockstore_slot {
|
||||||
// If the starting slot is lower than what's available in blockstore assume the entire
|
// 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 {
|
if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage {
|
||||||
return self
|
return self
|
||||||
.runtime
|
.runtime
|
||||||
@ -833,19 +845,38 @@ impl JsonRpcRequestProcessor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(self
|
// Finalized blocks
|
||||||
|
let mut blocks: Vec<_> = self
|
||||||
.blockstore
|
.blockstore
|
||||||
.rooted_slot_iterator(max(start_slot, lowest_blockstore_slot))
|
.rooted_slot_iterator(max(start_slot, lowest_blockstore_slot))
|
||||||
.map_err(|_| Error::internal_error())?
|
.map_err(|_| Error::internal_error())?
|
||||||
.filter(|&slot| slot <= end_slot)
|
.filter(|&slot| slot <= end_slot && slot <= highest_confirmed_root)
|
||||||
.collect())
|
.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(
|
pub fn get_confirmed_blocks_with_limit(
|
||||||
&self,
|
&self,
|
||||||
start_slot: Slot,
|
start_slot: Slot,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> Result<Vec<Slot>> {
|
) -> Result<Vec<Slot>> {
|
||||||
|
let commitment = commitment.unwrap_or_default();
|
||||||
|
check_is_at_least_confirmed(commitment)?;
|
||||||
|
|
||||||
if limit > MAX_GET_CONFIRMED_BLOCKS_RANGE as usize {
|
if limit > MAX_GET_CONFIRMED_BLOCKS_RANGE as usize {
|
||||||
return Err(Error::invalid_params(format!(
|
return Err(Error::invalid_params(format!(
|
||||||
"Limit too large; max {}",
|
"Limit too large; max {}",
|
||||||
@ -857,7 +888,8 @@ impl JsonRpcRequestProcessor {
|
|||||||
|
|
||||||
if start_slot < lowest_blockstore_slot {
|
if start_slot < lowest_blockstore_slot {
|
||||||
// If the starting slot is lower than what's available in blockstore assume the entire
|
// 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 {
|
if let Some(bigtable_ledger_storage) = &self.bigtable_ledger_storage {
|
||||||
return Ok(self
|
return Ok(self
|
||||||
.runtime
|
.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
|
.blockstore
|
||||||
.rooted_slot_iterator(max(start_slot, lowest_blockstore_slot))
|
.rooted_slot_iterator(max(start_slot, lowest_blockstore_slot))
|
||||||
.map_err(|_| Error::internal_error())?
|
.map_err(|_| Error::internal_error())?
|
||||||
.take(limit)
|
.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<Option<UnixTimestamp>> {
|
pub fn get_block_time(&self, slot: Slot) -> Result<Option<UnixTimestamp>> {
|
||||||
@ -2263,7 +2318,8 @@ pub mod rpc_full {
|
|||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
start_slot: Slot,
|
start_slot: Slot,
|
||||||
end_slot: Option<Slot>,
|
config: Option<RpcConfirmedBlocksConfig>,
|
||||||
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> Result<Vec<Slot>>;
|
) -> Result<Vec<Slot>>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getConfirmedBlocksWithLimit")]
|
#[rpc(meta, name = "getConfirmedBlocksWithLimit")]
|
||||||
@ -2272,6 +2328,7 @@ pub mod rpc_full {
|
|||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
start_slot: Slot,
|
start_slot: Slot,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> Result<Vec<Slot>>;
|
) -> Result<Vec<Slot>>;
|
||||||
|
|
||||||
#[rpc(meta, name = "getConfirmedTransaction")]
|
#[rpc(meta, name = "getConfirmedTransaction")]
|
||||||
@ -2919,13 +2976,16 @@ pub mod rpc_full {
|
|||||||
&self,
|
&self,
|
||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
start_slot: Slot,
|
start_slot: Slot,
|
||||||
end_slot: Option<Slot>,
|
config: Option<RpcConfirmedBlocksConfig>,
|
||||||
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> Result<Vec<Slot>> {
|
) -> Result<Vec<Slot>> {
|
||||||
|
let (end_slot, maybe_commitment) =
|
||||||
|
config.map(|config| config.unzip()).unwrap_or_default();
|
||||||
debug!(
|
debug!(
|
||||||
"get_confirmed_blocks rpc request received: {}-{:?}",
|
"get_confirmed_blocks rpc request received: {}-{:?}",
|
||||||
start_slot, end_slot
|
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(
|
fn get_confirmed_blocks_with_limit(
|
||||||
@ -2933,12 +2993,13 @@ pub mod rpc_full {
|
|||||||
meta: Self::Metadata,
|
meta: Self::Metadata,
|
||||||
start_slot: Slot,
|
start_slot: Slot,
|
||||||
limit: usize,
|
limit: usize,
|
||||||
|
commitment: Option<CommitmentConfig>,
|
||||||
) -> Result<Vec<Slot>> {
|
) -> Result<Vec<Slot>> {
|
||||||
debug!(
|
debug!(
|
||||||
"get_confirmed_blocks_with_limit rpc request received: {}-{}",
|
"get_confirmed_blocks_with_limit rpc request received: {}-{}",
|
||||||
start_slot, limit,
|
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(
|
fn get_block_time(
|
||||||
|
@ -687,6 +687,7 @@ Returns a list of confirmed blocks between two slots
|
|||||||
|
|
||||||
- `<u64>` - start_slot, as u64 integer
|
- `<u64>` - start_slot, as u64 integer
|
||||||
- `<u64>` - (optional) end_slot, as u64 integer
|
- `<u64>` - (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:
|
#### Results:
|
||||||
|
|
||||||
@ -717,6 +718,7 @@ Returns a list of confirmed blocks starting at the given slot
|
|||||||
|
|
||||||
- `<u64>` - start_slot, as u64 integer
|
- `<u64>` - start_slot, as u64 integer
|
||||||
- `<u64>` - limit, as u64 integer
|
- `<u64>` - 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:
|
#### Results:
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user