v0.3.1-pre.003

This commit is contained in:
2026-08-29 07:51:09 +02:00
parent 11e4cde77f
commit ecc5607fd4
13 changed files with 1274 additions and 34 deletions

View File

@@ -0,0 +1,9 @@
// file: crates/ksp-store-api/src/error.rs
// version: 1
/// 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");

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-store-api/src/lib.rs
// version: 1
// version: 2
#![warn(missing_docs)]
#![deny(unreachable_pub)]
@@ -17,8 +17,49 @@
//! dispatch remain outside this crate.
mod capability;
mod error;
mod model;
/// 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;
/// Maximum UTF-8 byte length accepted for one safe logical RAW/provenance code.
pub use self::model::raw_primitives::MAX_RAW_CODE_BYTES;
/// Maximum KSP-owned canonical RAW payload admitted by the Store API.
pub use self::model::raw_primitives::MAX_RAW_PAYLOAD_BYTES;
/// Maximum source-wire payload size recorded as acquisition metadata.
pub use self::model::raw_primitives::MAX_RAW_SOURCE_PAYLOAD_BYTES;
/// Maximum supported Unix millisecond timestamp.
pub use self::model::raw_primitives::MAX_RAW_UNIX_MILLIS;
/// Origin category describing why one acquisition was performed.
pub use self::model::raw_primitives::RawAcquisitionOrigin;
/// Safe source-independent acquisition provenance attached to one persisted observation.
pub use self::model::raw_primitives::RawAcquisitionProvenance;
/// Fixed-size digest identifying canonical or source bytes without retaining them.
pub use self::model::raw_primitives::RawContentHash;
/// Bounded identifier of one KSP-owned source-independent RAW persistence format.
pub use self::model::raw_primitives::RawFormatId;
/// Bounded logical network/cluster identifier used in backend-independent Store identities.
pub use self::model::raw_primitives::RawNetworkId;
/// Stable deterministic idempotence key for one persisted acquisition observation.
pub use self::model::raw_primitives::RawObservationKey;
/// Bounded source-independent KSP RAW persistence payload.
pub use self::model::raw_primitives::RawPayload;
/// Bounded logical code used by acquisition provenance fields.
pub use self::model::raw_primitives::RawProvenanceCode;
/// Bounded UTC timestamp represented as whole milliseconds since Unix epoch.
pub use self::model::raw_primitives::RawTimestamp;
/// Canonical 64-byte Solana transaction signature used by Store identities.
pub use self::model::raw_primitives::RawTransactionSignature;
/// 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.
pub use self::model::raw_transaction::RawTransactionObservation;
/// Durable backend-independent identity of one canonical RAW transaction.
pub use self::model::raw_transaction::RawTransactionReference;
/// 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,9 +1,12 @@
// file: crates/ksp-store-api/src/model.rs
// version: 1
// version: 2
//! Private home for persistent Store models.
//!
//! The current release is N1 RAW-only. Future N2 STRUCTURAL models are a
//! separate data layer and are not introduced by this scaffold. Persistent
//! models and backend capabilities remain separate even when one capability
//! operates on one or more models.
//! separate data layer and are not introduced here. Persistent models and
//! backend capabilities remain separate even when one capability operates on
//! one or more models.
pub(crate) mod raw_primitives;
pub(crate) mod raw_transaction;

View File

