v0.3.1-pre.006

This commit is contained in:
2026-08-29 09:10:07 +02:00
parent c83e3261d0
commit b76baf6835
19 changed files with 1175 additions and 60 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/capability.rs
// version: 2
// version: 3
//! Private home for backend-agnostic Store capability contracts.
//!
@@ -9,6 +9,7 @@
//! outside `ksp-store-api`.
pub(crate) mod raw_account;
pub(crate) mod raw_retention;
pub(crate) mod raw_transaction;
/// Boxed async operation returned by object-safe Store capability contracts.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/capability/raw_account.rs
// version: 1
// version: 2
/// Read capability for complete canonical RAW account states.
///
@@ -12,21 +12,31 @@ pub trait RawAccountStateRead: std::marker::Send + std::marker::Sync {
&'a self,
reference: &'a crate::RawAccountStateReference,
) -> crate::StoreApiFuture<'a, crate::Result<std::option::Option<crate::RawAccountState>>>;
/// Lists deterministic account-state references using an opaque backend cursor.
///
/// The backend must not invent a KSP policy ceiling below the caller's
/// request. A real backend/physical limitation may determine the page that
/// can be served and continuation is represented by the opaque cursor.
fn list_raw_account_states<'a>(
&'a self,
query: &'a crate::RawAccountStateQuery,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawPage<crate::RawAccountStateReference>>>;
}
/// Write capability for complete RAW account-state acquisitions.
///
/// The account state and its acquisition observation form one logical
/// persistence operation. An implementation must not leave one side durable if
/// the other side fails. Detailed idempotence/conflict outcomes are introduced
/// by `0.3.1-pre.006`; this tranche exposes only success/failure.
/// the other side fails. Divergent content for one logical identity is a stable
/// conflict error, never a silent overwrite.
pub trait RawAccountStateWrite: std::marker::Send + std::marker::Sync {
/// Persists one complete RAW account state together with one observation atomically.
fn persist_raw_account_acquisition<'a>(
&'a self,
state: crate::RawAccountState,
observation: crate::RawAccountObservation,
) -> crate::StoreApiFuture<'a, crate::Result<()>>;
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawAcquisitionWriteOutcome>>;
}
/// Read capability for persisted RAW account-state observations.
@@ -41,9 +51,11 @@ pub trait RawAccountObservationRead: std::marker::Send + std::marker::Sync {
/// Write capability for an additional observation of an already persisted RAW account state.
///
/// This capability allows repeated HTTP/WS/gRPC acquisitions to be retained
/// without resubmitting account bytes. The referenced state must already exist;
/// detailed outcomes are deferred to `0.3.1-pre.006`.
/// without resubmitting account bytes. The referenced state must already exist.
pub trait RawAccountObservationWrite: std::marker::Send + std::marker::Sync {
/// Persists one additional acquisition observation for an existing RAW account state.
fn record_raw_account_observation<'a>(&'a self, observation: crate::RawAccountObservation) -> crate::StoreApiFuture<'a, crate::Result<()>>;
fn record_raw_account_observation<'a>(
&'a self,
observation: crate::RawAccountObservation,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawObservationWriteOutcome>>;
}

View File

