From c83e3261d021132135cf9a9aebbb0529dfeaec5d Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Sat, 29 Aug 2026 08:45:21 +0200 Subject: [PATCH] v0.3.1-pre.005 --- Cargo.toml | 4 +- crates/ksp-store-api/src/capability.rs | 18 +- .../src/capability/raw_account.rs | 49 ++++++ .../src/capability/raw_transaction.rs | 50 ++++++ crates/ksp-store-api/src/lib.rs | 20 ++- .../tests/dependency_boundary.rs | 28 ++- .../ksp-store-api/tests/external_backend.rs | 128 ++++++++++++++ crates/ksp-store-api/tests/public_api.rs | 23 ++- deltas/0.3.1/pre.005.md | 165 ++++++++++++++++++ docs/plans/022-V0_3_1_STORE_RAW_PLAN.md | 56 ++++-- docs/validation/018-V0_3_1_STORE_RAW.md | 47 ++++- 11 files changed, 552 insertions(+), 36 deletions(-) create mode 100644 crates/ksp-store-api/src/capability/raw_account.rs create mode 100644 crates/ksp-store-api/src/capability/raw_transaction.rs create mode 100644 crates/ksp-store-api/tests/external_backend.rs create mode 100644 deltas/0.3.1/pre.005.md diff --git a/Cargo.toml b/Cargo.toml index 99c0534..664db63 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 326 +# version: 327 [workspace] resolver = "3" members = ["crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-store-api", "crates/ksp-wallet-lib"] [workspace.package] -version = "0.3.1-pre.4" +version = "0.3.1-pre.5" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-store-api/src/capability.rs b/crates/ksp-store-api/src/capability.rs index 4f6e562..35731c7 100644 --- a/crates/ksp-store-api/src/capability.rs +++ b/crates/ksp-store-api/src/capability.rs @@ -1,8 +1,18 @@ // file: crates/ksp-store-api/src/capability.rs -// version: 1 +// version: 2 //! Private home for backend-agnostic Store capability contracts. //! -//! `0.3.1-pre.002` establishes the ownership boundary only. Concrete read and -//! write capabilities are introduced after the RAW models they operate on are -//! defined; no backend/runtime contract belongs here. +//! Capabilities are split by persistent family and operation direction so a +//! backend can implement only the contracts it actually supports. The runtime +//! Store facade, backend selection and concrete database implementations remain +//! outside `ksp-store-api`. + +pub(crate) mod raw_account; +pub(crate) mod raw_transaction; + +/// Boxed async operation returned by object-safe Store capability contracts. +/// +/// The alias uses only standard-library primitives so backend implementations +/// need no async helper dependency merely to implement `ksp-store-api`. +pub type StoreApiFuture<'a, T> = std::pin::Pin + std::marker::Send + 'a>>; diff --git a/crates/ksp-store-api/src/capability/raw_account.rs b/crates/ksp-store-api/src/capability/raw_account.rs new file mode 100644 index 0000000..9d5fd07 --- /dev/null +++ b/crates/ksp-store-api/src/capability/raw_account.rs @@ -0,0 +1,49 @@ +// file: crates/ksp-store-api/src/capability/raw_account.rs +// version: 1 + +/// Read capability for complete canonical RAW account states. +/// +/// Implementations must return the common Store model without leaking backend +/// rows, SQL handles or acquisition transport types. Absence is represented by +/// `None`; backend/runtime failures use the common KSP error contract. +pub trait RawAccountStateRead: std::marker::Send + std::marker::Sync { + /// Reads one complete canonical RAW account state by durable reference. + fn get_raw_account_state<'a>( + &'a self, + reference: &'a crate::RawAccountStateReference, + ) -> crate::StoreApiFuture<'a, crate::Result>>; +} + +/// 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. +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<()>>; +} + +/// Read capability for persisted RAW account-state observations. +pub trait RawAccountObservationRead: std::marker::Send + std::marker::Sync { + /// Reads one account observation by deterministic producer-owned idempotence key. + fn get_raw_account_observation<'a>( + &'a self, + observation_key: &'a crate::RawObservationKey, + ) -> crate::StoreApiFuture<'a, crate::Result>>; +} + +/// 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`. +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<()>>; +} diff --git a/crates/ksp-store-api/src/capability/raw_transaction.rs b/crates/ksp-store-api/src/capability/raw_transaction.rs new file mode 100644 index 0000000..384214d --- /dev/null +++ b/crates/ksp-store-api/src/capability/raw_transaction.rs @@ -0,0 +1,50 @@ +// file: crates/ksp-store-api/src/capability/raw_transaction.rs +// version: 1 + +/// Read capability for canonical RAW transactions. +/// +/// Implementations must return the canonical Store model without exposing +/// backend rows, SQL handles or transport-specific DTOs. Absence is represented +/// by `None`; backend/runtime failures use the common KSP error contract. +pub trait RawTransactionRead: std::marker::Send + std::marker::Sync { + /// Reads one canonical RAW transaction by durable backend-independent reference. + fn get_raw_transaction<'a>( + &'a self, + reference: &'a crate::RawTransactionReference, + ) -> crate::StoreApiFuture<'a, crate::Result>>; +} + +/// 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. +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<()>>; +} + +/// Read capability for persisted RAW transaction observations. +pub trait RawTransactionObservationRead: std::marker::Send + std::marker::Sync { + /// Reads one transaction observation by deterministic producer-owned idempotence key. + fn get_raw_transaction_observation<'a>( + &'a self, + observation_key: &'a crate::RawObservationKey, + ) -> crate::StoreApiFuture<'a, crate::Result>>; +} + +/// Write capability for an additional observation of an already persisted RAW transaction. +/// +/// 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`. +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<()>>; +} diff --git a/crates/ksp-store-api/src/lib.rs b/crates/ksp-store-api/src/lib.rs index 32b8c4f..c5fdc08 100644 --- a/crates/ksp-store-api/src/lib.rs +++ b/crates/ksp-store-api/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-api/src/lib.rs -// version: 3 +// version: 4 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -20,6 +20,24 @@ mod capability; mod error; mod model; +/// Boxed async operation returned by object-safe Store capability contracts. +pub use self::capability::StoreApiFuture; +/// Read capability for persisted RAW account-state observations. +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 complete canonical RAW account states. +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 persisted RAW transaction observations. +pub use self::capability::raw_transaction::RawTransactionObservationRead; +/// Write capability for additional observations of already persisted RAW transactions. +pub use self::capability::raw_transaction::RawTransactionObservationWrite; +/// Read capability for canonical RAW transactions. +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 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. diff --git a/crates/ksp-store-api/tests/dependency_boundary.rs b/crates/ksp-store-api/tests/dependency_boundary.rs index 40745b8..0134f76 100644 --- a/crates/ksp-store-api/tests/dependency_boundary.rs +++ b/crates/ksp-store-api/tests/dependency_boundary.rs @@ -1,10 +1,10 @@ // file: crates/ksp-store-api/tests/dependency_boundary.rs -// version: 3 +// version: 4 //! Dependency canaries for the Store API RAW foundation. #[test] -fn pre_004_manifest_keeps_exact_core_only_runtime_dependency() { +fn pre_005_manifest_keeps_exact_core_only_runtime_dependency() { let manifest = include_str!("../Cargo.toml"); let dependencies_tail = manifest.split("[dependencies]").nth(1); assert!(dependencies_tail.is_some(), "Store API dependencies section must exist"); @@ -49,19 +49,24 @@ fn pre_004_manifest_keeps_exact_core_only_runtime_dependency() { } #[test] -fn pre_004_source_boundary_keeps_raw_models_passive_and_backend_free() { +fn pre_005_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_primitives = include_str!("../src/model/raw_primitives.rs"); let raw_transaction = include_str!("../src/model/raw_transaction.rs"); + let capability_home = include_str!("../src/capability.rs"); + let raw_account_capability = include_str!("../src/capability/raw_account.rs"); + let raw_transaction_capability = include_str!("../src/capability/raw_transaction.rs"); assert!(crate_root.contains("mod capability;")); assert!(crate_root.contains("mod error;")); assert!(crate_root.contains("mod model;")); assert!(model_home.contains("raw_account")); assert!(model_home.contains("raw_primitives")); assert!(model_home.contains("raw_transaction")); - for source in [crate_root, model_home, raw_account, raw_primitives, raw_transaction] { + assert!(capability_home.contains("raw_account")); + assert!(capability_home.contains("raw_transaction")); + for source in [crate_root, model_home, raw_account, raw_primitives, raw_transaction, capability_home, raw_account_capability, raw_transaction_capability] { for forbidden in [ "ksp_store_lib", "ksp_store_postgres_lib", @@ -80,7 +85,20 @@ fn pre_004_source_boundary_keeps_raw_models_passive_and_backend_free() { } assert!(!raw_transaction.contains("RawLog")); for forbidden in ["TransactionStatusObservation", "RawLogNotification", "RawSlotEvent", "RawVoteEvent", "RawBlock", "YellowstoneEntry"] { - assert!(!crate_root.contains(forbidden), "deferred pre.004 model leaked into Store API surface: {forbidden}"); + assert!(!crate_root.contains(forbidden), "deferred pre.005 model leaked into Store API surface: {forbidden}"); + } + assert!(raw_transaction_capability.contains("trait RawTransactionRead")); + assert!(raw_transaction_capability.contains("trait RawTransactionWrite")); + assert!(raw_transaction_capability.contains("trait RawTransactionObservationRead")); + assert!(raw_transaction_capability.contains("trait RawTransactionObservationWrite")); + assert!(raw_account_capability.contains("trait RawAccountStateRead")); + assert!(raw_account_capability.contains("trait RawAccountStateWrite")); + assert!(raw_account_capability.contains("trait RawAccountObservationRead")); + assert!(raw_account_capability.contains("trait RawAccountObservationWrite")); + for forbidden in ["trait StoreBackend", "trait Store", "PostgresStore", "MySqlStore", "Arc( + &'a self, + reference: &'a ksp_store_api::RawTransactionReference, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + let _ = reference; + return std::boxed::Box::pin(async { + return std::result::Result::Ok(std::option::Option::None); + }); + } +} + +impl ksp_store_api::RawTransactionWrite for ExternalMemoryBackend { + fn persist_raw_transaction_acquisition<'a>( + &'a self, + transaction: ksp_store_api::RawTransaction, + observation: ksp_store_api::RawTransactionObservation, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<()>> { + let _ = transaction; + let _ = observation; + return std::boxed::Box::pin(async { + return std::result::Result::Ok(()); + }); + } +} + +impl ksp_store_api::RawTransactionObservationRead for ExternalMemoryBackend { + fn get_raw_transaction_observation<'a>( + &'a self, + observation_key: &'a ksp_store_api::RawObservationKey, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + let _ = observation_key; + return std::boxed::Box::pin(async { + return std::result::Result::Ok(std::option::Option::None); + }); + } +} + +impl ksp_store_api::RawTransactionObservationWrite for ExternalMemoryBackend { + fn record_raw_transaction_observation<'a>( + &'a self, + observation: ksp_store_api::RawTransactionObservation, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<()>> { + let _ = observation; + return std::boxed::Box::pin(async { + return std::result::Result::Ok(()); + }); + } +} + +impl ksp_store_api::RawAccountStateRead for ExternalMemoryBackend { + fn get_raw_account_state<'a>( + &'a self, + reference: &'a ksp_store_api::RawAccountStateReference, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + let _ = reference; + return std::boxed::Box::pin(async { + return std::result::Result::Ok(std::option::Option::None); + }); + } +} + +impl ksp_store_api::RawAccountStateWrite for ExternalMemoryBackend { + fn persist_raw_account_acquisition<'a>( + &'a self, + state: ksp_store_api::RawAccountState, + observation: ksp_store_api::RawAccountObservation, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<()>> { + let _ = state; + let _ = observation; + return std::boxed::Box::pin(async { + return std::result::Result::Ok(()); + }); + } +} + +impl ksp_store_api::RawAccountObservationRead for ExternalMemoryBackend { + fn get_raw_account_observation<'a>( + &'a self, + observation_key: &'a ksp_store_api::RawObservationKey, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result>> { + let _ = observation_key; + return std::boxed::Box::pin(async { + return std::result::Result::Ok(std::option::Option::None); + }); + } +} + +impl ksp_store_api::RawAccountObservationWrite for ExternalMemoryBackend { + fn record_raw_account_observation<'a>( + &'a self, + observation: ksp_store_api::RawAccountObservation, + ) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<()>> { + let _ = observation; + return std::boxed::Box::pin(async { + return std::result::Result::Ok(()); + }); + } +} + +#[test] +fn pre_005_external_backend_implements_each_capability_without_store_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_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_observation_read: &dyn ksp_store_api::RawAccountObservationRead = &backend; + let account_observation_write: &dyn ksp_store_api::RawAccountObservationWrite = &backend; + let _ = transaction_read; + let _ = transaction_write; + let _ = transaction_observation_read; + let _ = transaction_observation_write; + let _ = account_read; + let _ = account_write; + let _ = account_observation_read; + let _ = account_observation_write; + return; +} diff --git a/crates/ksp-store-api/tests/public_api.rs b/crates/ksp-store-api/tests/public_api.rs index 17aabe2..cc9bdf8 100644 --- a/crates/ksp-store-api/tests/public_api.rs +++ b/crates/ksp-store-api/tests/public_api.rs @@ -1,5 +1,5 @@ // file: crates/ksp-store-api/tests/public_api.rs -// version: 3 +// version: 4 //! Integration canaries for the public `ksp-store-api` surface. @@ -125,3 +125,24 @@ fn public_pre_004_raw_account_state_and_observation_are_constructible_from_crate assert_eq!(ksp_store_api::MAX_RAW_ACCOUNT_DATA_BYTES, 16 * 1024 * 1024); return; } + +#[test] +fn public_pre_005_capabilities_are_available_from_crate_root_and_dyn_compatible() { + let transaction_read: std::option::Option<&dyn ksp_store_api::RawTransactionRead> = std::option::Option::None; + let transaction_write: std::option::Option<&dyn ksp_store_api::RawTransactionWrite> = std::option::Option::None; + let transaction_observation_read: std::option::Option<&dyn ksp_store_api::RawTransactionObservationRead> = std::option::Option::None; + let transaction_observation_write: std::option::Option<&dyn ksp_store_api::RawTransactionObservationWrite> = std::option::Option::None; + let account_read: std::option::Option<&dyn ksp_store_api::RawAccountStateRead> = std::option::Option::None; + let account_write: std::option::Option<&dyn ksp_store_api::RawAccountStateWrite> = std::option::Option::None; + let account_observation_read: std::option::Option<&dyn ksp_store_api::RawAccountObservationRead> = std::option::Option::None; + let account_observation_write: std::option::Option<&dyn ksp_store_api::RawAccountObservationWrite> = std::option::Option::None; + assert!(transaction_read.is_none()); + assert!(transaction_write.is_none()); + assert!(transaction_observation_read.is_none()); + assert!(transaction_observation_write.is_none()); + assert!(account_read.is_none()); + assert!(account_write.is_none()); + assert!(account_observation_read.is_none()); + assert!(account_observation_write.is_none()); + return; +} diff --git a/deltas/0.3.1/pre.005.md b/deltas/0.3.1/pre.005.md new file mode 100644 index 0000000..d1e7776 --- /dev/null +++ b/deltas/0.3.1/pre.005.md @@ -0,0 +1,165 @@ + + + +# Delta `0.3.1-pre.005` — capabilities backend extensibles + +## Base requise + +```text +0.3.1-pre.4 +``` + +Le gate opérateur de `pre.004` est vert : audits Rust/Markdown, `cargo check --workspace`, `cargo clippy --workspace --all-targets` et `cargo test -p ksp-store-api` passent. + +## Objectif + +Matérialiser les premiers contracts d'opérations backend-agnostic de `ksp-store-api` sans introduire la façade runtime `ksp-store-lib`, un backend concret ou une dépendance async supplémentaire. + +La tranche doit démontrer qu'un backend externe peut implémenter séparément les capabilities réellement supportées et être utilisé derrière des trait objects. + +## Fichiers ajoutés + +```text +crates/ksp-store-api/src/capability/raw_account.rs +crates/ksp-store-api/src/capability/raw_transaction.rs +crates/ksp-store-api/tests/external_backend.rs +deltas/0.3.1/pre.005.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +crates/ksp-store-api/src/capability.rs +crates/ksp-store-api/src/lib.rs +crates/ksp-store-api/tests/dependency_boundary.rs +crates/ksp-store-api/tests/public_api.rs +docs/plans/022-V0_3_1_STORE_RAW_PLAN.md +docs/validation/018-V0_3_1_STORE_RAW.md +``` + +## Fichiers supprimés + +```text +aucun +``` + +## Version + +```text +workspace.package.version = 0.3.1-pre.5 +``` + +## Surface ajoutée + +```text +StoreApiFuture<'a, T> + +RawTransactionRead +RawTransactionWrite +RawTransactionObservationRead +RawTransactionObservationWrite + +RawAccountStateRead +RawAccountStateWrite +RawAccountObservationRead +RawAccountObservationWrite +``` + +Aucun trait global `StoreBackend` n'est ajouté. Les backends peuvent implémenter une combinaison de capabilities sans prétendre supporter toutes les familles N1. + +## Contrat async/object-safe + +`StoreApiFuture<'a, T>` est un alias KSP utilisant uniquement la bibliothèque standard : + +```text +Pin + Send + 'a>> +``` + +Les capabilities sont `Send + Sync`, dyn-compatible et n'ajoutent aucune dépendance `async-trait`, `tokio` ou `futures-util` à `ksp-store-api`. + +## Contrats d'écriture + +Les opérations suivantes sont atomiques au niveau métier : + +```text +persist_raw_transaction_acquisition(transaction, observation) +persist_raw_account_acquisition(state, observation) +``` + +Le backend doit persister le RAW et son observation ensemble ou ne laisser aucun des deux durable. + +Les opérations : + +```text +record_raw_transaction_observation(observation) +record_raw_account_observation(observation) +``` + +servent aux acquisitions supplémentaires d'un RAW déjà présent et évitent de retransmettre le payload transactionnel ou les bytes de compte. + +`pre.005` retourne uniquement `Result<()>` sur les écritures. `pre.006` remplace/finalise cette surface avec les outcomes d'idempotence/conflit, les queries et le lifecycle RAW avant stabilisation de la release. + +## Canari backend externe + +`tests/external_backend.rs` définit un backend mémoire externe à l'implémentation Store officielle et implémente les huit capabilities uniquement via la façade crate-root de `ksp-store-api`. + +Le canari vérifie notamment : + +```text +impl externe sans ksp-store-lib +impl externe sans ksp-store-postgres-lib +aucun SQL/PostgreSQL +aucun async runtime requis pour implémenter les signatures +conversion en &dyn capability possible +``` + +## Validations exécutées dans l'environnement de génération + +```text +python3 scripts/audit_rust_workspace_rules.py +General Rust rule audit: clean +Rust export completeness audit: 0 candidate(s) +KSP workspace Rust rule audit: clean + +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas/0.3.1 +Markdown table audit: clean (186 table(s), 129 file(s)) +``` + +## Validations non exécutées dans l'environnement de génération + +`cargo`, `rustc` et `rustfmt` ne sont pas installés dans l'environnement de génération. L'opérateur doit donc exécuter : + +```text +cargo fmt --all +cargo check --workspace +cargo clippy --workspace --all-targets +cargo test -p ksp-store-api +``` + +Une commande non exécutée n'est pas déclarée PASS. + +## Hors scope confirmé + +```text +façade runtime ksp-store-lib +trait StoreBackend monolithique +ksp-store-postgres-lib +PostgreSQL/tokio-postgres +Config/std.store +queries/list/pagination +outcomes d'idempotence/conflit finaux +retention/tombstone +health runtime +TransactionStatusObservation commun +models event-only logs/slot/vote +RawBlock persistence +Yellowstone Entry persistence +N2 STRUCTURAL +N3 DECODED +N4 DOMAIN +``` + +## Suite + +`0.3.1-pre.006` finalise les queries/pages bornées, outcomes d'écriture/idempotence, contrat de backlog utile, lifecycle `RawRetentionState`, tombstone minimal et sémantiques normal-skip/force-rehydrate sans implémenter de compression/archive physique. diff --git a/docs/plans/022-V0_3_1_STORE_RAW_PLAN.md b/docs/plans/022-V0_3_1_STORE_RAW_PLAN.md index 11f23da..65cbaeb 100644 --- a/docs/plans/022-V0_3_1_STORE_RAW_PLAN.md +++ b/docs/plans/022-V0_3_1_STORE_RAW_PLAN.md @@ -1,5 +1,5 @@ - + # Plan `0.3.1` — Store API RAW foundation @@ -803,7 +803,7 @@ La stratégie candidate est : ```text capabilities Send + Sync méthodes object-safe -futures boxed KSP-owned via un alias StoreFuture<'a, T> +futures boxed KSP-owned via un alias StoreApiFuture<'a, T> future ksp-store-lib::Store compose les capabilities disponibles ``` @@ -813,21 +813,36 @@ Cette stratégie évite une dépendance `async-trait` uniquement pour masquer la Éviter un trait monolithique exigeant tous les types de données à chaque backend. -La candidate est une composition fine : +`pre.005` matérialise une composition fine par famille et par direction : ```text -StoreHealth RawTransactionRead RawTransactionWrite RawTransactionObservationRead RawTransactionObservationWrite -future RawAccountStateRead -future RawAccountStateWrite -future TransactionStatus* si persistence réellement retenue +RawAccountStateRead +RawAccountStateWrite +RawAccountObservationRead +RawAccountObservationWrite ``` -Un modèle event-only ne crée aucune capability Store par défaut. +Les capabilities d'écriture distinguent deux usages : + +```text +persist_raw_*_acquisition(raw, observation) + = création/admission atomique du RAW + observation + +record_raw_*_observation(observation) + = acquisition supplémentaire d'un RAW déjà persistant + = ne retransmet pas le payload volumineux +``` + +Les traits sont `Send + Sync`, dyn-compatible et retournent `StoreApiFuture<'a, T>`, alias KSP basé uniquement sur `Pin>`. Un backend externe peut donc les implémenter sans `async-trait`, `tokio`, `ksp-store-lib` ou crate backend officielle. + +`StoreHealth`, les listes/queries et les outcomes détaillés ne sont pas artificiellement introduits dans cette tranche. `pre.006` finalise les résultats d'écriture et le lifecycle logique avant fermeture de l'API. + +Un modèle event-only ne crée aucune capability Store par défaut. Aucun trait monolithique `StoreBackend` n'est introduit : un backend peut implémenter uniquement les familles réellement supportées. La future façade `ksp-store-lib::Store` peut exposer seulement les capabilities réellement compilées/supportées et produire une erreur stable lorsqu'une opération demandée n'est pas disponible. @@ -835,22 +850,25 @@ La future façade `ksp-store-lib::Store` peut exposer seulement les capabilities Aucun handle de transaction SQL/public n'est exposé. -Les invariants multi-écritures sont exprimés par des opérations logiques communes. Candidate initiale : +La surface matérialisée par `pre.005` commence par les opérations unitaires nécessaires : ```text -persist_transaction_acquisition(transaction, observation) -record_transaction_observation(observation) get_raw_transaction(reference) -list_raw_transactions(query, page) -list_transaction_observations(query, page) +persist_raw_transaction_acquisition(transaction, observation) +get_raw_transaction_observation(observation_key) +record_raw_transaction_observation(observation) -future persist_account_acquisition(state, observation) -future account reads/queries - -health() +get_raw_account_state(reference) +persist_raw_account_acquisition(state, observation) +get_raw_account_observation(observation_key) +record_raw_account_observation(observation) ``` -`persist_transaction_acquisition` signifie au contrat que le RAW et son observation réussissent atomiquement ou échouent ensemble. PostgreSQL réalisera cela avec une transaction privée en `0.3.2`; un autre backend utilisera son mécanisme natif. +Les opérations `persist_raw_*_acquisition` signifient au contrat que le RAW et son observation réussissent atomiquement ou échouent ensemble. PostgreSQL réalisera cela avec une transaction privée en `0.3.2`; un autre backend utilisera son mécanisme natif. + +Les opérations `record_raw_*_observation` supposent que la référence RAW ciblée existe déjà et permettent de retenir une acquisition supplémentaire sans retransmettre la donnée RAW complète. + +Les listes, queries, backlog, health commun et outcomes détaillés restent à finaliser dans `pre.006`; aucune transaction backend publique n'est nécessaire pour les exprimer. ### 11.5 Outcomes @@ -1375,7 +1393,7 @@ Audit HTTP/WS/gRPC matérialisé. `RawAccountState`/observation sont admis avec ### `pre.005` — Capabilities backend extensibles -Implémenter les contracts read/write object-safe et l'external backend canary, sans façade runtime `Store` et sans runtime DB. Un modèle prévu n'oblige pas chaque backend à implémenter sa capability. +Matérialiser les contracts read/write object-safe pour transaction/account et leurs observations, l'alias `StoreApiFuture`, ainsi qu'un canari backend externe. Les écritures d'acquisition imposent RAW + observation atomiques et les observations supplémentaires peuvent être enregistrées sans retransmettre le RAW. Aucune façade runtime `Store`, aucun trait monolithique backend et aucun runtime DB. ### `pre.006` — Queries, outcomes et lifecycle RAW diff --git a/docs/validation/018-V0_3_1_STORE_RAW.md b/docs/validation/018-V0_3_1_STORE_RAW.md index 25989e0..5ad30d9 100644 --- a/docs/validation/018-V0_3_1_STORE_RAW.md +++ b/docs/validation/018-V0_3_1_STORE_RAW.md @@ -1,5 +1,5 @@ - + # Validation `0.3.1` — Store API RAW foundation @@ -181,12 +181,12 @@ crate/test backend externe | status surfaces artificiellement fusionnées | aucun modèle commun prématuré | `pre.004` PASS | | duplicate same content | `AlreadyPresent` | `pre.006` | | duplicate divergent content | Conflict stable | `pre.006` | -| partial transaction+observation | interdit par atomic acquisition | `pre.005` | +| partial transaction+observation | interdit par atomic acquisition | `pre.005` PASS | | event-only -> Store capability | absent par défaut | `pre.004/007` | | Interface/Store duplicate model | absent | `pre.007` | | page limit 0/>500 | rejet | `pre.006` | | SQL/backend cursor leak | absent | `pre.006/007` | -| external backend | implémente API sans Store lib | `pre.005` | +| external backend | implémente API sans Store lib | `pre.005` PASS | | processing `bool` comme vérité | absent ; future preuve version-aware documentée | `pre.006/007` | | purge sans policy/evidence | impossible par contrat | `pre.006/007` | | tombstone supprimé avec payload | interdit | `pre.006` | @@ -268,6 +268,45 @@ jsonParsed/dataSlice/bare program -> non La conversion source -> modèle reste hors `ksp-store-api`; la crate ne dépend toujours que de `ksp-core-lib`. +### 8.3 Matérialisation `pre.005` + +La tranche ajoute uniquement des contracts de capability backend-agnostic : + +```text +StoreApiFuture<'a, T> + +RawTransactionRead +RawTransactionWrite +RawTransactionObservationRead +RawTransactionObservationWrite + +RawAccountStateRead +RawAccountStateWrite +RawAccountObservationRead +RawAccountObservationWrite +``` + +Gates matérialisés : + +```text +traits Send + Sync et dyn-compatible +aucune dépendance async-trait/tokio/futures-util ajoutée +backend externe implémentable avec std + ksp-store-api seulement +aucun trait StoreBackend monolithique +aucune façade runtime Store +aucun type Config/backend/SQL public +persist_raw_transaction_acquisition = transaction + observation atomiques +persist_raw_account_acquisition = account state + observation atomiques +record_*_observation = acquisition supplémentaire sans retransmettre le RAW +get_* = référence/observation key backend-independent +write success/failure seulement en pre.005 +outcomes/idempotence/conflict détaillés réservés à pre.006 +``` + +Le canari `tests/external_backend.rs` définit un backend mémoire externe qui implémente les huit traits sans dépendre de `ksp-store-lib`, PostgreSQL ou d'un runtime async. Il prouve également que chaque capability est utilisable derrière `dyn Trait`. + +Aucune implémentation de persistence n'est fournie par `ksp-store-api`; les futures concrètes du canari ne servent qu'à vérifier le contrat d'extension. + ## 9. Gates de fermeture prévus ### Gate technique final `pre.008` @@ -359,7 +398,7 @@ sécurité | `pre.002` | scaffold + taxonomie Store API | À FAIRE | | `pre.003` | primitives + RawTransaction | À FAIRE | | `pre.004` | admission matrix + account/status models | PRÊT après gate local | -| `pre.005` | backend contracts/capabilities | À FAIRE | +| `pre.005` | backend contracts/capabilities | PRÊT après gate local | | `pre.006` | queries/outcomes/retention/tombstone | À FAIRE | | `pre.007` | boundary/adversarial/completeness | À FAIRE | | `pre.008` | gate technique final | À FAIRE |