@@ -0,0 +1,449 @@
// file: crates/ksp-store-api/src/model/raw_primitives.rs
// version: 1
/// Maximum UTF-8 byte length accepted for one safe logical RAW/provenance code.
pub const MAX_RAW_CODE_BYTES: usize = 128;
/// Maximum KSP-owned canonical RAW payload admitted by the Store API.
///
/// This is a Store admission guard, not a Solana protocol-size claim.
pub const MAX_RAW_PAYLOAD_BYTES: usize = 16 * 1024 * 1024;
/// Maximum source-wire payload size recorded as acquisition metadata.
///
/// The source payload itself is never retained by this metadata field.
pub const MAX_RAW_SOURCE_PAYLOAD_BYTES: u64 = 64 * 1024 * 1024;
/// Maximum supported Unix millisecond timestamp (`9999-12-31T23:59:59.999Z`).
pub const MAX_RAW_UNIX_MILLIS: u64 = 253_402_300_799_999;
/// Fixed-size digest identifying canonical or source bytes without retaining them.
#[derive(Clone, Copy, Eq, Hash, PartialEq)]
pub struct RawContentHash([u8; 32]);
impl RawContentHash {
/// Creates one opaque 32-byte KSP content digest.
#[must_use]
pub const fn new(bytes: [u8; 32]) -> Self {
return Self(bytes);
}
/// Returns the exact digest bytes.
#[must_use]
pub const fn as_bytes(&self) -> &[u8; 32] {
return &self.0;
}
}
impl std::fmt::Debug for RawContentHash {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.write_str("RawContentHash(..)");
}
}
/// Stable deterministic idempotence key for one persisted acquisition observation.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct RawObservationKey([u8; 32]);
impl RawObservationKey {
/// Creates one producer-owned deterministic observation key.
#[must_use]
pub const fn new(bytes: [u8; 32]) -> Self {
return Self(bytes);
}
/// Returns the exact observation-key bytes.
#[must_use]
pub const fn as_bytes(&self) -> &[u8; 32] {
return &self.0;
}
}
/// Canonical 64-byte Solana transaction signature used by Store identities.
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RawTransactionSignature([u8; 64]);
impl RawTransactionSignature {
/// Creates one canonical signature from already-decoded Solana signature bytes.
#[must_use]
pub const fn new(bytes: [u8; 64]) -> Self {
return Self(bytes);
}
/// Returns the exact signature bytes.
#[must_use]
pub const fn as_bytes(&self) -> &[u8; 64] {
return &self.0;
}
}
impl std::fmt::Debug for RawTransactionSignature {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter.write_str("RawTransactionSignature(..)");
}
}
/// Bounded logical network/cluster identifier used in backend-independent Store identities.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RawNetworkId(std::string::String);
impl RawNetworkId {
/// Creates one safe non-empty network/cluster identifier.
pub fn new(value: impl std::convert::Into<std::string::String>) -> ksp_core_lib::Result<Self> {
let value = value.into();
if !valid_raw_code(value.as_str()) {
return std::result::Result::Err(raw_model_error("network"));
}
return std::result::Result::Ok(Self(value));
}
/// Returns the network/cluster identifier.
#[must_use]
pub fn as_str(&self) -> &str {
return self.0.as_str();
}
}
/// Bounded logical code used by acquisition provenance fields.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RawProvenanceCode(std::string::String);
impl RawProvenanceCode {
/// Creates one safe non-empty provenance code.
pub fn new(value: impl std::convert::Into<std::string::String>) -> ksp_core_lib::Result<Self> {
let value = value.into();
if !valid_raw_code(value.as_str()) {
return std::result::Result::Err(raw_provenance_error("code"));
}
return std::result::Result::Ok(Self(value));
}
/// Returns the validated provenance code.
#[must_use]
pub fn as_str(&self) -> &str {
return self.0.as_str();
}
}
/// Bounded identifier of one KSP-owned source-independent RAW persistence format.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RawFormatId(std::string::String);
impl RawFormatId {
/// Creates one safe non-empty RAW format identifier.
pub fn new(value: impl std::convert::Into<std::string::String>) -> ksp_core_lib::Result<Self> {
let value = value.into();
if !valid_raw_code(value.as_str()) {
return std::result::Result::Err(raw_payload_error("format_id"));
}
return std::result::Result::Ok(Self(value));
}
/// Returns the KSP-owned RAW format identifier.
#[must_use]
pub fn as_str(&self) -> &str {
return self.0.as_str();
}
}
/// Bounded UTC timestamp represented as whole milliseconds since Unix epoch.
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RawTimestamp {
unix_millis: u64,
}
impl RawTimestamp {
/// Creates a bounded UTC timestamp from Unix milliseconds.
pub fn from_unix_millis(unix_millis: u64) -> ksp_core_lib::Result<Self> {
if unix_millis > crate::MAX_RAW_UNIX_MILLIS {
return std::result::Result::Err(raw_model_error("timestamp"));
}
return std::result::Result::Ok(Self { unix_millis });
}
/// Returns whole milliseconds since Unix epoch.
#[must_use]
pub const fn unix_millis(&self) -> u64 {
return self.unix_millis;
}
}
/// Origin category describing why one acquisition was performed.
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum RawAcquisitionOrigin {
/// Historical acquisition intended to fill missing durable data.
Backfill,
/// Explicit import from a non-live source controlled by the caller.
Import,
/// Live acquisition from a currently active transport/session.
Live,
/// Explicit repair or reconciliation of previously known data.
Repair,
/// Explicit replay of an already-known source or archived acquisition.
Replay,
}
/// Safe source-independent acquisition provenance attached to one persisted observation.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawAcquisitionProvenance {
acquisition_method: crate::RawProvenanceCode,
capture_session_id: std::option::Option<crate::RawProvenanceCode>,
commitment: std::option::Option<crate::RawProvenanceCode>,
endpoint_id: std::option::Option<crate::RawProvenanceCode>,
filter_id: std::option::Option<crate::RawProvenanceCode>,
observed_at: std::option::Option<crate::RawTimestamp>,
origin: crate::RawAcquisitionOrigin,
protocol: crate::RawProvenanceCode,
provider: crate::RawProvenanceCode,
received_at: crate::RawTimestamp,
source_payload_hash: std::option::Option<crate::RawContentHash>,
source_payload_size_bytes: std::option::Option<u64>,
}
impl RawAcquisitionProvenance {
/// Creates one successful acquisition provenance record with only mandatory safe metadata.
#[must_use]
pub fn new(
provider: crate::RawProvenanceCode,
protocol: crate::RawProvenanceCode,
acquisition_method: crate::RawProvenanceCode,
origin: crate::RawAcquisitionOrigin,
received_at: crate::RawTimestamp,
) -> Self {
return Self {
acquisition_method,
capture_session_id: std::option::Option::None,
commitment: std::option::Option::None,
endpoint_id: std::option::Option::None,
filter_id: std::option::Option::None,
observed_at: std::option::Option::None,
origin,
protocol,
provider,
received_at,
source_payload_hash: std::option::Option::None,
source_payload_size_bytes: std::option::Option::None,
};
}
/// Attaches one safe logical capture/session identifier.
#[must_use]
pub fn with_capture_session_id(mut self, value: crate::RawProvenanceCode) -> Self {
self.capture_session_id = std::option::Option::Some(value);
return self;
}
/// Attaches one safe commitment code captured at acquisition.
#[must_use]
pub fn with_commitment(mut self, value: crate::RawProvenanceCode) -> Self {
self.commitment = std::option::Option::Some(value);
return self;
}
/// Attaches one Config-owned logical endpoint identifier.
#[must_use]
pub fn with_endpoint_id(mut self, value: crate::RawProvenanceCode) -> Self {
self.endpoint_id = std::option::Option::Some(value);
return self;
}
/// Attaches one safe logical filter identifier.
#[must_use]
pub fn with_filter_id(mut self, value: crate::RawProvenanceCode) -> Self {
self.filter_id = std::option::Option::Some(value);
return self;
}
/// Attaches the source observation timestamp when it does not follow local receipt.
pub fn try_with_observed_at(mut self, value: crate::RawTimestamp) -> ksp_core_lib::Result<Self> {
if value > self.received_at {
return std::result::Result::Err(raw_provenance_error("observed_at"));
}
self.observed_at = std::option::Option::Some(value);
return std::result::Result::Ok(self);
}
/// Attaches the digest of source-specific bytes without retaining those bytes.
#[must_use]
pub fn with_source_payload_hash(mut self, value: crate::RawContentHash) -> Self {
self.source_payload_hash = std::option::Option::Some(value);
return self;
}
/// Attaches the bounded source-wire payload size.
pub fn try_with_source_payload_size_bytes(mut self, value: u64) -> ksp_core_lib::Result<Self> {
if value > crate::MAX_RAW_SOURCE_PAYLOAD_BYTES {
return std::result::Result::Err(raw_provenance_error("source_payload_size_bytes"));
}
self.source_payload_size_bytes = std::option::Option::Some(value);
return std::result::Result::Ok(self);
}
/// Returns the logical acquisition method code.
#[must_use]
pub const fn acquisition_method(&self) -> &crate::RawProvenanceCode {
return &self.acquisition_method;
}
/// Returns the optional logical capture/session identifier.
#[must_use]
pub fn capture_session_id(&self) -> std::option::Option<&crate::RawProvenanceCode> {
return self.capture_session_id.as_ref();
}
/// Returns the optional commitment code captured at acquisition.
#[must_use]
pub fn commitment(&self) -> std::option::Option<&crate::RawProvenanceCode> {
return self.commitment.as_ref();
}
/// Returns the optional Config-owned logical endpoint identifier.
#[must_use]
pub fn endpoint_id(&self) -> std::option::Option<&crate::RawProvenanceCode> {
return self.endpoint_id.as_ref();
}
/// Returns the optional logical filter identifier.
#[must_use]
pub fn filter_id(&self) -> std::option::Option<&crate::RawProvenanceCode> {
return self.filter_id.as_ref();
}
/// Returns the optional source observation timestamp when the source supplies one.
#[must_use]
pub const fn observed_at(&self) -> std::option::Option<crate::RawTimestamp> {
return self.observed_at;
}
/// Returns the acquisition origin category.
#[must_use]
pub const fn origin(&self) -> crate::RawAcquisitionOrigin {
return self.origin;
}
/// Returns the logical transport/protocol code.
#[must_use]
pub fn protocol(&self) -> &crate::RawProvenanceCode {
return &self.protocol;
}
/// Returns the safe provider code.
#[must_use]
pub fn provider(&self) -> &crate::RawProvenanceCode {
return &self.provider;
}
/// Returns the local receipt timestamp.
#[must_use]
pub const fn received_at(&self) -> crate::RawTimestamp {
return self.received_at;
}
/// Returns the optional digest of source-specific bytes without retaining those bytes.
#[must_use]
pub const fn source_payload_hash(&self) -> std::option::Option<crate::RawContentHash> {
return self.source_payload_hash;
}
/// Returns the optional source-wire payload size.
#[must_use]
pub const fn source_payload_size_bytes(&self) -> std::option::Option<u64> {
return self.source_payload_size_bytes;
}
}
/// Bounded source-independent KSP RAW persistence payload.
pub struct RawPayload {
bytes: std::boxed::Box<[u8]>,
content_hash: crate::RawContentHash,
format_id: crate::RawFormatId,
format_version: u32,
}
impl RawPayload {
/// Creates one canonical RAW payload after enforcing Store-owned admission invariants.
///
/// The supplied bytes must already use the KSP-owned source-independent format identified
/// by `format_id` and `format_version`; this constructor performs no transport conversion.
pub fn try_new(
format_id: crate::RawFormatId,
format_version: u32,
bytes: std::boxed::Box<[u8]>,
content_hash: crate::RawContentHash,
) -> ksp_core_lib::Result<Self> {
if format_version == 0 {
return std::result::Result::Err(raw_payload_error("format_version"));
}
if bytes.is_empty() || bytes.len() > crate::MAX_RAW_PAYLOAD_BYTES {
return std::result::Result::Err(
raw_payload_error("bytes")
.with_context("actual_len", bytes.len().to_string())
.with_context("maximum_len", crate::MAX_RAW_PAYLOAD_BYTES.to_string()),
);
}
return std::result::Result::Ok(Self { bytes, content_hash, format_id, format_version });
}
/// Returns the canonical RAW bytes without transport/provider interpretation.
#[must_use]
pub fn bytes(&self) -> &[u8] {
return self.bytes.as_ref();
}
/// Returns the deterministic content digest supplied for these exact canonical bytes.
#[must_use]
pub const fn content_hash(&self) -> crate::RawContentHash {
return self.content_hash;
}
/// Returns the KSP-owned source-independent format identifier.
#[must_use]
pub fn format_id(&self) -> &crate::RawFormatId {
return &self.format_id;
}
/// Returns the KSP-owned format version.
#[must_use]
pub const fn format_version(&self) -> u32 {
return self.format_version;
}
/// Returns the canonical payload length in bytes.
#[must_use]
pub fn byte_len(&self) -> usize {
return self.bytes.len();
}
}
impl std::fmt::Debug for RawPayload {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
return formatter
.debug_struct("RawPayload")
.field("format_id", &self.format_id)
.field("format_version", &self.format_version)
.field("len", &self.bytes.len())
.field("content_hash", &self.content_hash)
.finish();
}
}
fn raw_model_error(field: &'static str) -> ksp_core_lib::Error {
return ksp_core_lib::Error::new(crate::ERROR_CODE_RAW_MODEL_INVALID, "invalid backend-agnostic RAW Store model").with_context("field", field);
}
fn raw_payload_error(field: &'static str) -> ksp_core_lib::Error {
return ksp_core_lib::Error::new(crate::ERROR_CODE_RAW_PAYLOAD_INVALID, "invalid KSP RAW persistence payload").with_context("field", field);
}
fn raw_provenance_error(field: &'static str) -> ksp_core_lib::Error {
return ksp_core_lib::Error::new(crate::ERROR_CODE_RAW_PROVENANCE_INVALID, "invalid RAW acquisition provenance").with_context("field", field);
}
fn valid_raw_code(value: &str) -> bool {
if value.is_empty() || value.len() > crate::MAX_RAW_CODE_BYTES {
return false;
}
return value.bytes().all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'_' | b'-' | b'.' | b':'));
}
#[cfg(test)]
#[path = "../../unit_tests/model/raw_primitives.rs"]
mod tests;

