v0.3.16-pre.002

This commit is contained in:
2026-09-21 08:31:42 +02:00
parent c7a6df859d
commit b0deefddb7
8 changed files with 597 additions and 36 deletions

View File

@@ -1,10 +1,8 @@
// file: crates/ksp-store-api/src/lib.rs
// version: 7
// version: 8
#![warn(missing_docs)]
#![deny(unreachable_pub)]
#![forbid(unsafe_code)]
//! Backend-agnostic persistence contracts for KSP Store implementations.
//!
//! `ksp-store-api` owns persistent models plus the contracts that operate on
@@ -15,11 +13,9 @@
//! Models and capabilities deliberately have separate private module homes.
//! Backend implementations, SQL, migrations, Config, Transport and runtime
//! dispatch remain outside this crate.
mod capability;
mod error;
mod model;
/// Boxed async operation returned by object-safe Store capability contracts.
pub use self::capability::StoreApiFuture;
/// Read capability for random-access RAW account-observation inspection.
@@ -94,6 +90,8 @@ pub use self::model::raw_outcome::RawAcquisitionWriteOutcome;
pub use self::model::raw_outcome::RawEntityWriteOutcome;
/// Outcome for one deterministic acquisition observation write.
pub use self::model::raw_outcome::RawObservationWriteOutcome;
/// Durable Store outcome for one variant-aware RAW transaction acquisition.
pub use self::model::raw_outcome::RawTransactionVariantWriteOutcome;
/// 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.
@@ -154,10 +152,24 @@ pub use self::model::raw_retention::RawTransactionRetentionTransition;
pub use self::model::raw_retention::RawTransactionTombstone;
/// Canonical source-independent N1 RAW transaction persisted by Store backends.
pub use self::model::raw_transaction::RawTransaction;
/// Durable lifecycle status of one RAW transaction conflict case.
pub use self::model::raw_transaction::RawTransactionConflictStatus;
/// Persistable acquisition observation linked to one canonical RAW transaction.
pub use self::model::raw_transaction::RawTransactionObservation;
/// Durable backend-independent identity of one canonical RAW transaction.
pub use self::model::raw_transaction::RawTransactionReference;
/// Validated backend-neutral result of comparing canonical and incoming RAW transaction variants.
pub use self::model::raw_transaction::RawTransactionVariantComparison;
/// Stable non-zero Store identity of one persisted RAW transaction variant.
pub use self::model::raw_transaction::RawTransactionVariantId;
/// Origin of one persisted RAW transaction variant.
pub use self::model::raw_transaction::RawTransactionVariantOrigin;
/// Durable reference to one persisted RAW transaction variant.
pub use self::model::raw_transaction::RawTransactionVariantReference;
/// Backend-neutral quality relation between canonical and incoming RAW transaction variants.
pub use self::model::raw_transaction::RawTransactionVariantRelation;
/// Stable backend-neutral reason explaining one RAW transaction variant relation.
pub use self::model::raw_transaction::RawTransactionVariantRelationReason;
/// Common KSP error type used by Store-facing contracts.
pub use ksp_core_lib::Error;
/// Stable structured code identifying a KSP error category and condition.

View File

