v0.3.8-pre.003

This commit is contained in:
2026-09-03 11:39:09 +02:00
parent 74890a6268
commit 9300dccbe2
22 changed files with 1109 additions and 40 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/capability/raw_account.rs
// version: 2
// version: 3
/// Read capability for complete canonical RAW account states.
///
@@ -24,6 +24,20 @@ pub trait RawAccountStateRead: std::marker::Send + std::marker::Sync {
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawPage<crate::RawAccountStateReference>>>;
}
/// Read capability for data-free random-access RAW account-state inspection.
///
/// This capability is intended for bounded operator/admin inspection. It must
/// return exact logical counts and summaries only; complete account bytes stay
/// behind [`RawAccountStateRead::get_raw_account_state`]. Worker, replay and
/// backfill traversal continue to use the opaque-cursor list operation.
pub trait RawAccountStateInspectionRead: std::marker::Send + std::marker::Sync {
/// Inspects one random-access account-state window with exact logical counts.
fn inspect_raw_account_states<'a>(
&'a self,
query: &'a crate::RawAccountStateInspectionQuery,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawInspectionPage<crate::RawAccountStateSummary>>>;
}
/// Write capability for complete RAW account-state acquisitions.
///
/// The account state and its acquisition observation form one logical

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/capability/raw_transaction.rs
// version: 2
// version: 3
/// Read capability for canonical RAW transactions.
///
@@ -24,6 +24,20 @@ pub trait RawTransactionRead: std::marker::Send + std::marker::Sync {
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawPage<crate::RawTransactionReference>>>;
}
/// Read capability for payload-free random-access RAW transaction inspection.
///
/// This capability is intended for bounded operator/admin inspection. It must
/// return exact logical counts and summaries only; canonical payload bytes stay
/// behind [`RawTransactionRead::get_raw_transaction`]. Worker, replay and
/// backfill traversal continue to use the opaque-cursor list operation.
pub trait RawTransactionInspectionRead: std::marker::Send + std::marker::Sync {
/// Inspects one random-access transaction window with exact logical counts.
fn inspect_raw_transactions<'a>(
&'a self,
query: &'a crate::RawTransactionInspectionQuery,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawInspectionPage<crate::RawTransactionSummary>>>;
}
/// Write capability for canonical RAW transaction acquisitions.
///
/// The transaction and its acquisition observation form one logical persistence

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/lib.rs
// version: 5
// version: 6
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -26,6 +26,8 @@ pub use self::capability::StoreApiFuture;
pub use self::capability::raw_account::RawAccountObservationRead;
/// Write capability for additional observations of already persisted RAW account states.
pub use self::capability::raw_account::RawAccountObservationWrite;
/// Read capability for data-free random-access RAW account-state inspection.
pub use self::capability::raw_account::RawAccountStateInspectionRead;
/// Read capability for complete canonical RAW account states.
pub use self::capability::raw_account::RawAccountStateRead;
/// Write capability for complete canonical RAW account-state acquisitions.
@@ -34,6 +36,8 @@ pub use self::capability::raw_account::RawAccountStateWrite;
pub use self::capability::raw_retention::RawTransactionRetentionRead;
/// Write capability for policy-authorized RAW transaction retention transitions.
pub use self::capability::raw_retention::RawTransactionRetentionWrite;
/// Read capability for payload-free random-access RAW transaction inspection.
pub use self::capability::raw_transaction::RawTransactionInspectionRead;
/// Read capability for persisted RAW transaction observations.
pub use self::capability::raw_transaction::RawTransactionObservationRead;
/// Write capability for additional observations of already persisted RAW transactions.
@@ -60,6 +64,18 @@ pub use self::model::raw_account::RawAccountObservation;
pub use self::model::raw_account::RawAccountState;
/// Durable backend-independent identity of one canonical RAW account state.
pub use self::model::raw_account::RawAccountStateReference;
/// Backend-independent random-access inspection query for RAW account states.
pub use self::model::raw_inspection::RawAccountStateInspectionQuery;
/// Data-free summary of one canonical RAW account state for operator inspection.
pub use self::model::raw_inspection::RawAccountStateSummary;
/// One bounded random-access inspection page with exact logical counts.
pub use self::model::raw_inspection::RawInspectionPage;
/// Random-access page request dedicated to bounded interactive RAW inspection.
pub use self::model::raw_inspection::RawInspectionPageRequest;
/// Backend-independent random-access inspection query for RAW transactions.
pub use self::model::raw_inspection::RawTransactionInspectionQuery;
/// Payload-free summary of one canonical RAW transaction for operator inspection.
pub use self::model::raw_inspection::RawTransactionSummary;
/// Combined outcome of one atomic canonical RAW entity plus observation acquisition.
pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;
/// Outcome for one canonical RAW entity in an idempotent persistence operation.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/model.rs
// version: 4
// version: 5
//! Private home for persistent Store models.
//!
@@ -9,6 +9,7 @@
//! one or more models.
pub(crate) mod raw_account;
pub(crate) mod raw_inspection;
pub(crate) mod raw_outcome;
pub(crate) mod raw_pagination;
pub(crate) mod raw_primitives;