View File

@@ -0,0 +1,113 @@
// file: crates/ksp-store-api/src/model/raw_transaction.rs
// version: 1
/// Durable backend-independent identity of one canonical RAW transaction.
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct RawTransactionReference {
network: crate::RawNetworkId,
signature: crate::RawTransactionSignature,
}
impl RawTransactionReference {
/// Creates one durable transaction identity from network and canonical Solana signature.
#[must_use]
pub fn new(network: crate::RawNetworkId, signature: crate::RawTransactionSignature) -> Self {
return Self { network, signature };
}
/// Returns the logical Solana network/cluster identifier.
#[must_use]
pub fn network(&self) -> &crate::RawNetworkId {
return &self.network;
}
/// Returns the canonical transaction signature.
#[must_use]
pub const fn signature(&self) -> crate::RawTransactionSignature {
return self.signature;
}
}
/// Canonical source-independent N1 RAW transaction persisted by Store backends.
///
/// The payload must contain the complete KSP canonical transaction representation required
/// for future STRUCTURAL replay, including transaction execution metadata and transaction
/// log messages when the canonical format defines them. Provider/transport provenance is
/// deliberately kept in [`crate::RawTransactionObservation`] instead.
#[derive(Debug)]
pub struct RawTransaction {
block_time: std::option::Option<crate::RawTimestamp>,
payload: crate::RawPayload,
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> {
return self.block_time;
}
/// Returns the complete KSP-owned canonical RAW payload.
#[must_use]
pub fn payload(&self) -> &crate::RawPayload {
return &self.payload;
}
/// Returns the durable backend-independent transaction identity.
#[must_use]
pub fn reference(&self) -> &crate::RawTransactionReference {
return &self.reference;
}
/// Returns the Solana slot containing the transaction.
#[must_use]
pub const fn slot(&self) -> u64 {
return self.slot;
}
}
/// Persistable acquisition observation linked to one canonical RAW transaction.
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct RawTransactionObservation {
observation_key: crate::RawObservationKey,
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 {
return self.observation_key;
}
/// Returns safe source-independent acquisition provenance.
#[must_use]
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 {
return &self.transaction;
}
}
#[cfg(test)]
#[path = "../../unit_tests/model/raw_transaction.rs"]
mod tests;