@@ -0,0 +1,29 @@
// file: crates/ksp-store-api/src/capability/raw_retention.rs
// version: 1
/// Read capability for canonical RAW transaction retention metadata.
pub trait RawTransactionRetentionRead: std::marker::Send + std::marker::Sync {
/// Reads the logical retention state for one known transaction identity.
fn get_raw_transaction_retention_state<'a>(
&'a self,
reference: &'a crate::RawTransactionReference,
) -> crate::StoreApiFuture<'a, crate::Result<std::option::Option<crate::RawRetentionState>>>;
/// Reads the minimal durable tombstone when the transaction payload was purged.
fn get_raw_transaction_tombstone<'a>(
&'a self,
reference: &'a crate::RawTransactionReference,
) -> crate::StoreApiFuture<'a, crate::Result<std::option::Option<crate::RawTransactionTombstone>>>;
}
/// Write capability for policy-authorized RAW transaction retention transitions.
///
/// This contract applies a transition selected by an external worker/job policy;
/// the Store backend never decides by itself that higher processing is complete.
pub trait RawTransactionRetentionWrite: std::marker::Send + std::marker::Sync {
/// Applies one atomic forward lifecycle transition chosen by the caller.
fn transition_raw_transaction_retention<'a>(
&'a self,
transition: crate::RawTransactionRetentionTransition,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawRetentionWriteOutcome>>;
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/capability/raw_transaction.rs
// version: 1
// version: 2
/// Read capability for canonical RAW transactions.
///
@@ -12,21 +12,32 @@ pub trait RawTransactionRead: std::marker::Send + std::marker::Sync {
&'a self,
reference: &'a crate::RawTransactionReference,
) -> crate::StoreApiFuture<'a, crate::Result<std::option::Option<crate::RawTransaction>>>;
/// Lists deterministic transaction references using an opaque backend cursor.
///
/// The backend must honor the caller-requested page limit unless a real
/// backend/physical limitation prevents it. `ksp-store-api` imposes no
/// arbitrary smaller result ceiling or executor policy.
fn list_raw_transactions<'a>(
&'a self,
query: &'a crate::RawTransactionQuery,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawPage<crate::RawTransactionReference>>>;
}
/// Write capability for canonical RAW transaction acquisitions.
///
/// The transaction and its acquisition observation form one logical persistence
/// operation. An implementation must not leave one side durable if the other
/// side fails. Detailed idempotence/conflict outcomes are introduced by
/// `0.3.1-pre.006`; this tranche exposes only success/failure.
/// side fails. Divergent content for one logical identity is a stable conflict
/// error, never a silent overwrite.
pub trait RawTransactionWrite: std::marker::Send + std::marker::Sync {
/// Persists one complete RAW transaction together with one observation atomically.
fn persist_raw_transaction_acquisition<'a>(
&'a self,
transaction: crate::RawTransaction,
observation: crate::RawTransactionObservation,
) -> crate::StoreApiFuture<'a, crate::Result<()>>;
mode: crate::RawTransactionAcquisitionMode,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawAcquisitionWriteOutcome>>;
}
/// Read capability for persisted RAW transaction observations.
@@ -42,9 +53,11 @@ pub trait RawTransactionObservationRead: std::marker::Send + std::marker::Sync {
///
/// This capability exists so repeated acquisitions can be recorded without
/// resubmitting the potentially large canonical transaction payload. The
/// referenced transaction must already exist; detailed outcomes are deferred to
/// `0.3.1-pre.006`.
/// referenced transaction must already exist.
pub trait RawTransactionObservationWrite: std::marker::Send + std::marker::Sync {
/// Persists one additional acquisition observation for an existing RAW transaction.
fn record_raw_transaction_observation<'a>(&'a self, observation: crate::RawTransactionObservation) -> crate::StoreApiFuture<'a, crate::Result<()>>;
fn record_raw_transaction_observation<'a>(
&'a self,
observation: crate::RawTransactionObservation,
) -> crate::StoreApiFuture<'a, crate::Result<crate::RawObservationWriteOutcome>>;
}

View File