View File

@@ -0,0 +1,366 @@
// file: crates/ksp-store-api/src/model/raw_inspection.rs
// version: 1
/// Random-access page request dedicated to bounded interactive RAW inspection.
///
/// This request is intentionally distinct from [`crate::RawPageRequest`]. It
/// provides offset/limit semantics for operator-facing inspection while the
/// canonical RAW list APIs keep their opaque backend cursors for replay,
/// backfill and worker traversal.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RawInspectionPageRequest {
limit: crate::RawPageLimit,
offset: u64,
}
impl RawInspectionPageRequest {
/// Creates one inspection request from an absolute zero-based offset and positive page limit.
#[must_use]
pub const fn new(offset: u64, limit: crate::RawPageLimit) -> Self {
return Self { limit, offset };
}
/// Returns the exact caller-requested page size.
#[must_use]
pub const fn limit(&self) -> crate::RawPageLimit {
return self.limit;
}
/// Returns the absolute zero-based item offset within the filtered result set.
#[must_use]
pub const fn offset(&self) -> u64 {
return self.offset;
}
}
/// One bounded random-access inspection page with exact logical counts.
///
/// `total_items` is the exact count inside the mandatory query scope before
/// optional filters. `filtered_items` is the exact count after optional query
/// filters. The item vector contains only the requested page window.
#[derive(Debug)]
pub struct RawInspectionPage<T> {
filtered_items: u64,
items: std::vec::Vec<T>,
total_items: u64,
}
impl<T> RawInspectionPage<T> {
/// Creates one inspection page after validating count consistency.
pub fn try_new(items: std::vec::Vec<T>, total_items: u64, filtered_items: u64) -> crate::Result<Self> {
if filtered_items > total_items {
return std::result::Result::Err(raw_model_error("filtered_items"));
}
let item_count = u64::try_from(items.len());
let item_count = match item_count {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("items")),
};
if item_count > filtered_items {
return std::result::Result::Err(raw_model_error("items"));
}
return std::result::Result::Ok(Self { filtered_items, items, total_items });
}
/// Returns the exact count after optional query filters.
#[must_use]
pub const fn filtered_items(&self) -> u64 {
return self.filtered_items;
}
/// Returns the current inspection page items.
#[must_use]
pub fn items(&self) -> &[T] {
return self.items.as_slice();
}
/// Consumes the page and returns its bounded inspection items.
#[must_use]
pub fn into_items(self) -> std::vec::Vec<T> {
return self.items;
}
/// Returns the exact count before optional query filters inside the mandatory scope.
#[must_use]
pub const fn total_items(&self) -> u64 {
return self.total_items;
}
}
/// Backend-independent random-access inspection query for RAW transactions.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionInspectionQuery {
direction: crate::RawSortDirection,
network: crate::RawNetworkId,
page: crate::RawInspectionPageRequest,
slots: crate::RawSlotRange,
}
impl RawTransactionInspectionQuery {
/// Creates one transaction inspection query.
#[must_use]
pub fn new(network: crate::RawNetworkId, slots: crate::RawSlotRange, direction: crate::RawSortDirection, page: crate::RawInspectionPageRequest) -> Self {
return Self { direction, network, page, slots };
}
/// Returns the requested deterministic traversal direction.
#[must_use]
pub const fn direction(&self) -> crate::RawSortDirection {
return self.direction;
}
/// Returns the mandatory logical network scope.
#[must_use]
pub fn network(&self) -> &crate::RawNetworkId {
return &self.network;
}
/// Returns the random-access inspection page request.
#[must_use]
pub const fn page(&self) -> crate::RawInspectionPageRequest {
return self.page;
}
/// Returns optional inclusive slot filters.
#[must_use]
pub const fn slots(&self) -> crate::RawSlotRange {
return self.slots;
}
}
/// Backend-independent random-access inspection query for RAW account states.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawAccountStateInspectionQuery {
direction: crate::RawSortDirection,
network: crate::RawNetworkId,
page: crate::RawInspectionPageRequest,
pubkey: std::option::Option<crate::Pubkey>,
slots: crate::RawSlotRange,
}
impl RawAccountStateInspectionQuery {
/// Creates one account-state inspection query.
#[must_use]
pub fn new(
network: crate::RawNetworkId,
pubkey: std::option::Option<crate::Pubkey>,
slots: crate::RawSlotRange,
direction: crate::RawSortDirection,
page: crate::RawInspectionPageRequest,
) -> Self {
return Self { direction, network, page, pubkey, slots };
}
/// Returns the requested deterministic traversal direction.
#[must_use]
pub const fn direction(&self) -> crate::RawSortDirection {
return self.direction;
}
/// Returns the mandatory logical network scope.
#[must_use]
pub fn network(&self) -> &crate::RawNetworkId {
return &self.network;
}
/// Returns the random-access inspection page request.
#[must_use]
pub const fn page(&self) -> crate::RawInspectionPageRequest {
return self.page;
}
/// Returns an optional exact account-address filter.
#[must_use]
pub fn pubkey(&self) -> std::option::Option<&crate::Pubkey> {
return self.pubkey.as_ref();
}
/// Returns optional inclusive slot filters.
#[must_use]
pub const fn slots(&self) -> crate::RawSlotRange {
return self.slots;
}
}
/// Payload-free summary of one canonical RAW transaction for operator inspection.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionSummary {
block_time: std::option::Option<crate::RawTimestamp>,
content_hash: crate::RawContentHash,
format_id: crate::RawFormatId,
format_version: u32,
payload_size_bytes: std::option::Option<u64>,
reference: crate::RawTransactionReference,
retention_state: crate::RawRetentionState,
slot: u64,
}
impl RawTransactionSummary {
/// Creates one payload-free transaction summary after validating summary invariants.
pub fn try_new(
reference: crate::RawTransactionReference,
slot: u64,
block_time: std::option::Option<crate::RawTimestamp>,
format_id: crate::RawFormatId,
format_version: u32,
content_hash: crate::RawContentHash,
payload_size_bytes: std::option::Option<u64>,
retention_state: crate::RawRetentionState,
) -> crate::Result<Self> {
if format_version == 0 {
return std::result::Result::Err(raw_model_error("format_version"));
}
if let std::option::Option::Some(size) = payload_size_bytes {
let size = usize::try_from(size);
let size = match size {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("payload_size_bytes")),
};
if size == 0 || size > crate::MAX_RAW_PAYLOAD_BYTES {
return std::result::Result::Err(raw_model_error("payload_size_bytes"));
}
}
if retention_state == crate::RawRetentionState::Purged {
if payload_size_bytes.is_some() {
return std::result::Result::Err(raw_model_error("payload_size_bytes"));
}
} else if payload_size_bytes.is_none() {
return std::result::Result::Err(raw_model_error("payload_size_bytes"));
}
return std::result::Result::Ok(Self {
block_time,
content_hash,
format_id,
format_version,
payload_size_bytes,
reference,
retention_state,
slot,
});
}
/// Returns the optional canonical block timestamp.
#[must_use]
pub const fn block_time(&self) -> std::option::Option<crate::RawTimestamp> {
return self.block_time;
}
/// Returns the deterministic canonical content digest.
#[must_use]
pub const fn content_hash(&self) -> crate::RawContentHash {
return self.content_hash;
}
/// Returns the KSP-owned canonical RAW format identifier.
#[must_use]
pub fn format_id(&self) -> &crate::RawFormatId {
return &self.format_id;
}
/// Returns the KSP-owned canonical RAW format version.
#[must_use]
pub const fn format_version(&self) -> u32 {
return self.format_version;
}
/// Returns the canonical payload size when payload content is logically retained.
#[must_use]
pub const fn payload_size_bytes(&self) -> std::option::Option<u64> {
return self.payload_size_bytes;
}
/// Returns the durable backend-independent transaction identity.
#[must_use]
pub fn reference(&self) -> &crate::RawTransactionReference {
return &self.reference;
}
/// Returns the logical RAW transaction retention state.
#[must_use]
pub const fn retention_state(&self) -> crate::RawRetentionState {
return self.retention_state;
}
/// Returns the Solana slot containing the transaction.
#[must_use]
pub const fn slot(&self) -> u64 {
return self.slot;
}
}
/// Data-free summary of one canonical RAW account state for operator inspection.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawAccountStateSummary {
data_length_bytes: u64,
executable: bool,
lamports: u64,
owner: crate::Pubkey,
reference: crate::RawAccountStateReference,
rent_epoch: u64,
}
impl RawAccountStateSummary {
/// Creates one data-free account-state summary after validating its bounded data length.
pub fn try_new(
reference: crate::RawAccountStateReference,
lamports: u64,
owner: crate::Pubkey,
executable: bool,
rent_epoch: u64,
data_length_bytes: u64,
) -> crate::Result<Self> {
let data_length = usize::try_from(data_length_bytes);
let data_length = match data_length {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::result::Result::Err(raw_model_error("data_length_bytes")),
};
if data_length > crate::MAX_RAW_ACCOUNT_DATA_BYTES {
return std::result::Result::Err(raw_model_error("data_length_bytes"));
}
return std::result::Result::Ok(Self { data_length_bytes, executable, lamports, owner, reference, rent_epoch });
}
/// Returns the complete canonical account-data length without exposing account bytes.
#[must_use]
pub const fn data_length_bytes(&self) -> u64 {
return self.data_length_bytes;
}
/// Returns whether the account is executable.
#[must_use]
pub const fn executable(&self) -> bool {
return self.executable;
}
/// Returns the account lamport balance.
#[must_use]
pub const fn lamports(&self) -> u64 {
return self.lamports;
}
/// Returns the account owner program public key.
#[must_use]
pub const fn owner(&self) -> &crate::Pubkey {
return &self.owner;
}
/// Returns the durable backend-independent account-state identity.
#[must_use]
pub fn reference(&self) -> &crate::RawAccountStateReference {
return &self.reference;
}
/// Returns the rent epoch reported for this account state.
#[must_use]
pub const fn rent_epoch(&self) -> u64 {
return self.rent_epoch;
}
}
fn raw_model_error(field: &'static str) -> crate::Error {
return crate::Error::new(crate::ERROR_CODE_RAW_MODEL_INVALID, "invalid backend-agnostic RAW inspection model").with_context("field", field);
}
#[cfg(test)]
#[path = "../../unit_tests/model/raw_inspection.rs"]
mod tests;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/tests/dependency_boundary.rs
// version: 5
// version: 6
//! Dependency canaries for the Store API RAW foundation.
@@ -53,6 +53,7 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
let crate_root = include_str!("../src/lib.rs");
let model_home = include_str!("../src/model.rs");
let raw_account = include_str!("../src/model/raw_account.rs");
let raw_inspection = include_str!("../src/model/raw_inspection.rs");
let raw_outcome = include_str!("../src/model/raw_outcome.rs");
let raw_pagination = include_str!("../src/model/raw_pagination.rs");
let raw_primitives = include_str!("../src/model/raw_primitives.rs");
@@ -66,6 +67,7 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
assert!(crate_root.contains("mod error;"));
assert!(crate_root.contains("mod model;"));
assert!(model_home.contains("raw_account"));
assert!(model_home.contains("raw_inspection"));
assert!(model_home.contains("raw_outcome"));
assert!(model_home.contains("raw_pagination"));
assert!(model_home.contains("raw_primitives"));
@@ -78,6 +80,7 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
crate_root,
model_home,
raw_account,
raw_inspection,
raw_outcome,
raw_pagination,
raw_primitives,
@@ -109,12 +112,14 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
assert!(!crate_root.contains(forbidden), "deferred pre.006 model leaked into Store API surface: {forbidden}");
}
assert!(raw_transaction_capability.contains("trait RawTransactionRead"));
assert!(raw_transaction_capability.contains("trait RawTransactionInspectionRead"));
assert!(raw_transaction_capability.contains("trait RawTransactionWrite"));
assert!(raw_transaction_capability.contains("trait RawTransactionObservationRead"));
assert!(raw_transaction_capability.contains("trait RawTransactionObservationWrite"));
assert!(raw_retention_capability.contains("trait RawTransactionRetentionRead"));
assert!(raw_retention_capability.contains("trait RawTransactionRetentionWrite"));
assert!(raw_account_capability.contains("trait RawAccountStateRead"));
assert!(raw_account_capability.contains("trait RawAccountStateInspectionRead"));
assert!(raw_account_capability.contains("trait RawAccountStateWrite"));
assert!(raw_account_capability.contains("trait RawAccountObservationRead"));
assert!(raw_account_capability.contains("trait RawAccountObservationWrite"));
@@ -126,6 +131,9 @@ fn pre_006_source_boundary_keeps_models_and_capabilities_backend_free() {
}
assert!(!raw_pagination.contains("u64::MAX"));
assert!(!raw_pagination.contains("MAX_RAW_PAGE_ITEMS"));
for forbidden in ["DataTable", "recordsTotal", "recordsFiltered", "draw", "SQL", "OFFSET ", "LIMIT "] {
assert!(!raw_inspection.contains(forbidden), "UI/physical inspection concept leaked into Store API: {forbidden}");
}
return;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/tests/external_backend.rs
// version: 2
// version: 3
//! External-implementation canary for object-safe Store API capabilities.
@@ -27,6 +27,18 @@ impl ksp_store_api::RawTransactionRead for ExternalMemoryBackend {
}
}
impl ksp_store_api::RawTransactionInspectionRead for ExternalMemoryBackend {
fn inspect_raw_transactions<'a>(
&'a self,
query: &'a ksp_store_api::RawTransactionInspectionQuery,
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawInspectionPage<ksp_store_api::RawTransactionSummary>>> {
let _ = query;
return std::boxed::Box::pin(async {
return ksp_store_api::RawInspectionPage::try_new(std::vec::Vec::new(), 0, 0);
});
}
}
impl ksp_store_api::RawTransactionWrite for ExternalMemoryBackend {
fn persist_raw_transaction_acquisition<'a>(
&'a self,
@@ -92,6 +104,18 @@ impl ksp_store_api::RawAccountStateRead for ExternalMemoryBackend {
}
}
impl ksp_store_api::RawAccountStateInspectionRead for ExternalMemoryBackend {
fn inspect_raw_account_states<'a>(
&'a self,
query: &'a ksp_store_api::RawAccountStateInspectionQuery,
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<ksp_store_api::RawInspectionPage<ksp_store_api::RawAccountStateSummary>>> {
let _ = query;
return std::boxed::Box::pin(async {
return ksp_store_api::RawInspectionPage::try_new(std::vec::Vec::new(), 0, 0);
});
}
}
impl ksp_store_api::RawAccountStateWrite for ExternalMemoryBackend {
fn persist_raw_account_acquisition<'a>(
&'a self,
@@ -168,24 +192,28 @@ impl ksp_store_api::RawTransactionRetentionWrite for ExternalMemoryBackend {
}
#[test]
fn pre_006_external_backend_implements_each_capability_without_store_runtime_crate() {
fn v0_3_8_pre_003_external_backend_implements_canonical_and_inspection_capabilities_without_runtime_crate() {
let backend = ExternalMemoryBackend;
let transaction_read: &dyn ksp_store_api::RawTransactionRead = &backend;
let transaction_write: &dyn ksp_store_api::RawTransactionWrite = &backend;
let transaction_inspection: &dyn ksp_store_api::RawTransactionInspectionRead = &backend;
let transaction_observation_read: &dyn ksp_store_api::RawTransactionObservationRead = &backend;
let transaction_observation_write: &dyn ksp_store_api::RawTransactionObservationWrite = &backend;
let account_read: &dyn ksp_store_api::RawAccountStateRead = &backend;
let account_write: &dyn ksp_store_api::RawAccountStateWrite = &backend;
let account_inspection: &dyn ksp_store_api::RawAccountStateInspectionRead = &backend;
let account_observation_read: &dyn ksp_store_api::RawAccountObservationRead = &backend;
let account_observation_write: &dyn ksp_store_api::RawAccountObservationWrite = &backend;
let retention_read: &dyn ksp_store_api::RawTransactionRetentionRead = &backend;
let retention_write: &dyn ksp_store_api::RawTransactionRetentionWrite = &backend;
let _ = transaction_read;
let _ = transaction_write;
let _ = transaction_inspection;
let _ = transaction_observation_read;
let _ = transaction_observation_write;
let _ = account_read;
let _ = account_write;
let _ = account_inspection;
let _ = account_observation_read;
let _ = account_observation_write;
let _ = retention_read;

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/tests/public_api.rs
// version: 6
// version: 7
//! Integration canaries for the public `ksp-store-api` surface.
@@ -186,3 +186,21 @@ fn public_pre_007_retention_race_outcome_is_available_from_crate_root() {
assert_ne!(ksp_store_api::RawRetentionWriteOutcome::AlreadyAtTarget, ksp_store_api::RawRetentionWriteOutcome::ExpectedStateMismatch);
return;
}
#[test]
fn public_v0_3_8_pre_003_inspection_contracts_are_backend_neutral_and_dyn_compatible() {
let limit = match ksp_store_api::RawPageLimit::new(25) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = ksp_store_api::RawInspectionPageRequest::new(50, limit);
assert_eq!(request.offset(), 50);
assert_eq!(request.limit().get(), 25);
let page = ksp_store_api::RawInspectionPage::<u64>::try_new(std::vec![1, 2], 10, 5);
assert!(page.is_ok());
let transaction_inspection: std::option::Option<&dyn ksp_store_api::RawTransactionInspectionRead> = std::option::Option::None;
let account_inspection: std::option::Option<&dyn ksp_store_api::RawAccountStateInspectionRead> = std::option::Option::None;
assert!(transaction_inspection.is_none());
assert!(account_inspection.is_none());
return;
}

View File

@@ -1,10 +1,10 @@
// file: crates/ksp-store-api/tests/release_completeness.rs
// version: 1
// version: 2
//! Release-level boundary and completeness canaries for the `0.3.1` Store API RAW surface.
//! Release-level boundary and completeness canaries for the backend-neutral Store API RAW surface.
#[test]
fn pre_007_exact_crate_root_export_inventory_is_stable() {
fn v0_3_8_pre_003_exact_crate_root_export_inventory_is_stable() {
let crate_root = include_str!("../src/lib.rs");
let mut actual = std::vec::Vec::new();
for line in crate_root.lines() {
@@ -24,12 +24,14 @@ fn pre_007_exact_crate_root_export_inventory_is_stable() {
"pub use self::capability::raw_account::RawAccountObservationRead;",
"pub use self::capability::raw_account::RawAccountObservationWrite;",
"pub use self::capability::raw_account::RawAccountStateRead;",
"pub use self::capability::raw_account::RawAccountStateInspectionRead;",
"pub use self::capability::raw_account::RawAccountStateWrite;",
"pub use self::capability::raw_retention::RawTransactionRetentionRead;",
"pub use self::capability::raw_retention::RawTransactionRetentionWrite;",
"pub use self::capability::raw_transaction::RawTransactionObservationRead;",
"pub use self::capability::raw_transaction::RawTransactionObservationWrite;",
"pub use self::capability::raw_transaction::RawTransactionRead;",
"pub use self::capability::raw_transaction::RawTransactionInspectionRead;",
"pub use self::capability::raw_transaction::RawTransactionWrite;",
"pub use self::error::ERROR_CODE_RAW_CONFLICT;",
"pub use self::error::ERROR_CODE_RAW_MODEL_INVALID;",
@@ -40,6 +42,12 @@ fn pre_007_exact_crate_root_export_inventory_is_stable() {
"pub use self::model::raw_account::RawAccountObservation;",
"pub use self::model::raw_account::RawAccountState;",
"pub use self::model::raw_account::RawAccountStateReference;",
"pub use self::model::raw_inspection::RawAccountStateInspectionQuery;",
"pub use self::model::raw_inspection::RawAccountStateSummary;",
"pub use self::model::raw_inspection::RawInspectionPage;",
"pub use self::model::raw_inspection::RawInspectionPageRequest;",
"pub use self::model::raw_inspection::RawTransactionInspectionQuery;",
"pub use self::model::raw_inspection::RawTransactionSummary;",
"pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;",
"pub use self::model::raw_outcome::RawEntityWriteOutcome;",
"pub use self::model::raw_outcome::RawObservationWriteOutcome;",
@@ -83,7 +91,7 @@ fn pre_007_exact_crate_root_export_inventory_is_stable() {
}
#[test]
fn pre_007_exact_production_module_inventory_is_raw_only() {
fn v0_3_8_pre_003_exact_production_module_inventory_is_raw_only() {
let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let root_names = rust_file_names(root.as_path());
assert!(root_names.is_ok());
@@ -100,7 +108,7 @@ fn pre_007_exact_production_module_inventory_is_raw_only() {
};
assert_eq!(
model_names,
std::vec!["raw_account.rs", "raw_outcome.rs", "raw_pagination.rs", "raw_primitives.rs", "raw_retention.rs", "raw_transaction.rs"]
std::vec!["raw_account.rs", "raw_inspection.rs", "raw_outcome.rs", "raw_pagination.rs", "raw_primitives.rs", "raw_retention.rs", "raw_transaction.rs"]
);
let capability_names = rust_file_names(root.join("capability").as_path());
assert!(capability_names.is_ok());
@@ -141,6 +149,7 @@ fn pre_007_interface_store_ownership_and_negative_scope_remain_explicit() {
include_str!("../src/lib.rs"),
include_str!("../src/model.rs"),
include_str!("../src/model/raw_account.rs"),
include_str!("../src/model/raw_inspection.rs"),
include_str!("../src/model/raw_outcome.rs"),
include_str!("../src/model/raw_pagination.rs"),
include_str!("../src/model/raw_primitives.rs"),
@@ -176,7 +185,7 @@ fn pre_007_interface_store_ownership_and_negative_scope_remain_explicit() {
}
#[test]
fn pre_007_capability_inventory_stays_fine_grained_without_runtime_facade() {
fn v0_3_8_pre_003_capability_inventory_stays_fine_grained_without_runtime_facade() {
let sources = [
include_str!("../src/capability/raw_account.rs"),
include_str!("../src/capability/raw_retention.rs"),
@@ -196,10 +205,12 @@ fn pre_007_capability_inventory_stays_fine_grained_without_runtime_facade() {
"pub trait RawAccountObservationRead: std::marker::Send + std::marker::Sync {",
"pub trait RawAccountObservationWrite: std::marker::Send + std::marker::Sync {",
"pub trait RawAccountStateRead: std::marker::Send + std::marker::Sync {",
"pub trait RawAccountStateInspectionRead: std::marker::Send + std::marker::Sync {",
"pub trait RawAccountStateWrite: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionObservationRead: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionObservationWrite: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionRead: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionInspectionRead: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionRetentionRead: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionRetentionWrite: std::marker::Send + std::marker::Sync {",
"pub trait RawTransactionWrite: std::marker::Send + std::marker::Sync {",

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/tests/security_hardening.rs
// version: 1
// version: 2
//! Adversarial and retention-race canaries for the Store API RAW foundation.
@@ -122,3 +122,47 @@ fn pre_007_retention_outcome_distinguishes_lost_compare_and_transition_race() {
assert_ne!(ksp_store_api::RawRetentionWriteOutcome::AlreadyAtTarget, ksp_store_api::RawRetentionWriteOutcome::ExpectedStateMismatch);
return;
}
#[test]
fn v0_3_8_pre_003_inspection_summaries_and_counts_reject_payload_shaped_or_inconsistent_state() {
let network = match ksp_store_api::RawNetworkId::new("mainnet-beta".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let format = match ksp_store_api::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let transaction_reference = ksp_store_api::RawTransactionReference::new(network.clone(), ksp_store_api::RawTransactionSignature::new([0x61_u8; 64]));
let summary = ksp_store_api::RawTransactionSummary::try_new(
transaction_reference,
42,
std::option::Option::None,
format,
1,
ksp_store_api::RawContentHash::new([0x62_u8; 32]),
std::option::Option::Some(512),
ksp_store_api::RawRetentionState::Full,
);
assert!(summary.is_ok());
let summary = match summary {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let debug = std::format!("{summary:?}");
assert!(!debug.contains(HOSTILE_MARKER));
assert!(!debug.contains("payload:"));
let account_reference = ksp_store_api::RawAccountStateReference::new(
network,
ksp_store_api::Pubkey::new_from_array([0x63_u8; 32]),
43,
ksp_store_api::RawContentHash::new([0x64_u8; 32]),
);
assert!(
ksp_store_api::RawAccountStateSummary::try_new(account_reference, 1, ksp_store_api::Pubkey::new_from_array([0x65_u8; 32]), false, 0, 16_777_217,)
.is_err()
);
assert!(ksp_store_api::RawInspectionPage::<u64>::try_new(std::vec![1], 0, 0).is_err());
assert!(ksp_store_api::RawInspectionPage::<u64>::try_new(std::vec::Vec::new(), 1, 2).is_err());
return;
}

View File

@@ -0,0 +1,156 @@
// file: crates/ksp-store-api/unit_tests/model/raw_inspection.rs
// version: 1
//! Unit tests for backend-neutral RAW inspection contracts.
fn network() -> std::option::Option<crate::RawNetworkId> {
return match crate::RawNetworkId::new("mainnet-beta".to_owned()) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
fn format_id() -> std::option::Option<crate::RawFormatId> {
return match crate::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
#[test]
fn inspection_page_request_preserves_large_offset_and_caller_limit_without_cursor_semantics() {
let limit = match crate::RawPageLimit::new(100) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let request = crate::RawInspectionPageRequest::new(u64::MAX, limit);
assert_eq!(request.offset(), u64::MAX);
assert_eq!(request.limit().get(), 100);
return;
}
#[test]
fn inspection_page_rejects_inconsistent_exact_counts() {
assert!(crate::RawInspectionPage::<u64>::try_new(std::vec![1], 0, 0).is_err());
assert!(crate::RawInspectionPage::<u64>::try_new(std::vec::Vec::new(), 4, 5).is_err());
let page = crate::RawInspectionPage::<u64>::try_new(std::vec![1, 2], 5, 3);
assert!(page.is_ok());
let page = match page {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(page.total_items(), 5);
assert_eq!(page.filtered_items(), 3);
assert_eq!(page.items(), &[1, 2]);
return;
}
#[test]
fn inspection_queries_keep_backend_neutral_filters_and_random_access_page() {
let network = match network() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let slots = match crate::RawSlotRange::new(std::option::Option::Some(10), std::option::Option::Some(20)) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let limit = match crate::RawPageLimit::new(25) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let transaction =
crate::RawTransactionInspectionQuery::new(network.clone(), slots, crate::RawSortDirection::Descending, crate::RawInspectionPageRequest::new(50, limit));
assert_eq!(transaction.network().as_str(), "mainnet-beta");
assert_eq!(transaction.page().offset(), 50);
assert_eq!(transaction.slots().end_inclusive(), std::option::Option::Some(20));
assert_eq!(transaction.direction(), crate::RawSortDirection::Descending);
let pubkey = crate::Pubkey::new_from_array([0x21_u8; 32]);
let account = crate::RawAccountStateInspectionQuery::new(
network,
std::option::Option::Some(pubkey),
slots,
crate::RawSortDirection::Ascending,
crate::RawInspectionPageRequest::new(75, limit),
);
assert_eq!(account.page().offset(), 75);
assert_eq!(account.pubkey(), std::option::Option::Some(&pubkey));
return;
}
#[test]
fn transaction_summary_is_payload_free_and_retention_consistent() {
let network = match network() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let format_id = match format_id() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reference = crate::RawTransactionReference::new(network, crate::RawTransactionSignature::new([0x31_u8; 64]));
let full = crate::RawTransactionSummary::try_new(
reference.clone(),
42,
std::option::Option::None,
format_id.clone(),
1,
crate::RawContentHash::new([0x32_u8; 32]),
std::option::Option::Some(123),
crate::RawRetentionState::Full,
);
assert!(full.is_ok());
let full = match full {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(full.slot(), 42);
assert_eq!(full.payload_size_bytes(), std::option::Option::Some(123));
assert_eq!(full.retention_state(), crate::RawRetentionState::Full);
assert!(
crate::RawTransactionSummary::try_new(
reference.clone(),
42,
std::option::Option::None,
format_id.clone(),
1,
crate::RawContentHash::new([0x32_u8; 32]),
std::option::Option::None,
crate::RawRetentionState::Full,
)
.is_err()
);
assert!(
crate::RawTransactionSummary::try_new(
reference,
42,
std::option::Option::None,
format_id,
1,
crate::RawContentHash::new([0x32_u8; 32]),
std::option::Option::Some(123),
crate::RawRetentionState::Purged,
)
.is_err()
);
return;
}
#[test]
fn account_summary_exposes_length_only_and_enforces_raw_account_bound() {
let network = match network() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reference = crate::RawAccountStateReference::new(network, crate::Pubkey::new_from_array([0x41_u8; 32]), 88, crate::RawContentHash::new([0x42_u8; 32]));
let summary = crate::RawAccountStateSummary::try_new(reference.clone(), 1000, crate::Pubkey::new_from_array([0x43_u8; 32]), false, 9, 512);
assert!(summary.is_ok());
let summary = match summary {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(summary.reference().slot(), 88);
assert_eq!(summary.data_length_bytes(), 512);
assert!(crate::RawAccountStateSummary::try_new(reference, 1000, crate::Pubkey::new_from_array([0x43_u8; 32]), false, 9, 16_777_217,).is_err());
return;
}