View File

@@ -1,10 +1,10 @@
// file: crates/ksp-store-api/tests/dependency_boundary.rs
// version: 1
// version: 2
//! Dependency canaries for the Store API scaffold.
//! Dependency canaries for the Store API RAW foundation.
#[test]
fn pre_002_manifest_has_exact_core_only_runtime_dependency() {
fn pre_003_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,26 +49,34 @@ fn pre_002_manifest_has_exact_core_only_runtime_dependency() {
}
#[test]
fn pre_002_source_boundary_separates_models_capabilities_and_backend_runtime() {
fn pre_003_source_boundary_keeps_raw_models_passive_and_backend_free() {
let crate_root = include_str!("../src/lib.rs");
let model_home = include_str!("../src/model.rs");
let raw_primitives = include_str!("../src/model/raw_primitives.rs");
let raw_transaction = include_str!("../src/model/raw_transaction.rs");
assert!(crate_root.contains("mod capability;"));
assert!(crate_root.contains("mod error;"));
assert!(crate_root.contains("mod model;"));
assert!(!crate_root.contains("pub mod "));
for forbidden in [
"ksp_store_lib",
"ksp_store_postgres_lib",
"ksp_onchain_transport_lib",
"ksp_program_api",
"serde::",
"sqlx::",
"tokio::",
"tokio_postgres::",
"std::env::",
"std::fs::",
"std::net::",
] {
assert!(!crate_root.contains(forbidden), "forbidden Store API crate-root dependency/surface detected: {forbidden}");
assert!(model_home.contains("raw_primitives"));
assert!(model_home.contains("raw_transaction"));
for source in [crate_root, model_home, raw_primitives, raw_transaction] {
for forbidden in [
"ksp_store_lib",
"ksp_store_postgres_lib",
"ksp_onchain_transport_lib",
"ksp_program_api",
"serde::",
"sqlx::",
"tokio::",
"tokio_postgres::",
"std::env::",
"std::fs::",
"std::net::",
] {
assert!(!source.contains(forbidden), "forbidden Store API dependency/runtime path detected: {forbidden}");
}
}
assert!(!raw_transaction.contains("RawLog"));
return;
}

View File

@@ -1,14 +1,21 @@
// file: crates/ksp-store-api/tests/public_api.rs
// version: 1
// version: 2
//! Integration canaries for the public `ksp-store-api` scaffold.
//! Integration canaries for the public `ksp-store-api` surface.
fn consume_result(value: ksp_store_api::Result<ksp_store_api::Pubkey>) -> ksp_store_api::Result<ksp_store_api::Pubkey> {
return value;
}
fn code(value: &str) -> std::option::Option<ksp_store_api::RawProvenanceCode> {
return match ksp_store_api::RawProvenanceCode::new(value.to_owned()) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
#[test]
fn public_pre_002_core_facade_is_available_from_crate_root() {
fn public_pre_002_core_facade_remains_available_from_crate_root() {
let pubkey = ksp_store_api::Pubkey::new_from_array([0x31_u8; 32]);
let forwarded = consume_result(std::result::Result::Ok(pubkey));
assert!(forwarded.is_ok());
@@ -22,11 +29,54 @@ fn public_pre_002_core_facade_is_available_from_crate_root() {
}
#[test]
fn public_pre_002_scaffold_exposes_no_private_module_paths_or_backend_types() {
fn public_pre_003_raw_transaction_and_observation_are_constructible_from_crate_root() {
let network = match ksp_store_api::RawNetworkId::new("mainnet-beta".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let reference = ksp_store_api::RawTransactionReference::new(network, ksp_store_api::RawTransactionSignature::new([1_u8; 64]));
let format = match ksp_store_api::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let payload = match ksp_store_api::RawPayload::try_new(format, 1, vec![1_u8, 2_u8, 3_u8].into_boxed_slice(), ksp_store_api::RawContentHash::new([2_u8; 32]))
{
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let transaction = ksp_store_api::RawTransaction::new(reference.clone(), 123, std::option::Option::None, payload);
let received_at = match ksp_store_api::RawTimestamp::from_unix_millis(1_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let provider = match code("provider") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let protocol = match code("solana_http") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let method = match code("getTransaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let provenance = ksp_store_api::RawAcquisitionProvenance::new(provider, protocol, method, ksp_store_api::RawAcquisitionOrigin::Backfill, received_at);
let observation = ksp_store_api::RawTransactionObservation::new(ksp_store_api::RawObservationKey::new([3_u8; 32]), reference, provenance);
assert_eq!(transaction.slot(), 123);
assert_eq!(transaction.payload().bytes(), &[1_u8, 2_u8, 3_u8]);
assert_eq!(observation.transaction().network().as_str(), "mainnet-beta");
assert_eq!(observation.provenance().acquisition_method().as_str(), "getTransaction");
assert_eq!(ksp_store_api::ERROR_CODE_RAW_PAYLOAD_INVALID.domain(), "store_api");
return;
}
#[test]
fn public_pre_003_surface_keeps_backend_and_structural_types_out() {
let source = include_str!("../src/lib.rs");
assert!(!source.contains("pub mod "));
for forbidden in ["Postgres", "Sql", "Migration", "ProgramInstruction", "RawTransaction"] {
assert!(!source.contains(forbidden), "forbidden pre.002 Store API public concept detected: {forbidden}");
for forbidden in ["Postgres", "Sql", "Migration", "StructuralTransaction", "RawLog", "ProgramInstruction"] {
assert!(!source.contains(forbidden), "forbidden pre.003 Store API public concept detected: {forbidden}");
}
return;
}

View File

@@ -0,0 +1,197 @@
// file: crates/ksp-store-api/unit_tests/model/raw_primitives.rs
// version: 1
fn code(value: &str) -> std::option::Option<crate::RawProvenanceCode> {
return match crate::RawProvenanceCode::new(value.to_owned()) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
fn timestamp(unix_millis: u64) -> std::option::Option<crate::RawTimestamp> {
return match crate::RawTimestamp::from_unix_millis(unix_millis) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
#[test]
fn raw_codes_are_bounded_nonempty_and_reject_url_like_or_control_values() {
let network = crate::RawNetworkId::new("mainnet-beta".to_owned());
assert!(network.is_ok());
let format = crate::RawFormatId::new("ksp.solana.raw_transaction".to_owned());
assert!(format.is_ok());
let maximum = "a".repeat(crate::MAX_RAW_CODE_BYTES);
assert!(crate::RawProvenanceCode::new(maximum).is_ok());
assert!(crate::RawProvenanceCode::new(std::string::String::new()).is_err());
assert!(crate::RawProvenanceCode::new("bad value".to_owned()).is_err());
assert!(crate::RawProvenanceCode::new("https://secret.example".to_owned()).is_err());
assert!(crate::RawProvenanceCode::new("bad\nvalue".to_owned()).is_err());
assert!(crate::RawProvenanceCode::new("a".repeat(crate::MAX_RAW_CODE_BYTES + 1)).is_err());
return;
}
#[test]
fn raw_timestamp_accepts_documented_maximum_and_rejects_larger_values() {
let maximum = crate::RawTimestamp::from_unix_millis(crate::MAX_RAW_UNIX_MILLIS);
assert!(maximum.is_ok());
let too_large = crate::RawTimestamp::from_unix_millis(crate::MAX_RAW_UNIX_MILLIS + 1);
assert!(too_large.is_err());
return;
}
#[test]
fn raw_payload_is_nonempty_versioned_bounded_and_debug_omits_bytes() {
let format_result = crate::RawFormatId::new("ksp.solana.raw_transaction".to_owned());
assert!(format_result.is_ok());
let format = match format_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let sentinel = b"RAW_PAYLOAD_SENTINEL_NEVER_RENDER".to_vec().into_boxed_slice();
let payload_result = crate::RawPayload::try_new(format, 1, sentinel, crate::RawContentHash::new([7_u8; 32]));
assert!(payload_result.is_ok());
let payload = match payload_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(payload.bytes(), b"RAW_PAYLOAD_SENTINEL_NEVER_RENDER");
assert_eq!(payload.byte_len(), b"RAW_PAYLOAD_SENTINEL_NEVER_RENDER".len());
assert_eq!(payload.format_version(), 1);
let debug = format!("{payload:?}");
assert!(!debug.contains("RAW_PAYLOAD_SENTINEL_NEVER_RENDER"));
assert!(debug.contains("len"));
let empty_format = match crate::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert!(crate::RawPayload::try_new(empty_format, 1, std::vec::Vec::new().into_boxed_slice(), crate::RawContentHash::new([0_u8; 32])).is_err());
let zero_version_format = match crate::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert!(crate::RawPayload::try_new(zero_version_format, 0, vec![1_u8].into_boxed_slice(), crate::RawContentHash::new([0_u8; 32])).is_err());
let oversized_format = match crate::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let oversized = vec![0_u8; crate::MAX_RAW_PAYLOAD_BYTES + 1].into_boxed_slice();
let error = crate::RawPayload::try_new(oversized_format, 1, oversized, crate::RawContentHash::new([0_u8; 32]));
assert!(error.is_err());
return;
}
#[test]
fn acquisition_provenance_preserves_safe_metadata_and_validates_time_and_size() {
let received_at = match timestamp(2_000) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let observed_at = match timestamp(1_000) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let provider = match code("publicnode") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let protocol = match code("yellowstone_grpc") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let method = match code("transactions") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let endpoint = match code("main") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let commitment = match code("confirmed") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let session = match code("session_1") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let filter = match code("all_transactions") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let provenance = crate::RawAcquisitionProvenance::new(provider, protocol, method, crate::RawAcquisitionOrigin::Live, received_at)
.with_endpoint_id(endpoint)
.with_commitment(commitment)
.with_capture_session_id(session)
.with_filter_id(filter)
.with_source_payload_hash(crate::RawContentHash::new([3_u8; 32]));
let provenance_result = provenance.try_with_observed_at(observed_at);
assert!(provenance_result.is_ok());
let provenance = match provenance_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let provenance_result = provenance.try_with_source_payload_size_bytes(4_096);
assert!(provenance_result.is_ok());
let provenance = match provenance_result {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert_eq!(provenance.provider().as_str(), "publicnode");
assert_eq!(provenance.protocol().as_str(), "yellowstone_grpc");
assert_eq!(provenance.acquisition_method().as_str(), "transactions");
assert_eq!(provenance.origin(), crate::RawAcquisitionOrigin::Live);
assert_eq!(provenance.observed_at(), std::option::Option::Some(observed_at));
assert_eq!(provenance.received_at(), received_at);
assert_eq!(provenance.source_payload_size_bytes(), std::option::Option::Some(4_096));
let reversed_provider = match code("provider") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reversed_protocol = match code("solana_websocket") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reversed_method = match code("transactionSubscribe") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reversed_received = match timestamp(1_000) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reversed_observed = match timestamp(2_000) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let reversed =
crate::RawAcquisitionProvenance::new(reversed_provider, reversed_protocol, reversed_method, crate::RawAcquisitionOrigin::Live, reversed_received)
.try_with_observed_at(reversed_observed);
assert!(reversed.is_err());
let oversized_provider = match code("provider") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let oversized_protocol = match code("solana_http") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let oversized_method = match code("getTransaction") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let oversized_received = match timestamp(2_000) {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let oversized_source = crate::RawAcquisitionProvenance::new(
oversized_provider,
oversized_protocol,
oversized_method,
crate::RawAcquisitionOrigin::Backfill,
oversized_received,
)
.try_with_source_payload_size_bytes(crate::MAX_RAW_SOURCE_PAYLOAD_BYTES + 1);
assert!(oversized_source.is_err());
return;
}

View File

@@ -0,0 +1,85 @@
// file: crates/ksp-store-api/unit_tests/model/raw_transaction.rs
// version: 1
fn network() -> std::option::Option<crate::RawNetworkId> {
return match crate::RawNetworkId::new("mainnet-beta".to_owned()) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
fn payload() -> std::option::Option<crate::RawPayload> {
let format = match crate::RawFormatId::new("ksp.solana.raw_transaction".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
return match crate::RawPayload::try_new(
format,
1,
b"canonical transaction including logs".to_vec().into_boxed_slice(),
crate::RawContentHash::new([5_u8; 32]),
) {
std::result::Result::Ok(value) => std::option::Option::Some(value),
std::result::Result::Err(_) => std::option::Option::None,
};
}
fn provenance() -> std::option::Option<crate::RawAcquisitionProvenance> {
let provider = match crate::RawProvenanceCode::new("publicnode".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let protocol = match crate::RawProvenanceCode::new("yellowstone_grpc".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let method = match crate::RawProvenanceCode::new("transactions".to_owned()) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let received_at = match crate::RawTimestamp::from_unix_millis(1_000) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
return std::option::Option::Some(crate::RawAcquisitionProvenance::new(provider, protocol, method, crate::RawAcquisitionOrigin::Backfill, received_at));
}
#[test]
fn raw_transaction_identity_is_network_plus_signature_and_payload_remains_whole() {
let network = match network() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let payload = match payload() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let signature = crate::RawTransactionSignature::new([9_u8; 64]);
let reference = crate::RawTransactionReference::new(network, signature);
let transaction = crate::RawTransaction::new(reference, 42, std::option::Option::None, payload);
assert_eq!(transaction.reference().network().as_str(), "mainnet-beta");
assert_eq!(transaction.reference().signature(), signature);
assert_eq!(transaction.slot(), 42);
assert!(transaction.block_time().is_none());
assert_eq!(transaction.payload().bytes(), b"canonical transaction including logs");
return;
}
#[test]
fn transaction_observation_is_separate_from_canonical_raw_payload() {
let network = match network() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let provenance = match provenance() {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let signature = crate::RawTransactionSignature::new([11_u8; 64]);
let reference = crate::RawTransactionReference::new(network, signature);
let observation = crate::RawTransactionObservation::new(crate::RawObservationKey::new([12_u8; 32]), reference, provenance);
assert_eq!(observation.transaction().signature(), signature);
assert_eq!(observation.observation_key().as_bytes(), &[12_u8; 32]);
assert_eq!(observation.provenance().provider().as_str(), "publicnode");
return;
}