@@ -1,9 +1,15 @@
// file: crates/ksp-store-api/src/error.rs
// version: 1
// version: 2
/// Error code used when a RAW write collides with divergent content for the same logical identity.
pub const ERROR_CODE_RAW_CONFLICT: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("store_api", "raw_conflict");
/// Error code used when a RAW Store model violates one of its backend-agnostic invariants.
pub const ERROR_CODE_RAW_MODEL_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("store_api", "raw_model_invalid");
/// Error code used when a KSP-owned RAW persistence payload violates its format or admission contract.
pub const ERROR_CODE_RAW_PAYLOAD_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("store_api", "raw_payload_invalid");
/// Error code used when acquisition provenance is malformed, unsafe or internally inconsistent.
pub const ERROR_CODE_RAW_PROVENANCE_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("store_api", "raw_provenance_invalid");
/// Error code used when one RAW query or cursor violates backend-agnostic query invariants.
pub const ERROR_CODE_RAW_QUERY_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("store_api", "raw_query_invalid");
/// Error code used when a RAW retention transition violates the logical lifecycle contract.
pub const ERROR_CODE_RAW_RETENTION_INVALID: ksp_core_lib::ErrorCode = ksp_core_lib::ErrorCode::new("store_api", "raw_retention_invalid");

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/lib.rs
// version: 4
// version: 5
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -30,6 +30,10 @@ pub use self::capability::raw_account::RawAccountObservationWrite;
pub use self::capability::raw_account::RawAccountStateRead;
/// Write capability for complete canonical RAW account-state acquisitions.
pub use self::capability::raw_account::RawAccountStateWrite;
/// Read capability for canonical RAW transaction retention metadata.
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 persisted RAW transaction observations.
pub use self::capability::raw_transaction::RawTransactionObservationRead;
/// Write capability for additional observations of already persisted RAW transactions.
@@ -38,18 +42,48 @@ pub use self::capability::raw_transaction::RawTransactionObservationWrite;
pub use self::capability::raw_transaction::RawTransactionRead;
/// Write capability for canonical RAW transaction acquisitions.
pub use self::capability::raw_transaction::RawTransactionWrite;
/// Error code used when a RAW write collides with divergent content for the same logical identity.
pub use self::error::ERROR_CODE_RAW_CONFLICT;
/// Error code used when a RAW Store model violates one of its backend-agnostic invariants.
pub use self::error::ERROR_CODE_RAW_MODEL_INVALID;
/// Error code used when a KSP-owned RAW persistence payload violates its format or admission contract.
pub use self::error::ERROR_CODE_RAW_PAYLOAD_INVALID;
/// Error code used when acquisition provenance is malformed, unsafe or internally inconsistent.
pub use self::error::ERROR_CODE_RAW_PROVENANCE_INVALID;
/// Error code used when one RAW query or cursor violates backend-agnostic query invariants.
pub use self::error::ERROR_CODE_RAW_QUERY_INVALID;
/// Error code used when a RAW retention transition violates the logical lifecycle contract.
pub use self::error::ERROR_CODE_RAW_RETENTION_INVALID;
/// Persistable acquisition observation linked to one complete canonical RAW account state.
pub use self::model::raw_account::RawAccountObservation;
/// Canonical complete N1 RAW account state independent from acquisition transport.
pub use self::model::raw_account::RawAccountState;
/// Durable backend-independent identity of one canonical RAW account state.
pub use self::model::raw_account::RawAccountStateReference;
/// 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.
pub use self::model::raw_outcome::RawEntityWriteOutcome;
/// Outcome for one deterministic acquisition observation write.
pub use self::model::raw_outcome::RawObservationWriteOutcome;
/// Maximum opaque query cursor length admitted by the Store API.
pub use self::model::raw_pagination::MAX_RAW_PAGE_CURSOR_BYTES;
/// Backend-independent list query for complete canonical RAW account states.
pub use self::model::raw_pagination::RawAccountStateQuery;
/// One deterministic page of backend-independent Store results.
pub use self::model::raw_pagination::RawPage;
/// Opaque backend-owned cursor returned by one deterministic Store query.
pub use self::model::raw_pagination::RawPageCursor;
/// Caller-requested page size without an arbitrary KSP policy ceiling.
pub use self::model::raw_pagination::RawPageLimit;
/// Opaque-cursor page request used by backend-independent list operations.
pub use self::model::raw_pagination::RawPageRequest;
/// Optional inclusive Solana slot bounds for one Store query.
pub use self::model::raw_pagination::RawSlotRange;
/// Deterministic traversal direction for Store list queries.
pub use self::model::raw_pagination::RawSortDirection;
/// Backend-independent list query for canonical RAW transactions.
pub use self::model::raw_pagination::RawTransactionQuery;
/// Maximum complete RAW account-data length admitted by the Store API.
pub use self::model::raw_primitives::MAX_RAW_ACCOUNT_DATA_BYTES;
/// Maximum UTF-8 byte length accepted for one safe logical RAW/provenance code.
@@ -80,6 +114,16 @@ pub use self::model::raw_primitives::RawProvenanceCode;
pub use self::model::raw_primitives::RawTimestamp;
/// Canonical 64-byte Solana transaction signature used by Store identities.
pub use self::model::raw_primitives::RawTransactionSignature;
/// Logical availability state of one canonical RAW payload.
pub use self::model::raw_retention::RawRetentionState;
/// Outcome of one atomic RAW retention transition.
pub use self::model::raw_retention::RawRetentionWriteOutcome;
/// Explicit write mode for canonical RAW transaction acquisitions.
pub use self::model::raw_retention::RawTransactionAcquisitionMode;
/// Requested compare-and-transition operation for one RAW transaction retention state.
pub use self::model::raw_retention::RawTransactionRetentionTransition;
/// Minimal durable identity retained after a canonical RAW transaction payload is purged.
pub use self::model::raw_retention::RawTransactionTombstone;
/// Canonical source-independent N1 RAW transaction persisted by Store backends.
pub use self::model::raw_transaction::RawTransaction;
/// Persistable acquisition observation linked to one canonical RAW transaction.