@@ -1,6 +1,5 @@
// file: crates/ksp-store-api/src/model/raw_outcome.rs
// version: 1
// version: 2
/// Outcome for one canonical RAW entity in an idempotent persistence operation.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -14,7 +13,6 @@ pub enum RawEntityWriteOutcome {
/// Normal persistence skipped a durable purged tombstone.
SkippedPurged,
}
/// Outcome for one deterministic acquisition observation write.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
@@ -26,14 +24,12 @@ pub enum RawObservationWriteOutcome {
/// 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]
@@ -46,10 +42,33 @@ impl RawAcquisitionWriteOutcome {
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;
}
}
/// Durable Store outcome for one RAW transaction acquisition after variant-aware convergence.
///
/// Every variant represents a successful Store-domain result. In particular,
/// [`Self::QuarantinedConflict`] is durable success and must not be reinterpreted as a
/// transport failure or terminal persistence error by callers.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawTransactionVariantWriteOutcome {
/// First durable variant became the initial canonical transaction.
InsertedCanonical,
/// Incoming content exactly matched an already durable variant/canonical representation.
ObservedExact,
/// Incoming content was durably observed while the current canonical variant remained strictly more complete.
ObservedCompatibleLessComplete,
/// Incoming content was durably persisted and atomically promoted because it was strictly more complete.
PromotedCompatibleMoreComplete,
/// Divergent or incomparable content was durably preserved without changing the current canonical variant.
QuarantinedConflict,
/// Explicit force-rehydration restored a previously purged logical transaction into variant-aware storage.
Rehydrated,
/// Normal acquisition respected an existing purged tombstone and performed no rehydration.
SkippedPurged,
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/model/raw_transaction.rs
// version: 1
// version: 2
/// Durable backend-independent identity of one canonical RAW transaction.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
@@ -7,7 +7,6 @@ pub struct RawTransactionReference {
network: crate::RawNetworkId,
signature: crate::RawTransactionSignature,
}
impl RawTransactionReference {
/// Creates one durable transaction identity from network and canonical Solana signature.
#[must_use]
@@ -20,7 +19,6 @@ impl RawTransactionReference {
pub fn network(&self) -> &crate::RawNetworkId {
return &self.network;
}
/// Returns the canonical transaction signature.
#[must_use]
pub const fn signature(&self) -> crate::RawTransactionSignature {
@@ -28,6 +26,195 @@ impl RawTransactionReference {
}
}
/// Stable non-zero Store identity of one persisted RAW transaction variant.
///
/// The identifier is a backend-neutral surrogate. It deliberately does not encode
/// `content_hash`, provider identity, canonicality or any physical database key layout.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RawTransactionVariantId(u64);
impl RawTransactionVariantId {
/// Creates one non-zero stable variant identifier.
pub fn try_new(value: u64) -> crate::Result<Self> {
if value == 0 {
return std::result::Result::Err(raw_variant_model_error("variant_id"));
}
return std::result::Result::Ok(Self(value));
}
/// Returns the opaque numeric surrogate unchanged.
#[must_use]
pub const fn get(&self) -> u64 {
return self.0;
}
}
/// Durable reference to one persisted RAW transaction variant.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct RawTransactionVariantReference {
transaction: crate::RawTransactionReference,
variant_id: crate::RawTransactionVariantId,
}
impl RawTransactionVariantReference {
/// Creates one durable variant reference from logical transaction identity and surrogate id.
#[must_use]
pub fn new(transaction: crate::RawTransactionReference, variant_id: crate::RawTransactionVariantId) -> Self {
return Self { transaction, variant_id };
}
/// Returns the logical transaction identity owning this variant.
#[must_use]
pub fn transaction(&self) -> &crate::RawTransactionReference {
return &self.transaction;
}
/// Returns the stable surrogate variant identifier.
#[must_use]
pub const fn variant_id(&self) -> crate::RawTransactionVariantId {
return self.variant_id;
}
}
/// Origin of one persisted RAW transaction variant.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawTransactionVariantOrigin {
/// Exact canonical RAW representation received from one acquisition source.
Native,
/// Explicitly synthesized canonical RAW representation derived from preserved parent variants.
Synthetic,
}
impl RawTransactionVariantOrigin {
/// Returns the stable persistence code for this origin.
#[must_use]
pub const fn code(&self) -> &'static str {
return match self {
Self::Native => "native",
Self::Synthetic => "synthetic",
};
}
}
/// Backend-neutral quality relation between the current canonical RAW transaction and one incoming variant.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawTransactionVariantRelation {
/// Canonical transaction content is exactly equal after exact comparison.
Exact,
/// Incoming content is compatible but provably less complete than the current canonical variant.
CompatibleLessComplete,
/// Incoming content is compatible and provably more complete than the current canonical variant.
CompatibleMoreComplete,
/// A canonical transaction invariant is contradicted by the incoming variant.
Conflict,
/// No contradiction must be asserted, but no safe dominance relation is proved.
Incomparable,
}
impl RawTransactionVariantRelation {
/// Returns the stable persistence/diagnostic code for this relation.
#[must_use]
pub const fn code(&self) -> &'static str {
return match self {
Self::Exact => "exact",
Self::CompatibleLessComplete => "compatible_less_complete",
Self::CompatibleMoreComplete => "compatible_more_complete",
Self::Conflict => "conflict",
Self::Incomparable => "incomparable",
};
}
}
/// Stable backend-neutral reason explaining one RAW transaction variant relation.
///
/// These codes describe Store-domain comparison evidence only. They do not encode a
/// provider, route, backend implementation or retry classification.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawTransactionVariantRelationReason {
/// Exact canonical transaction content equality was proved.
ExactCanonicalContent,
/// The incoming variant differs only by explicitly proved truncated `logMessages`.
IncomingLogMessagesTruncated,
/// The current canonical variant differs only by explicitly proved truncated `logMessages`.
CanonicalLogMessagesTruncated,
/// The transaction slot contradicts the current canonical transaction.
SlotMismatch,
/// The optional block timestamp contradicts the current canonical transaction.
BlockTimeMismatch,
/// Canonical RAW format identity/version differs and no safe equivalence contract applies.
PayloadFormatMismatch,
/// Canonical RAW payload content contains a proved contradiction.
CanonicalPayloadConflict,
/// Equal content digests were observed for canonical representations that are not byte-identical.
ContentHashCollision,
/// Canonical representations differ but the current comparator cannot prove equality, dominance or contradiction safely.
UnsupportedCanonicalDifference,
}
impl RawTransactionVariantRelationReason {
/// Returns the stable persistence/diagnostic code for this reason.
#[must_use]
pub const fn code(&self) -> &'static str {
return match self {
Self::ExactCanonicalContent => "exact_canonical_content",
Self::IncomingLogMessagesTruncated => "incoming_log_messages_truncated",
Self::CanonicalLogMessagesTruncated => "canonical_log_messages_truncated",
Self::SlotMismatch => "slot_mismatch",
Self::BlockTimeMismatch => "block_time_mismatch",
Self::PayloadFormatMismatch => "payload_format_mismatch",
Self::CanonicalPayloadConflict => "canonical_payload_conflict",
Self::ContentHashCollision => "content_hash_collision",
Self::UnsupportedCanonicalDifference => "unsupported_canonical_difference",
};
}
}
/// Validated backend-neutral result of comparing the current canonical transaction with one incoming variant.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RawTransactionVariantComparison {
reason: crate::RawTransactionVariantRelationReason,
relation: crate::RawTransactionVariantRelation,
}
impl RawTransactionVariantComparison {
/// Creates one relation/reason pair and rejects semantically inconsistent combinations.
pub fn try_new(relation: crate::RawTransactionVariantRelation, reason: crate::RawTransactionVariantRelationReason) -> crate::Result<Self> {
if !valid_variant_relation_reason(relation, reason) {
return std::result::Result::Err(raw_variant_model_error("relation_reason"));
}
return std::result::Result::Ok(Self { reason, relation });
}
/// Returns the stable reason code explaining the comparison.
#[must_use]
pub const fn reason(&self) -> crate::RawTransactionVariantRelationReason {
return self.reason;
}
/// Returns the backend-neutral quality relation.
#[must_use]
pub const fn relation(&self) -> crate::RawTransactionVariantRelation {
return self.relation;
}
}
/// Durable lifecycle status of one RAW transaction conflict case.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawTransactionConflictStatus {
/// At least one preserved variant still requires explicit or future automatic reconciliation.
Open,
/// The conflict currently has an explicit durable resolution while history remains preserved.
Resolved,
}
impl RawTransactionConflictStatus {
/// Returns the stable persistence code for this status.
#[must_use]
pub const fn code(&self) -> &'static str {
return match self {
Self::Open => "open",
Self::Resolved => "resolved",
};
}
}
/// Canonical source-independent N1 RAW transaction persisted by Store backends.
///
/// The payload must contain the complete KSP canonical transaction representation required
@@ -41,14 +228,12 @@ pub struct RawTransaction {
reference: crate::RawTransactionReference,
slot: u64,
}
impl RawTransaction {
/// Creates one complete canonical RAW transaction.
#[must_use]
pub fn new(reference: crate::RawTransactionReference, slot: u64, block_time: std::option::Option<crate::RawTimestamp>, payload: crate::RawPayload) -> Self {
return Self { block_time, payload, reference, slot };
}
/// Returns the optional canonical block timestamp.
#[must_use]
pub const fn block_time(&self) -> std::option::Option<crate::RawTimestamp> {
@@ -60,7 +245,6 @@ impl RawTransaction {
pub fn payload(&self) -> &crate::RawPayload {
return &self.payload;
}
/// Returns the durable backend-independent transaction identity.
#[must_use]
pub fn reference(&self) -> &crate::RawTransactionReference {
@@ -73,7 +257,6 @@ impl RawTransaction {
return self.slot;
}
}
/// Persistable acquisition observation linked to one canonical RAW transaction.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionObservation {
@@ -81,14 +264,12 @@ pub struct RawTransactionObservation {
provenance: crate::RawAcquisitionProvenance,
transaction: crate::RawTransactionReference,
}
impl RawTransactionObservation {
/// Creates one successful observation of a complete canonical RAW transaction.
#[must_use]
pub fn new(observation_key: crate::RawObservationKey, transaction: crate::RawTransactionReference, provenance: crate::RawAcquisitionProvenance) -> Self {
return Self { observation_key, provenance, transaction };
}
/// Returns the deterministic producer-owned observation idempotence key.
#[must_use]
pub const fn observation_key(&self) -> crate::RawObservationKey {
@@ -100,7 +281,6 @@ impl RawTransactionObservation {
pub fn provenance(&self) -> &crate::RawAcquisitionProvenance {
return &self.provenance;
}
/// Returns the durable transaction identity observed by this acquisition.
#[must_use]
pub fn transaction(&self) -> &crate::RawTransactionReference {
@@ -108,6 +288,30 @@ impl RawTransactionObservation {
}
}
fn raw_variant_model_error(field: &'static str) -> crate::Error {
return crate::Error::new(crate::ERROR_CODE_RAW_MODEL_INVALID, "invalid RAW transaction variant model").with_context("field", field);
}
fn valid_variant_relation_reason(relation: crate::RawTransactionVariantRelation, reason: crate::RawTransactionVariantRelationReason) -> bool {
return matches!(
(relation, reason),
(crate::RawTransactionVariantRelation::Exact, crate::RawTransactionVariantRelationReason::ExactCanonicalContent)
| (crate::RawTransactionVariantRelation::CompatibleLessComplete, crate::RawTransactionVariantRelationReason::IncomingLogMessagesTruncated)
| (crate::RawTransactionVariantRelation::CompatibleMoreComplete, crate::RawTransactionVariantRelationReason::CanonicalLogMessagesTruncated)
| (
crate::RawTransactionVariantRelation::Conflict,
crate::RawTransactionVariantRelationReason::SlotMismatch
| crate::RawTransactionVariantRelationReason::BlockTimeMismatch
| crate::RawTransactionVariantRelationReason::CanonicalPayloadConflict
| crate::RawTransactionVariantRelationReason::ContentHashCollision
)
| (
crate::RawTransactionVariantRelation::Incomparable,
crate::RawTransactionVariantRelationReason::PayloadFormatMismatch | crate::RawTransactionVariantRelationReason::UnsupportedCanonicalDifference
)
);
}
#[cfg(test)]
#[path = "../../unit_tests/model/raw_transaction.rs"]
mod tests;