v0.3.1-pre.003
This commit is contained in:
9
crates/ksp-store-api/src/error.rs
Normal file
9
crates/ksp-store-api/src/error.rs
Normal 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");
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
449
crates/ksp-store-api/src/model/raw_primitives.rs
Normal file
449
crates/ksp-store-api/src/model/raw_primitives.rs
Normal 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;
|
||||
113
crates/ksp-store-api/src/model/raw_transaction.rs
Normal file
113
crates/ksp-store-api/src/model/raw_transaction.rs
Normal 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;
|
||||
Reference in New Issue
Block a user