View File

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

View File

@@ -0,0 +1,55 @@
// file: crates/ksp-store-api/src/model/raw_outcome.rs
// version: 1
/// Outcome for one canonical RAW entity in an idempotent persistence operation.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawEntityWriteOutcome {
/// The canonical RAW entity was inserted for the first time.
Inserted,
/// Identical canonical content was already durable.
AlreadyPresent,
/// A previously purged RAW entity was explicitly rehydrated.
Rehydrated,
/// Normal persistence skipped a durable purged tombstone.
SkippedPurged,
}
/// Outcome for one deterministic acquisition observation write.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawObservationWriteOutcome {
/// The observation was inserted for the first time.
Inserted,
/// The same observation key already identified identical durable content.
AlreadyPresent,
/// No observation was recorded because the associated RAW entity was intentionally skipped.
NotRecorded,
}
/// Combined outcome of one atomic canonical RAW entity plus observation acquisition.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RawAcquisitionWriteOutcome {
entity: crate::RawEntityWriteOutcome,
observation: crate::RawObservationWriteOutcome,
}
impl RawAcquisitionWriteOutcome {
/// Creates one backend-independent atomic acquisition outcome.
#[must_use]
pub const fn new(entity: crate::RawEntityWriteOutcome, observation: crate::RawObservationWriteOutcome) -> Self {
return Self { entity, observation };
}
/// Returns the canonical RAW entity write outcome.
#[must_use]
pub const fn entity(&self) -> crate::RawEntityWriteOutcome {
return self.entity;
}
/// Returns the acquisition observation write outcome.
#[must_use]
pub const fn observation(&self) -> crate::RawObservationWriteOutcome {
return self.observation;
}
}

View File

