v0.2.4-pre.004
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
// file: crates/ksp-onchain-transport-lib/src/rpc_blocks.rs
|
||||
// version: 3
|
||||
// version: 4
|
||||
|
||||
/// Transaction detail level accepted by modern `getBlock` requests.
|
||||
#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)]
|
||||
@@ -570,7 +570,6 @@ impl SolanaPerformanceSample {
|
||||
}
|
||||
|
||||
/// Decodes one performance sample from its Solana JSON wire shape.
|
||||
#[cfg(test)]
|
||||
pub(crate) fn decode_wire(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<Self> {
|
||||
let decoded = crate::decode_wire_json::<WirePerformanceSample>(method, value);
|
||||
return match decoded {
|
||||
@@ -622,6 +621,93 @@ impl crate::HttpTransportPool {
|
||||
return self.get_blocks_u64("minimumLedgerSlot", role, std::vec::Vec::new()).await;
|
||||
}
|
||||
|
||||
/// Executes typed `getBlocks` while preserving all four supported parameter overloads.
|
||||
pub async fn get_blocks(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
start_slot: u64,
|
||||
end_slot: std::option::Option<u64>,
|
||||
config: std::option::Option<&crate::SolanaContextConfig>,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<u64>> {
|
||||
let validated = validate_blocks_context_commitment("getBlocks", config);
|
||||
if let std::result::Result::Err(error) = validated {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
if let std::option::Option::Some(end_slot) = end_slot
|
||||
&& end_slot >= start_slot
|
||||
&& end_slot - start_slot > MAX_GET_BLOCKS_RANGE
|
||||
{
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, "getBlocks range must not exceed 500000 slots")
|
||||
.with_context("rpc_method", "getBlocks")
|
||||
.with_context("start_slot", start_slot.to_string())
|
||||
.with_context("end_slot", end_slot.to_string()),
|
||||
);
|
||||
}
|
||||
let mut params = std::vec![serde_json::json!(start_slot)];
|
||||
if let std::option::Option::Some(end_slot) = end_slot {
|
||||
params.push(serde_json::json!(end_slot));
|
||||
}
|
||||
push_blocks_explicit_context_config(&mut params, config);
|
||||
return self.get_blocks_u64_list("getBlocks", role, params).await;
|
||||
}
|
||||
|
||||
/// Executes typed `getBlocksWithLimit` with the runtime-supported inclusive upper bound.
|
||||
pub async fn get_blocks_with_limit(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
start_slot: u64,
|
||||
limit: u64,
|
||||
config: std::option::Option<&crate::SolanaContextConfig>,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<u64>> {
|
||||
let validated = validate_blocks_context_commitment("getBlocksWithLimit", config);
|
||||
if let std::result::Result::Err(error) = validated {
|
||||
return std::result::Result::Err(error);
|
||||
}
|
||||
if limit > MAX_GET_BLOCKS_RANGE {
|
||||
return invalid_blocks_limit("getBlocksWithLimit", "getBlocksWithLimit limit must not exceed 500000", limit);
|
||||
}
|
||||
let mut params = std::vec![serde_json::json!(start_slot), serde_json::json!(limit)];
|
||||
push_blocks_explicit_context_config(&mut params, config);
|
||||
return self.get_blocks_u64_list("getBlocksWithLimit", role, params).await;
|
||||
}
|
||||
|
||||
/// Executes typed `getRecentPerformanceSamples`, preserving runtime order and older sample shapes.
|
||||
pub async fn get_recent_performance_samples(
|
||||
&self,
|
||||
role: &crate::HttpRoleName,
|
||||
limit: std::option::Option<u64>,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<crate::SolanaPerformanceSample>> {
|
||||
if let std::option::Option::Some(limit) = limit
|
||||
&& limit > MAX_GET_RECENT_PERFORMANCE_SAMPLES
|
||||
{
|
||||
return invalid_blocks_limit("getRecentPerformanceSamples", "getRecentPerformanceSamples limit must not exceed 720", limit);
|
||||
}
|
||||
let mut params = std::vec::Vec::new();
|
||||
if let std::option::Option::Some(limit) = limit {
|
||||
params.push(serde_json::json!(limit));
|
||||
}
|
||||
let value = self.execute_blocks_rpc("getRecentPerformanceSamples", role, params).await;
|
||||
let value = match value {
|
||||
std::result::Result::Ok(value) => value,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
return decode_performance_samples("getRecentPerformanceSamples", value);
|
||||
}
|
||||
|
||||
async fn get_blocks_u64_list(
|
||||
&self,
|
||||
method_name: &'static str,
|
||||
role: &crate::HttpRoleName,
|
||||
params: std::vec::Vec<serde_json::Value>,
|
||||
) -> ksp_core_lib::Result<std::vec::Vec<u64>> {
|
||||
let value = self.execute_blocks_rpc(method_name, role, params).await;
|
||||
return match value {
|
||||
std::result::Result::Ok(value) => crate::decode_wire_json::<std::vec::Vec<u64>>(method_name, value),
|
||||
std::result::Result::Err(error) => std::result::Result::Err(error),
|
||||
};
|
||||
}
|
||||
|
||||
async fn get_blocks_u64(
|
||||
&self,
|
||||
method_name: &'static str,
|
||||
@@ -650,6 +736,57 @@ impl crate::HttpTransportPool {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_GET_BLOCKS_RANGE: u64 = 500_000;
|
||||
const MAX_GET_RECENT_PERFORMANCE_SAMPLES: u64 = 720;
|
||||
|
||||
fn validate_blocks_context_commitment(method: &'static str, config: std::option::Option<&crate::SolanaContextConfig>) -> ksp_core_lib::Result<()> {
|
||||
if let std::option::Option::Some(config) = config
|
||||
&& config.commitment() == std::option::Option::Some(crate::SolanaCommitment::Processed)
|
||||
{
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(
|
||||
crate::ERROR_CODE_INVALID_RPC_PARAMETERS,
|
||||
"block-range commitment must be confirmed or finalized when explicitly provided",
|
||||
)
|
||||
.with_context("rpc_method", method)
|
||||
.with_context("commitment", "processed"),
|
||||
);
|
||||
}
|
||||
return std::result::Result::Ok(());
|
||||
}
|
||||
|
||||
fn push_blocks_explicit_context_config(params: &mut std::vec::Vec<serde_json::Value>, config: std::option::Option<&crate::SolanaContextConfig>) {
|
||||
if let std::option::Option::Some(config) = config {
|
||||
params.push((*config).to_json_value());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
fn invalid_blocks_limit<T>(method: &'static str, message: &'static str, limit: u64) -> ksp_core_lib::Result<T> {
|
||||
return std::result::Result::Err(
|
||||
ksp_core_lib::Error::new(crate::ERROR_CODE_INVALID_RPC_PARAMETERS, message)
|
||||
.with_context("rpc_method", method)
|
||||
.with_context("limit", limit.to_string()),
|
||||
);
|
||||
}
|
||||
|
||||
fn decode_performance_samples(method: &str, value: serde_json::Value) -> ksp_core_lib::Result<std::vec::Vec<crate::SolanaPerformanceSample>> {
|
||||
let values = crate::decode_wire_json::<std::vec::Vec<serde_json::Value>>(method, value);
|
||||
let values = match values {
|
||||
std::result::Result::Ok(values) => values,
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
};
|
||||
let mut samples = std::vec::Vec::with_capacity(values.len());
|
||||
for value in values {
|
||||
let sample = crate::SolanaPerformanceSample::decode_wire(method, value);
|
||||
match sample {
|
||||
std::result::Result::Ok(sample) => samples.push(sample),
|
||||
std::result::Result::Err(error) => return std::result::Result::Err(error),
|
||||
}
|
||||
}
|
||||
return std::result::Result::Ok(samples);
|
||||
}
|
||||
|
||||
fn push_blocks_context_config(params: &mut std::vec::Vec<serde_json::Value>, config: std::option::Option<&crate::SolanaContextConfig>) {
|
||||
if let std::option::Option::Some(config) = config
|
||||
&& (config.commitment().is_some() || config.min_context_slot().is_some())
|
||||
@@ -802,7 +939,6 @@ struct WireConfirmedBlock {
|
||||
block_height: std::option::Option<u64>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[derive(serde::Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct WirePerformanceSample {
|
||||
|
||||
Reference in New Issue
Block a user