v0.3.8-pre.008

This commit is contained in:
2026-09-03 19:53:25 +02:00
parent 10515d1562
commit 489d686023
16 changed files with 1050 additions and 35 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-store-desk/src/store_runtime.rs
// version: 3
// version: 4
//! Composite-selected Store readiness, status and bounded shutdown lifecycle owned by Store Desk.
@@ -35,6 +35,24 @@ impl crate::StoreStartup {
};
}
/// Executes one bounded server-side RAW account-state inspection query.
pub(crate) async fn query_accounts(&self, request: crate::StoreAccountQueryRequestDto) -> ksp_core_lib::Result<crate::StoreAccountQueryResponseDto> {
let runtime = self.runtime.as_ref();
return match runtime {
std::option::Option::Some(value) => value.query_accounts(request).await,
std::option::Option::None => std::result::Result::Err(store_runtime_unavailable()),
};
}
/// Loads one explicit bounded RAW account-state detail projection.
pub(crate) async fn account_detail(&self, request: crate::StoreAccountDetailRequestDto) -> ksp_core_lib::Result<crate::StoreAccountDetailDto> {
let runtime = self.runtime.as_ref();
return match runtime {
std::option::Option::Some(value) => value.account_detail(request).await,
std::option::Option::None => std::result::Result::Err(store_runtime_unavailable()),
};
}
/// Executes one bounded server-side RAW transaction inspection query.
pub(crate) async fn query_transactions(
&self,
@@ -116,6 +134,56 @@ impl crate::StoreRuntime {
};
}
/// Executes one validated random-access account-state inspection query while retaining the Store read guard.
pub(crate) async fn query_accounts(&self, request: crate::StoreAccountQueryRequestDto) -> ksp_core_lib::Result<crate::StoreAccountQueryResponseDto> {
let locked = self.store.read().await;
let store = locked.as_ref();
let store = match store {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(store_runtime_unavailable()),
};
let query = account_query_from_request(store, &request);
let query = match query {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let page = ksp_store_lib::RawAccountStateInspectionRead::inspect_raw_account_states(store, &query).await;
let page = match page {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let records_total_decimal = page.total_items().to_string();
let records_filtered_decimal = page.filtered_items().to_string();
let items = page.into_items();
let mut rows = std::vec::Vec::with_capacity(items.len());
for item in items {
rows.push(account_row_dto(&item));
}
return std::result::Result::Ok(crate::StoreAccountQueryResponseDto { records_filtered_decimal, records_total_decimal, rows });
}
/// Loads one account-state detail by application-owned row identity without exposing Store network selection to the frontend.
pub(crate) async fn account_detail(&self, request: crate::StoreAccountDetailRequestDto) -> ksp_core_lib::Result<crate::StoreAccountDetailDto> {
let locked = self.store.read().await;
let store = locked.as_ref();
let store = match store {
std::option::Option::Some(value) => value,
std::option::Option::None => return std::result::Result::Err(store_runtime_unavailable()),
};
let reference = account_reference_from_request(store, &request);
let reference = match reference {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let state = ksp_store_lib::RawAccountStateRead::get_raw_account_state(store, &reference).await;
let state = match state {
std::result::Result::Ok(std::option::Option::Some(value)) => value,
std::result::Result::Ok(std::option::Option::None) => return std::result::Result::Err(store_account_not_found()),
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
return std::result::Result::Ok(account_detail_dto(&state));
}
/// Executes one validated random-access transaction inspection query while retaining the Store read guard.
pub(crate) async fn query_transactions(
&self,
@@ -210,8 +278,111 @@ impl crate::StoreRuntime {
}
}
const ACCOUNT_DETAIL_PREVIEW_BYTES: usize = 512;
const TRANSACTION_DETAIL_PREVIEW_BYTES: usize = 512;
fn account_query_from_request(
store: &ksp_store_lib::Store,
request: &crate::StoreAccountQueryRequestDto,
) -> ksp_core_lib::Result<ksp_store_lib::RawAccountStateInspectionQuery> {
let limit = match request.limit {
25 | 50 | 100 => ksp_store_lib::RawPageLimit::new(u64::from(request.limit)),
_ => return std::result::Result::Err(store_query_invalid()),
};
let limit = match limit {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(store_query_invalid()),
};
let offset = request.offset.parse::<u64>();
let offset = match offset {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(store_query_invalid()),
};
let pubkey = parse_optional_pubkey(request.pubkey.as_deref());
let pubkey = match pubkey {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let slot_min = parse_optional_decimal_u64(request.slot_min.as_deref());
let slot_min = match slot_min {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let slot_max = parse_optional_decimal_u64(request.slot_max.as_deref());
let slot_max = match slot_max {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let slots = ksp_store_lib::RawSlotRange::new(slot_min, slot_max);
let slots = match slots {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(store_query_invalid()),
};
let direction = match request.direction.as_str() {
"ascending" => ksp_store_lib::RawSortDirection::Ascending,
"descending" => ksp_store_lib::RawSortDirection::Descending,
_ => return std::result::Result::Err(store_query_invalid()),
};
let network = store.runtime_snapshot().network().clone();
let page = ksp_store_lib::RawInspectionPageRequest::new(offset, limit);
return std::result::Result::Ok(ksp_store_lib::RawAccountStateInspectionQuery::new(network, pubkey, slots, direction, page));
}
fn account_reference_from_request(
store: &ksp_store_lib::Store,
request: &crate::StoreAccountDetailRequestDto,
) -> ksp_core_lib::Result<ksp_store_lib::RawAccountStateReference> {
let pubkey = request.pubkey.parse::<ksp_store_lib::Pubkey>();
let pubkey = match pubkey {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(store_query_invalid()),
};
let slot = request.slot.parse::<u64>();
let slot = match slot {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(store_query_invalid()),
};
let state_hash = decode_hex_32(request.state_hash.as_str());
let state_hash = match state_hash {
std::result::Result::Ok(value) => ksp_store_lib::RawContentHash::new(value),
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let network = store.runtime_snapshot().network().clone();
return std::result::Result::Ok(ksp_store_lib::RawAccountStateReference::new(network, pubkey, slot, state_hash));
}
fn account_detail_dto(state: &ksp_store_lib::RawAccountState) -> crate::StoreAccountDetailDto {
let data = state.data();
let preview_len = std::cmp::min(data.len(), ACCOUNT_DETAIL_PREVIEW_BYTES);
let state_hash = state.reference().state_hash();
return crate::StoreAccountDetailDto {
data_length_decimal: state.data_len().to_string(),
data_preview_hex: encode_hex(&data[..preview_len]),
data_preview_truncated: data.len() > preview_len,
executable: state.executable(),
lamports_decimal: state.lamports().to_string(),
owner: state.owner().to_string(),
pubkey: state.reference().pubkey().to_string(),
rent_epoch_decimal: state.rent_epoch().to_string(),
slot_decimal: state.reference().slot().to_string(),
state_hash: encode_hex(state_hash.as_bytes()),
};
}
fn account_row_dto(summary: &ksp_store_lib::RawAccountStateSummary) -> crate::StoreAccountRowDto {
let state_hash = summary.reference().state_hash();
return crate::StoreAccountRowDto {
data_length_decimal: summary.data_length_bytes().to_string(),
executable: summary.executable(),
lamports_decimal: summary.lamports().to_string(),
owner: summary.owner().to_string(),
pubkey: summary.reference().pubkey().to_string(),
rent_epoch_decimal: summary.rent_epoch().to_string(),
slot_decimal: summary.reference().slot().to_string(),
state_hash: encode_hex(state_hash.as_bytes()),
};
}
fn transaction_query_from_request(
store: &ksp_store_lib::Store,
request: &crate::StoreTransactionQueryRequestDto,
@@ -350,6 +521,16 @@ fn retention_state_code(state: ksp_store_lib::RawRetentionState) -> &'static str
};
}
fn parse_optional_pubkey(value: std::option::Option<&str>) -> ksp_core_lib::Result<std::option::Option<ksp_store_lib::Pubkey>> {
return match value {
std::option::Option::Some(text) => match text.parse::<ksp_store_lib::Pubkey>() {
std::result::Result::Ok(parsed) => std::result::Result::Ok(std::option::Option::Some(parsed)),
std::result::Result::Err(_) => std::result::Result::Err(store_query_invalid()),
},
std::option::Option::None => std::result::Result::Ok(std::option::Option::None),
};
}
fn parse_optional_decimal_u64(value: std::option::Option<&str>) -> ksp_core_lib::Result<std::option::Option<u64>> {
return match value {
std::option::Option::Some(text) => match text.parse::<u64>() {
@@ -360,6 +541,24 @@ fn parse_optional_decimal_u64(value: std::option::Option<&str>) -> ksp_core_lib:
};
}
fn decode_hex_32(value: &str) -> ksp_core_lib::Result<[u8; 32]> {
if value.len() != 64 || !value.is_ascii() {
return std::result::Result::Err(store_query_invalid());
}
let source = value.as_bytes();
let mut decoded = [0_u8; 32];
for index in 0..32 {
let high = decode_hex_nibble(source[index * 2]);
let low = decode_hex_nibble(source[index * 2 + 1]);
let (high, low) = match (high, low) {
(std::option::Option::Some(high), std::option::Option::Some(low)) => (high, low),
_ => return std::result::Result::Err(store_query_invalid()),
};
decoded[index] = (high << 4) | low;
}
return std::result::Result::Ok(decoded);
}
fn decode_hex_64(value: &str) -> ksp_core_lib::Result<[u8; 64]> {
if value.len() != 128 || !value.is_ascii() {
return std::result::Result::Err(store_query_invalid());
@@ -398,8 +597,12 @@ fn encode_hex(bytes: &[u8]) -> String {
return encoded;
}
fn store_account_not_found() -> ksp_core_lib::Error {
return ksp_core_lib::Error::new(crate::ERROR_CODE_STORE_ACCOUNT_NOT_FOUND, "Store Desk account state was not found");
}
fn store_query_invalid() -> ksp_core_lib::Error {
return ksp_core_lib::Error::new(crate::ERROR_CODE_STORE_QUERY_INVALID, "Store Desk transaction query is invalid");
return ksp_core_lib::Error::new(crate::ERROR_CODE_STORE_QUERY_INVALID, "Store Desk RAW inspection/detail request is invalid");
}
fn store_runtime_unavailable() -> ksp_core_lib::Error {