@@ -0,0 +1,267 @@
// file: crates/ksp-store-api/src/model/raw_pagination.rs
// version: 1
/// Maximum opaque cursor length accepted by Store API queries.
///
/// This bounds untrusted token material only. It is not a policy limit on the
/// number of rows/results a backend may return.
pub const MAX_RAW_PAGE_CURSOR_BYTES: usize = 4 * 1024;
/// Opaque backend-owned cursor returned by one deterministic Store query.
#[derive(Clone, Eq, PartialEq)]
pub struct RawPageCursor(std::boxed::Box<[u8]>);
impl RawPageCursor {
/// Creates one non-empty bounded opaque cursor.
pub fn try_new(bytes: std::boxed::Box<[u8]>) -> crate::Result<Self> {
if bytes.is_empty() || bytes.len() > crate::MAX_RAW_PAGE_CURSOR_BYTES {
return std::result::Result::Err(raw_query_error("cursor"));
}
return std::result::Result::Ok(Self(bytes));
}
/// Returns the opaque cursor bytes unchanged.
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
return self.0.as_ref();
}
}
impl std::fmt::Debug for RawPageCursor {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.debug_struct("RawPageCursor").field("len", &self.0.len()).finish();
}
}
/// Caller-requested page size without an arbitrary KSP policy ceiling.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RawPageLimit(u64);
impl RawPageLimit {
/// Creates one strictly positive requested page size.
///
/// `ksp-store-api` deliberately imposes no smaller functional maximum.
/// Concrete backends may expose or return their real physical limitations.
pub fn new(value: u64) -> crate::Result<Self> {
if value == 0 {
return std::result::Result::Err(raw_query_error("limit"));
}
return std::result::Result::Ok(Self(value));
}
/// Returns the exact caller-requested item count.
#[must_use]
pub const fn get(&self) -> u64 {
return self.0;
}
}
/// Opaque-cursor page request used by backend-independent list operations.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawPageRequest {
cursor: std::option::Option<crate::RawPageCursor>,
limit: crate::RawPageLimit,
}
impl RawPageRequest {
/// Creates one first-page request.
#[must_use]
pub const fn first(limit: crate::RawPageLimit) -> Self {
return Self { cursor: std::option::Option::None, limit };
}
/// Creates one continuation-page request with an opaque backend cursor.
#[must_use]
pub const fn after(limit: crate::RawPageLimit, cursor: crate::RawPageCursor) -> Self {
return Self { cursor: std::option::Option::Some(cursor), limit };
}
/// Returns the opaque continuation cursor when present.
#[must_use]
pub fn cursor(&self) -> std::option::Option<&crate::RawPageCursor> {
return self.cursor.as_ref();
}
/// Returns the exact caller-requested page size.
#[must_use]
pub const fn limit(&self) -> crate::RawPageLimit {
return self.limit;
}
}
/// One deterministic page of backend-independent Store results.
#[derive(Debug)]
pub struct RawPage<T> {
items: std::vec::Vec<T>,
next_cursor: std::option::Option<crate::RawPageCursor>,
}
impl<T> RawPage<T> {
/// Creates one result page from backend-provided items and optional continuation cursor.
#[must_use]
pub fn new(items: std::vec::Vec<T>, next_cursor: std::option::Option<crate::RawPageCursor>) -> Self {
return Self { items, next_cursor };
}
/// Returns the current page items.
#[must_use]
pub fn items(&self) -> &[T] {
return self.items.as_slice();
}
/// Consumes the page and returns its items.
#[must_use]
pub fn into_items(self) -> std::vec::Vec<T> {
return self.items;
}
/// Returns the next opaque cursor when more results are available.
#[must_use]
pub fn next_cursor(&self) -> std::option::Option<&crate::RawPageCursor> {
return self.next_cursor.as_ref();
}
}
/// Deterministic traversal direction for Store list queries.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawSortDirection {
/// Oldest/lower ordered keys first.
Ascending,
/// Newest/higher ordered keys first.
Descending,
}
/// Optional inclusive Solana slot bounds for one Store query.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RawSlotRange {
end_inclusive: std::option::Option<u64>,
start_inclusive: std::option::Option<u64>,
}
impl RawSlotRange {
/// Creates one optional inclusive slot range and rejects reversed bounds.
pub fn new(start_inclusive: std::option::Option<u64>, end_inclusive: std::option::Option<u64>) -> crate::Result<Self> {
if let (std::option::Option::Some(start), std::option::Option::Some(end)) = (start_inclusive, end_inclusive) {
if start > end {
return std::result::Result::Err(raw_query_error("slot_range"));
}
}
return std::result::Result::Ok(Self { end_inclusive, start_inclusive });
}
/// Returns the optional inclusive ending slot.
#[must_use]
pub const fn end_inclusive(&self) -> std::option::Option<u64> {
return self.end_inclusive;
}
/// Returns the optional inclusive starting slot.
#[must_use]
pub const fn start_inclusive(&self) -> std::option::Option<u64> {
return self.start_inclusive;
}
}
/// Backend-independent list query for canonical RAW transactions.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionQuery {
direction: crate::RawSortDirection,
network: crate::RawNetworkId,
page: crate::RawPageRequest,
slots: crate::RawSlotRange,
}
impl RawTransactionQuery {
/// Creates one deterministic transaction-reference query.
#[must_use]
pub fn new(network: crate::RawNetworkId, slots: crate::RawSlotRange, direction: crate::RawSortDirection, page: crate::RawPageRequest) -> 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 required logical network scope.
#[must_use]
pub fn network(&self) -> &crate::RawNetworkId {
return &self.network;
}
/// Returns pagination inputs without exposing backend cursor contents.
#[must_use]
pub fn page(&self) -> &crate::RawPageRequest {
return &self.page;
}
/// Returns optional inclusive slot bounds.
#[must_use]
pub const fn slots(&self) -> crate::RawSlotRange {
return self.slots;
}
}
/// Backend-independent list query for complete canonical RAW account states.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawAccountStateQuery {
direction: crate::RawSortDirection,
network: crate::RawNetworkId,
page: crate::RawPageRequest,
pubkey: std::option::Option<crate::Pubkey>,
slots: crate::RawSlotRange,
}
impl RawAccountStateQuery {
/// Creates one deterministic account-state-reference query.
#[must_use]
pub fn new(
network: crate::RawNetworkId,
pubkey: std::option::Option<crate::Pubkey>,
slots: crate::RawSlotRange,
direction: crate::RawSortDirection,
page: crate::RawPageRequest,
) -> 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 required logical network scope.
#[must_use]
pub fn network(&self) -> &crate::RawNetworkId {
return &self.network;
}
/// Returns pagination inputs without exposing backend cursor contents.
#[must_use]
pub fn page(&self) -> &crate::RawPageRequest {
return &self.page;
}
/// Returns an optional account-address restriction.
#[must_use]
pub fn pubkey(&self) -> std::option::Option<&crate::Pubkey> {
return self.pubkey.as_ref();
}
/// Returns optional inclusive slot bounds.
#[must_use]
pub const fn slots(&self) -> crate::RawSlotRange {
return self.slots;
}
}
fn raw_query_error(field: &'static str) -> crate::Error {
return crate::Error::new(crate::ERROR_CODE_RAW_QUERY_INVALID, "invalid backend-agnostic RAW Store query").with_context("field", field);
}
#[cfg(test)]
#[path = "../../unit_tests/model/raw_pagination.rs"]
mod tests;

