v0.3.1-pre.005
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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::boxed::Box<dyn std::future::Future<Output = T> + std::marker::Send + 'a>>;
|
||||
|
||||
49
crates/ksp-store-api/src/capability/raw_account.rs
Normal file
49
crates/ksp-store-api/src/capability/raw_account.rs
Normal file
@@ -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<std::option::Option<crate::RawAccountState>>>;
|
||||
}
|
||||
|
||||
/// 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<std::option::Option<crate::RawAccountObservation>>>;
|
||||
}
|
||||
|
||||
/// 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<()>>;
|
||||
}
|
||||
50
crates/ksp-store-api/src/capability/raw_transaction.rs
Normal file
50
crates/ksp-store-api/src/capability/raw_transaction.rs
Normal file
@@ -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<std::option::Option<crate::RawTransaction>>>;
|
||||
}
|
||||
|
||||
/// 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<std::option::Option<crate::RawTransactionObservation>>>;
|
||||
}
|
||||
|
||||
/// 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<()>>;
|
||||
}
|
||||
@@ -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.
|
||||
|
||||
@@ -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<dyn"] {
|
||||
assert!(!capability_home.contains(forbidden));
|
||||
assert!(!raw_account_capability.contains(forbidden));
|
||||
assert!(!raw_transaction_capability.contains(forbidden));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
128
crates/ksp-store-api/tests/external_backend.rs
Normal file
128
crates/ksp-store-api/tests/external_backend.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
// file: crates/ksp-store-api/tests/external_backend.rs
|
||||
// version: 1
|
||||
|
||||
//! External-implementation canary for object-safe Store API capabilities.
|
||||
|
||||
struct ExternalMemoryBackend;
|
||||
|
||||
impl ksp_store_api::RawTransactionRead for ExternalMemoryBackend {
|
||||
fn get_raw_transaction<'a>(
|
||||
&'a self,
|
||||
reference: &'a ksp_store_api::RawTransactionReference,
|
||||
) -> ksp_store_api::StoreApiFuture<'a, ksp_store_api::Result<std::option::Option<ksp_store_api::RawTransaction>>> {
|
||||
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<std::option::Option<ksp_store_api::RawTransactionObservation>>> {
|
||||
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<std::option::Option<ksp_store_api::RawAccountState>>> {
|
||||
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<std::option::Option<ksp_store_api::RawAccountObservation>>> {
|
||||
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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
165
deltas/0.3.1/pre.005.md
Normal file
165
deltas/0.3.1/pre.005.md
Normal file
@@ -0,0 +1,165 @@
|
||||
<!-- file: deltas/0.3.1/pre.005.md -->
|
||||
<!-- version: 1 -->
|
||||
|
||||
# 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<Box<dyn Future<Output = T> + 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.
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/plans/022-V0_3_1_STORE_RAW_PLAN.md -->
|
||||
<!-- version: 5 -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# 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<Box<dyn Future + Send>>`. 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
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!-- file: docs/validation/018-V0_3_1_STORE_RAW.md -->
|
||||
<!-- version: 5 -->
|
||||
<!-- version: 6 -->
|
||||
|
||||
# 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 |
|
||||
|
||||
Reference in New Issue
Block a user