View File

@@ -0,0 +1,152 @@
// file: crates/ksp-store-api/src/model/raw_retention.rs
// version: 1
/// Logical availability state of one canonical RAW payload.
///
/// These states describe payload retention only. They never prove that a
/// STRUCTURAL/DECODED/DOMAIN processor has completed.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawRetentionState {
/// Complete canonical RAW payload is retained in ordinary Store availability.
Full,
/// Complete logical RAW content is retained in a compacted representation.
Compacted,
/// RAW content has been moved to an archive managed outside the hot Store path.
Archived,
/// RAW payload is absent while a durable tombstone remains.
Purged,
}
/// Explicit write mode for canonical RAW transaction acquisitions.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawTransactionAcquisitionMode {
/// Normal acquisition must skip an existing purged tombstone.
Normal,
/// Explicitly allow an existing purged transaction to be rehydrated.
ForceRehydrate,
}
/// Minimal durable identity retained after a canonical RAW transaction payload is purged.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionTombstone {
content_hash: crate::RawContentHash,
format_id: crate::RawFormatId,
format_version: u32,
reference: crate::RawTransactionReference,
slot: u64,
}
impl RawTransactionTombstone {
/// Creates one minimal transaction tombstone from canonical RAW identity metadata.
pub fn try_new(
reference: crate::RawTransactionReference,
slot: u64,
format_id: crate::RawFormatId,
format_version: u32,
content_hash: crate::RawContentHash,
) -> crate::Result<Self> {
if format_version == 0 {
return std::result::Result::Err(raw_retention_error("format_version"));
}
return std::result::Result::Ok(Self { content_hash, format_id, format_version, reference, slot });
}
/// Returns the canonical content hash retained after purge.
#[must_use]
pub const fn content_hash(&self) -> crate::RawContentHash {
return self.content_hash;
}
/// Returns the KSP-owned canonical RAW format identifier retained after purge.
#[must_use]
pub fn format_id(&self) -> &crate::RawFormatId {
return &self.format_id;
}
/// Returns the KSP-owned canonical RAW format version retained after purge.
#[must_use]
pub const fn format_version(&self) -> u32 {
return self.format_version;
}
/// Returns the durable backend-independent transaction identity.
#[must_use]
pub fn reference(&self) -> &crate::RawTransactionReference {
return &self.reference;
}
/// Returns the known transaction slot retained after purge.
#[must_use]
pub const fn slot(&self) -> u64 {
return self.slot;
}
}
/// Requested compare-and-transition operation for one RAW transaction retention state.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionRetentionTransition {
expected: crate::RawRetentionState,
reference: crate::RawTransactionReference,
target: crate::RawRetentionState,
}
impl RawTransactionRetentionTransition {
/// Creates one allowed forward retention transition.
///
/// Rehydration from `Purged` is deliberately excluded and uses
/// [`crate::RawTransactionAcquisitionMode::ForceRehydrate`] instead.
pub fn try_new(reference: crate::RawTransactionReference, expected: crate::RawRetentionState, target: crate::RawRetentionState) -> crate::Result<Self> {
if !allowed_transition(expected, target) {
return std::result::Result::Err(raw_retention_error("transition"));
}
return std::result::Result::Ok(Self { expected, reference, target });
}
/// Returns the state the caller expects before applying the transition.
#[must_use]
pub const fn expected(&self) -> crate::RawRetentionState {
return self.expected;
}
/// Returns the durable transaction identity targeted by the transition.
#[must_use]
pub fn reference(&self) -> &crate::RawTransactionReference {
return &self.reference;
}
/// Returns the requested forward retention state.
#[must_use]
pub const fn target(&self) -> crate::RawRetentionState {
return self.target;
}
}
/// Outcome of one atomic RAW retention transition.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawRetentionWriteOutcome {
/// The requested forward transition was applied.
Applied,
/// The entity was already at the requested target state.
AlreadyAtTarget,
}
fn allowed_transition(expected: crate::RawRetentionState, target: crate::RawRetentionState) -> bool {
return matches!(
(expected, target),
(crate::RawRetentionState::Full, crate::RawRetentionState::Compacted)
| (crate::RawRetentionState::Full, crate::RawRetentionState::Archived)
| (crate::RawRetentionState::Compacted, crate::RawRetentionState::Archived)
| (crate::RawRetentionState::Archived, crate::RawRetentionState::Purged)
);
}
fn raw_retention_error(field: &'static str) -> crate::Error {
return crate::Error::new(crate::ERROR_CODE_RAW_RETENTION_INVALID, "invalid RAW retention lifecycle request").with_context("field", field);
}
#[cfg(test)]
#[path = "../../unit_tests/model/raw_retention.rs"]
mod tests;