diff --git a/Cargo.toml b/Cargo.toml index d248ade..7d153ab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ resolver = "3" members = ["crates/ksp-app-backfill-desk", "crates/ksp-app-config-desk", "crates/ksp-app-solprices-desk", "crates/ksp-app-store-desk", "crates/ksp-app-wallet-desk", "crates/ksp-config-lib", "crates/ksp-core-lib", "crates/ksp-interface-lib", "crates/ksp-job-api", "crates/ksp-job-backfill-lib", "crates/ksp-logging-lib", "crates/ksp-offchain-transport-lib", "crates/ksp-onchain-transport-lib", "crates/ksp-program-api", "crates/ksp-raw-transaction-lib", "crates/ksp-store-api", "crates/ksp-store-lib", "crates/ksp-store-postgres-lib", "crates/ksp-wallet-lib", "crates/ksp-worker-api", "crates/ksp-worker-raw-transaction-ingest-lib"] [workspace.package] -version = "0.3.11-pre.2" +version = "0.3.11-pre.3" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/src/error.rs b/crates/ksp-worker-raw-transaction-ingest-lib/src/error.rs new file mode 100644 index 0000000..93a4cf2 --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/src/error.rs @@ -0,0 +1,12 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/src/error.rs +// version: 2 + +/// Error code used when RAW transaction ingest Worker settings violate one bounded runtime invariant. +pub const ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID: ksp_core_lib::ErrorCode = + ksp_core_lib::ErrorCode::new("worker_raw_transaction_ingest", "settings_invalid"); + +/// Creates one settings-domain error carrying only the stable invalid field name. +pub(crate) fn settings_error(field: &'static str) -> ksp_core_lib::Error { + return ksp_core_lib::Error::new(crate::ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID, "invalid RAW transaction ingest Worker settings") + .with_context("field", field); +} diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/src/identity.rs b/crates/ksp-worker-raw-transaction-ingest-lib/src/identity.rs new file mode 100644 index 0000000..da34c2b --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/src/identity.rs @@ -0,0 +1,5 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/src/identity.rs +// version: 1 + +/// Stable Worker kind code used by the continuous RAW transaction ingest vertical. +pub const RAW_TRANSACTION_INGEST_WORKER_KIND_CODE: &str = "raw_transaction_ingest"; diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/src/lib.rs b/crates/ksp-worker-raw-transaction-ingest-lib/src/lib.rs index 29bf7bf..0f3f5b5 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/src/lib.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/src/lib.rs @@ -1,5 +1,5 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/src/lib.rs -// version: 1 +// version: 2 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -7,6 +7,38 @@ //! Source-neutral runtime foundation for continuous KSP RAW transaction ingestion. //! -//! The crate is intentionally limited to its dependency skeleton in this tranche. -//! Worker identity, settings, lifecycle, tasks, admission, persistence and snapshots -//! are introduced only by their dedicated prereleases. +//! This tranche owns only the concrete Worker family identity and validated technical +//! settings. Lifecycle, tasks, admission, persistence and snapshots are introduced only +//! by their dedicated prereleases; no live source or Transport dependency exists here. + +mod error; +mod identity; +mod settings; + +/// Error code used when RAW transaction ingest Worker settings violate one bounded runtime invariant. +pub use self::error::ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID; +/// Stable Worker kind code used by the continuous RAW transaction ingest vertical. +pub use self::identity::RAW_TRANSACTION_INGEST_WORKER_KIND_CODE; +/// Default bounded admission queue capacity for one RAW transaction ingest Worker. +pub use self::settings::DEFAULT_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY; +/// Default number of concurrent Store persistence operations for one RAW transaction ingest Worker. +pub use self::settings::DEFAULT_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY; +/// Default cooperative shutdown drain deadline for one RAW transaction ingest Worker. +pub use self::settings::DEFAULT_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT; +/// Maximum bounded admission queue capacity for one RAW transaction ingest Worker. +pub use self::settings::MAX_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY; +/// Maximum number of concurrent Store persistence operations for one RAW transaction ingest Worker. +pub use self::settings::MAX_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY; +/// Maximum cooperative shutdown drain deadline for one RAW transaction ingest Worker. +pub use self::settings::MAX_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT; +/// Minimum bounded admission queue capacity for one RAW transaction ingest Worker. +pub use self::settings::MIN_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY; +/// Minimum number of concurrent Store persistence operations for one RAW transaction ingest Worker. +pub use self::settings::MIN_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY; +/// Minimum cooperative shutdown drain deadline for one RAW transaction ingest Worker. +pub use self::settings::MIN_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT; +/// Validated source-neutral runtime settings for one continuous RAW transaction ingest Worker. +pub use self::settings::RawTransactionIngestSettings; + +/// Creates one settings-domain error without copying caller-supplied values into diagnostics. +pub(crate) use self::error::settings_error; diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/src/settings.rs b/crates/ksp-worker-raw-transaction-ingest-lib/src/settings.rs new file mode 100644 index 0000000..862f330 --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/src/settings.rs @@ -0,0 +1,127 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/src/settings.rs +// version: 3 + +/// Default bounded admission queue capacity for one RAW transaction ingest Worker. +pub const DEFAULT_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY: usize = 256; +/// Default number of concurrent Store persistence operations for one RAW transaction ingest Worker. +pub const DEFAULT_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY: usize = 8; +/// Default cooperative shutdown drain deadline for one RAW transaction ingest Worker. +pub const DEFAULT_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(5); +/// Maximum bounded admission queue capacity for one RAW transaction ingest Worker. +pub const MAX_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY: usize = 65_536; +/// Maximum number of concurrent Store persistence operations for one RAW transaction ingest Worker. +pub const MAX_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY: usize = 64; +/// Maximum cooperative shutdown drain deadline for one RAW transaction ingest Worker. +pub const MAX_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(30); +/// Minimum bounded admission queue capacity for one RAW transaction ingest Worker. +pub const MIN_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY: usize = 1; +/// Minimum number of concurrent Store persistence operations for one RAW transaction ingest Worker. +pub const MIN_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY: usize = 1; +/// Minimum cooperative shutdown drain deadline for one RAW transaction ingest Worker. +pub const MIN_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT: std::time::Duration = std::time::Duration::from_millis(100); + +/// Validated source-neutral runtime settings for one continuous RAW transaction ingest Worker. +#[derive(Clone, Eq, PartialEq)] +pub struct RawTransactionIngestSettings { + network: ksp_store_lib::RawNetworkId, + worker_id: ksp_worker_api::WorkerId, + admission_queue_capacity: usize, + persistence_concurrency: usize, + shutdown_drain_timeout: std::time::Duration, +} + +impl crate::RawTransactionIngestSettings { + /// Creates settings from validated network/Worker identities and explicit bounded runtime limits. + pub fn new( + network: ksp_store_lib::RawNetworkId, + worker_id: ksp_worker_api::WorkerId, + admission_queue_capacity: usize, + persistence_concurrency: usize, + shutdown_drain_timeout: std::time::Duration, + ) -> ksp_core_lib::Result { + validate_inclusive_usize( + admission_queue_capacity, + crate::MIN_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY, + crate::MAX_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY, + "admission_queue_capacity", + )?; + validate_inclusive_usize( + persistence_concurrency, + crate::MIN_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY, + crate::MAX_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY, + "persistence_concurrency", + )?; + if !(crate::MIN_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT..=crate::MAX_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT) + .contains(&shutdown_drain_timeout) + { + return std::result::Result::Err(crate::settings_error("shutdown_drain_timeout")); + } + return std::result::Result::Ok(Self { network, worker_id, admission_queue_capacity, persistence_concurrency, shutdown_drain_timeout }); + } + + /// Creates settings using the stable V1 runtime defaults for one validated network and Worker identity. + #[must_use] + pub fn with_defaults(network: ksp_store_lib::RawNetworkId, worker_id: ksp_worker_api::WorkerId) -> Self { + return Self { + network, + worker_id, + admission_queue_capacity: crate::DEFAULT_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY, + persistence_concurrency: crate::DEFAULT_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY, + shutdown_drain_timeout: crate::DEFAULT_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT, + }; + } + + /// Returns the Store-compatible logical network binding. + #[must_use] + pub const fn network(&self) -> &ksp_store_lib::RawNetworkId { + return &self.network; + } + + /// Returns the validated logical identity of this Worker instance. + #[must_use] + pub const fn worker_id(&self) -> &ksp_worker_api::WorkerId { + return &self.worker_id; + } + + /// Returns the bounded central admission queue capacity. + #[must_use] + pub const fn admission_queue_capacity(&self) -> usize { + return self.admission_queue_capacity; + } + + /// Returns the bounded maximum number of concurrent Store persistence operations. + #[must_use] + pub const fn persistence_concurrency(&self) -> usize { + return self.persistence_concurrency; + } + + /// Returns the bounded cooperative shutdown drain deadline. + #[must_use] + pub const fn shutdown_drain_timeout(&self) -> std::time::Duration { + return self.shutdown_drain_timeout; + } +} + +impl std::fmt::Debug for crate::RawTransactionIngestSettings { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter + .debug_struct("RawTransactionIngestSettings") + .field("network", &self.network) + .field("worker_id", &self.worker_id) + .field("admission_queue_capacity", &self.admission_queue_capacity) + .field("persistence_concurrency", &self.persistence_concurrency) + .field("shutdown_drain_timeout", &self.shutdown_drain_timeout) + .finish(); + } +} + +fn validate_inclusive_usize(value: usize, minimum: usize, maximum: usize, field: &'static str) -> ksp_core_lib::Result<()> { + if !(minimum..=maximum).contains(&value) { + return std::result::Result::Err(crate::settings_error(field)); + } + return std::result::Result::Ok(()); +} + +#[cfg(test)] +#[path = "../unit_tests/settings.rs"] +mod tests; diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/tests/dependency_boundary.rs b/crates/ksp-worker-raw-transaction-ingest-lib/tests/dependency_boundary.rs index e057818..71381c7 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/tests/dependency_boundary.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/tests/dependency_boundary.rs @@ -1,7 +1,7 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/tests/dependency_boundary.rs -// version: 1 +// version: 2 -//! Dependency firewall canaries for the RAW transaction ingest Worker skeleton. +//! Dependency firewall canaries for the RAW transaction ingest Worker foundation. #[test] fn pre_002_manifest_dependency_surface_is_exact() { @@ -46,28 +46,26 @@ fn pre_002_manifest_keeps_forbidden_layers_and_live_sources_out() { ] { assert!(!dependencies.contains(forbidden), "forbidden Worker dependency present: {forbidden}"); } - assert!(!manifest.contains("[dev-dependencies]"), "pre.002 requires no test-only dependency"); - assert!(!manifest.contains("[build-dependencies]"), "pre.002 requires no build dependency"); + assert!(!manifest.contains("[dev-dependencies]"), "foundation requires no test-only dependency"); + assert!(!manifest.contains("[build-dependencies]"), "foundation requires no build dependency"); return; } #[test] -fn pre_002_crate_root_contains_no_runtime_behavior_or_public_contract_yet() { +fn pre_003_source_surface_opens_identity_and_settings_without_runtime_behavior() { let root = include_str!("../src/lib.rs"); - for forbidden in [ - "pub mod ", - "pub struct ", - "pub enum ", - "pub trait ", - "pub fn ", - "tokio::", - "ksp_store_lib::", - "ksp_raw_transaction_lib::", - "ksp_worker_api::", - "start(", - "spawn(", + for required in [ + "mod error;", + "mod identity;", + "mod settings;", + "pub use self::error::ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID;", + "pub use self::identity::RAW_TRANSACTION_INGEST_WORKER_KIND_CODE;", + "pub use self::settings::RawTransactionIngestSettings;", ] { - assert!(!root.contains(forbidden), "pre.002 crate root opened behavior too early: {forbidden}"); + assert!(root.contains(required), "required pre.003 crate-root contract missing: {required}"); + } + for forbidden in ["pub mod ", "tokio::", "start(", "spawn(", "JoinHandle", "JoinSet", "mpsc::", "watch::", "ksp_onchain_transport_lib::"] { + assert!(!root.contains(forbidden), "pre.003 opened runtime behavior too early: {forbidden}"); } return; } diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs b/crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs new file mode 100644 index 0000000..36264e2 --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs @@ -0,0 +1,74 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs +// version: 4 + +//! External public-surface proofs for the RAW transaction ingest Worker identity and settings foundation. + +#[test] +fn pre_003_kind_code_and_settings_are_consumable_from_crate_root() { + assert_eq!(ksp_worker_raw_transaction_ingest_lib::RAW_TRANSACTION_INGEST_WORKER_KIND_CODE, "raw_transaction_ingest"); + let kind_result = ksp_worker_api::WorkerKindCode::new(ksp_worker_raw_transaction_ingest_lib::RAW_TRANSACTION_INGEST_WORKER_KIND_CODE); + assert!(kind_result.is_ok()); + let kind = match kind_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + assert_eq!(kind.as_str(), "raw_transaction_ingest"); + let network = match ksp_store_lib::RawNetworkId::new("mainnet") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let worker_id = match ksp_worker_api::WorkerId::new("raw-ingest-mainnet-001") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let settings = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings::with_defaults(network, worker_id); + assert_eq!(settings.network().as_str(), "mainnet"); + assert_eq!(settings.worker_id().as_str(), "raw-ingest-mainnet-001"); + assert_eq!(settings.admission_queue_capacity(), ksp_worker_raw_transaction_ingest_lib::DEFAULT_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY); + assert_eq!(settings.persistence_concurrency(), ksp_worker_raw_transaction_ingest_lib::DEFAULT_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY); + assert_eq!(settings.shutdown_drain_timeout(), ksp_worker_raw_transaction_ingest_lib::DEFAULT_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT); + return; +} + +#[test] +fn pre_003_invalid_network_and_worker_identity_remain_owned_by_lower_contracts() { + let network = ksp_store_lib::RawNetworkId::new(""); + let worker_id = ksp_worker_api::WorkerId::new("invalid worker id"); + assert!(network.is_err()); + assert!(worker_id.is_err()); + let network_error = match network { + std::result::Result::Ok(_) => return, + std::result::Result::Err(value) => value, + }; + assert_eq!(network_error.code(), ksp_store_lib::ERROR_CODE_RAW_MODEL_INVALID); + let worker_error = match worker_id { + std::result::Result::Ok(_) => return, + std::result::Result::Err(value) => value, + }; + assert_eq!(worker_error.code(), ksp_worker_api::ERROR_CODE_WORKER_ID_INVALID); + return; +} + +#[test] +fn pre_003_runtime_bound_errors_are_stable_and_do_not_echo_values() { + let network = match ksp_store_lib::RawNetworkId::new("mainnet") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let worker_id = match ksp_worker_api::WorkerId::new("raw-ingest-mainnet-001") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let result = ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings::new(network, worker_id, 0, 8, std::time::Duration::from_secs(5)); + assert!(result.is_err()); + let error = match result { + std::result::Result::Ok(_) => return, + std::result::Result::Err(value) => value, + }; + assert_eq!(error.code(), ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID); + assert_eq!(error.message(), "invalid RAW transaction ingest Worker settings"); + assert_eq!(error.context().len(), 1); + assert_eq!(error.context()[0].value(), "admission_queue_capacity"); + assert!(!std::format!("{error:?}").contains("raw-ingest-mainnet-001")); + return; +} diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/settings.rs b/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/settings.rs new file mode 100644 index 0000000..59a5297 --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/settings.rs @@ -0,0 +1,105 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/settings.rs +// version: 2 + +fn identities() -> std::option::Option<(ksp_store_lib::RawNetworkId, ksp_worker_api::WorkerId)> { + let network_result = ksp_store_lib::RawNetworkId::new("mainnet"); + assert!(network_result.is_ok()); + let network = match network_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::option::Option::None, + }; + let worker_id_result = ksp_worker_api::WorkerId::new("raw-ingest-mainnet-001"); + assert!(worker_id_result.is_ok()); + let worker_id = match worker_id_result { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::option::Option::None, + }; + return std::option::Option::Some((network, worker_id)); +} + +#[test] +fn pre_003_defaults_are_exact_and_preserve_typed_identity() { + let (network, worker_id) = match identities() { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let settings = crate::RawTransactionIngestSettings::with_defaults(network.clone(), worker_id.clone()); + assert_eq!(settings.network(), &network); + assert_eq!(settings.worker_id(), &worker_id); + assert_eq!(settings.admission_queue_capacity(), 256); + assert_eq!(settings.persistence_concurrency(), 8); + assert_eq!(settings.shutdown_drain_timeout(), std::time::Duration::from_secs(5)); + assert_eq!(crate::DEFAULT_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY, 256); + assert_eq!(crate::DEFAULT_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY, 8); + assert_eq!(crate::DEFAULT_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT, std::time::Duration::from_secs(5)); + return; +} + +#[test] +fn pre_003_inclusive_bounds_accept_exact_minimum_and_maximum() { + for (queue, concurrency, drain) in [ + ( + crate::MIN_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY, + crate::MIN_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY, + crate::MIN_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT, + ), + ( + crate::MAX_RAW_TRANSACTION_INGEST_ADMISSION_QUEUE_CAPACITY, + crate::MAX_RAW_TRANSACTION_INGEST_PERSISTENCE_CONCURRENCY, + crate::MAX_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT, + ), + ] { + let (network, worker_id) = match identities() { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let settings = crate::RawTransactionIngestSettings::new(network, worker_id, queue, concurrency, drain); + assert!(settings.is_ok()); + } + return; +} + +#[test] +fn pre_003_invalid_runtime_bounds_return_stable_field_scoped_error() { + let invalid = [ + (0, 8, std::time::Duration::from_secs(5), "admission_queue_capacity"), + (65_537, 8, std::time::Duration::from_secs(5), "admission_queue_capacity"), + (256, 0, std::time::Duration::from_secs(5), "persistence_concurrency"), + (256, 65, std::time::Duration::from_secs(5), "persistence_concurrency"), + (256, 8, std::time::Duration::from_millis(99), "shutdown_drain_timeout"), + (256, 8, std::time::Duration::from_millis(30_001), "shutdown_drain_timeout"), + ]; + for (queue, concurrency, drain, field) in invalid { + let (network, worker_id) = match identities() { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let result = crate::RawTransactionIngestSettings::new(network, worker_id, queue, concurrency, drain); + assert!(result.is_err()); + let error = match result { + std::result::Result::Ok(_) => continue, + std::result::Result::Err(value) => value, + }; + assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID); + assert_eq!(error.code().domain(), "worker_raw_transaction_ingest"); + assert_eq!(error.code().code(), "settings_invalid"); + assert_eq!(error.context().len(), 1); + assert_eq!(error.context()[0].key(), "field"); + assert_eq!(error.context()[0].value(), field); + } + return; +} + +#[test] +fn pre_003_debug_keeps_worker_identity_redacted() { + let (network, worker_id) = match identities() { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let settings = crate::RawTransactionIngestSettings::with_defaults(network, worker_id); + let rendered = std::format!("{settings:?}"); + assert!(rendered.contains("mainnet")); + assert!(rendered.contains("WorkerId(..)")); + assert!(!rendered.contains("raw-ingest-mainnet-001")); + return; +} diff --git a/deltas/0.3.11/pre.003.md b/deltas/0.3.11/pre.003.md new file mode 100644 index 0000000..4490a15 --- /dev/null +++ b/deltas/0.3.11/pre.003.md @@ -0,0 +1,185 @@ + + + +# Delta `0.3.11-pre.003` — identité Worker + settings bornés + +## Base requise + +```text +0.3.11-pre.002 +workspace.package.version = 0.3.11-pre.2 +``` + +Le gate opérateur communiqué pour `pre.002` est entièrement vert : `cargo fmt`, audits Rust/Markdown, `cargo check --workspace`, Clippy strict, tests de la crate et arbres Cargo normal/features. Les 3 tests `pre.002` passent et le firewall de dépendances reste conforme. + +## Objectif + +Matérialiser uniquement l'identité de la verticale Worker RAW transaction ingest et ses settings runtime techniques bornés. Aucun lifecycle, `start`, spawn, task, channel, supervisor, persistence Store, canonicalisation ou source live n'est introduit. + +## Version + +Cette tranche est une prerelease non-fix : + +```text +workspace.package.version = 0.3.11-pre.3 +``` + +## Identité Worker + +La verticale possède le code stable : + +```text +RAW_TRANSACTION_INGEST_WORKER_KIND_CODE = "raw_transaction_ingest" +``` + +La transformation de ce texte en `WorkerKindCode` reste sous validation de `ksp-worker-api`; aucun second validateur d'identité n'est créé dans la crate concrète. + +## `RawTransactionIngestSettings` + +Surface exacte : + +```text +network: ksp_store_lib::RawNetworkId +worker_id: ksp_worker_api::WorkerId +admission_queue_capacity: usize +persistence_concurrency: usize +shutdown_drain_timeout: Duration +``` + +Les champs sont privés et exposés par getters. Deux constructions existent : paramètres explicites bornés et defaults V1 sur des identités déjà validées. + +Bornes exactes : + +```text +admission queue : 1..=65_536, default 256 +persistence concurrency : 1..=64, default 8 +shutdown drain : 100 ms..=30 s, default 5 s +``` + +Les bornes sont inclusives. Aucun champ historique, provider-specific, retry, replay, gap repair, source capability ou frontier n'est ajouté. + +## Erreur settings + +La crate ouvre un seul code propre à cette tranche : + +```text +worker_raw_transaction_ingest.settings_invalid +``` + +Il couvre uniquement les limites techniques possédées par le Worker concret. Le contexte d'erreur contient seulement le nom statique du champ invalide : + +```text +admission_queue_capacity +persistence_concurrency +shutdown_drain_timeout +``` + +Aucune valeur caller-supplied n'est copiée dans le diagnostic. + +Les erreurs de `RawNetworkId`, `WorkerId` et `WorkerKindCode` restent celles de leurs crates propriétaires. + +## Redaction + +`Debug` du settings rend le network logique mais conserve le `WorkerId(..)` redacted fourni par `ksp-worker-api`; le texte réel du Worker ID n'est pas rendu. + +## Frontière runtime maintenue + +Toujours absents : + +```text +RawTransactionIngestWorker +start/spawn +JoinHandle/JoinSet +mpsc/watch +supervisor +Store write +common RAW conversion +Transport +source live/provider SDK +``` + +Le manifest de production est inchangé depuis `pre.002`. + +## Tests ajoutés/étendus + +```text +unit_tests/settings.rs + defaults exacts + bornes min/max inclusives + six cas hors bornes + code/contexte d'erreur stable + redaction Debug du WorkerId + +tests/public_api.rs + kind + settings consommables depuis crate root + validation network/Worker identity reste lower-layer-owned + erreur de borne publique stable et sans echo d'identité + +tests/dependency_boundary.rs + firewall pre.002 conservé + crate-root pre.003 limité à identity/settings + absence de comportement runtime prématuré +``` + +## Fichiers ajoutés + +```text +crates/ksp-worker-raw-transaction-ingest-lib/src/error.rs +crates/ksp-worker-raw-transaction-ingest-lib/src/identity.rs +crates/ksp-worker-raw-transaction-ingest-lib/src/settings.rs +crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/settings.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs +deltas/0.3.11/pre.003.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +crates/ksp-worker-raw-transaction-ingest-lib/src/lib.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/dependency_boundary.rs +docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md +docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md +``` + +## Fichiers supprimés + +Aucun. + +## Validations exécutées dans l'environnement d'assemblage + +Premier passage : quatre écarts de forme détectés par l'audit Rust (rustdoc `pub(crate)`, ordre de constantes, ligne vide de fonction), puis un espacement Markdown documentaire détecté après ajout de la validation. Ils ont tous été corrigés avant livraison. + +Gate final local : + +```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 +Markdown table audit: clean (340 table(s), 777 file(s)) + +contrôle statique runtime-boundary : clean +comparaison byte-à-byte avec pre.002 : 6 fichiers ajoutés, 5 modifiés, 0 supprimé +``` + +L'environnement d'assemblage ne fournit ni `cargo`, ni `rustc`, ni `rustfmt`. Aucun gate Cargo n'est déclaré PASS localement. + +## Gate opérateur demandé + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test -p ksp-worker-raw-transaction-ingest-lib +cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal +cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features +``` + +## Suite + +Après gate opérateur vert, `pre.004` introduit le `start` caller-runtime-owned, le handle concret, le lifecycle/terminal et le stop idempotent. Aucun supervisor complet ni persistence réelle ne doit être anticipé avant leurs tranches dédiées. diff --git a/docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md b/docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md index 5afe823..ec97bc3 100644 --- a/docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md +++ b/docs/plans/032-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION_PLAN.md @@ -1,5 +1,5 @@ - + # Plan v0.3.11 — fondation runtime du Worker RawTransaction ingest @@ -699,12 +699,14 @@ Budget cible : **10–15 min**. Vérifier la stable, corriger les deux divergenc Budget cible : **10–15 min**. Créer la crate minimale, l'ajouter au workspace, installer uniquement les edges décidés et les tests de manifest/dependency boundary. Pas de runtime comportemental. -État après matérialisation : **implémenté, gate opérateur requis**. Le manifest porte exactement les sept edges décidés (`core`, `logging`, common RAW, façade Store sans backend par défaut, Worker API, `sha2`, Tokio `macros/rt/sync/time`) ; la crate-root reste sans API ni comportement runtime et un test externe verrouille le firewall. +État après matérialisation : **implémenté et gate opérateur validé**. Le manifest porte exactement les sept edges décidés (`core`, `logging`, common RAW, façade Store sans backend par défaut, Worker API, `sha2`, Tokio `macros/rt/sync/time`) ; la crate-root reste sans API ni comportement runtime et un test externe verrouille le firewall. Le gate communiqué le 8 septembre 2026 est vert sur `fmt`, audits, `check`, Clippy strict, tests de crate et les deux arbres Cargo. ### `pre.003` — identity + settings foundation Budget cible : **10–15 min**. Kind code, settings, defaults/bounds, validation network/worker identity et erreurs correspondantes. Aucun spawn. +État après matérialisation : **implémenté, gate opérateur requis**. Le kind stable vaut `raw_transaction_ingest`; les settings portent uniquement `network`, `worker_id`, queue, concurrence de persistence et drain timeout. Les identités restent validées par leurs types propriétaires, tandis que les trois bornes techniques sont verrouillées par un code d'erreur Worker stable et des tests unitaires/externes. Aucun `start`, spawn, channel, supervisor ou appel Store n'est introduit. + ### `pre.004` — start/handle/lifecycle/terminal Budget cible : **15–20 min**. Start sur runtime caller-owned, handle public, stop idempotent, lifecycle générique, terminal future sans JoinHandle public. Harness runtime minimal. diff --git a/docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md b/docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md index e161dee..6a53d38 100644 --- a/docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md +++ b/docs/validation/028-V0_3_11_RAW_TRANSACTION_INGEST_WORKER_FOUNDATION.md @@ -1,5 +1,5 @@ - + # Validation v0.3.11 — fondation runtime du Worker RawTransaction ingest @@ -509,7 +509,7 @@ 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 -Markdown table audit: clean (340 table(s), 776 file(s)) +Markdown table audit: clean (340 table(s), 777 file(s)) contrôle TOML/statique : workspace 0.3.11-pre.2, 21 membres, Worker présent, 7 dépendances exactes, Store default-features=false, Tokio macros/rt/sync/time comparaison byte-à-byte avec pre.001 : 4 fichiers ajoutés, 3 modifiés, 0 supprimé @@ -532,3 +532,105 @@ cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features Critère de passage : le firewall exact reste vert et aucun backend Store, Job, Config, Transport/provider ou runtime comportemental prématuré n'apparaît. +## 14. Fermeture opérateur `pre.002` et matérialisation `pre.003` + +### 14.1 Fermeture opérateur de `pre.002` + +Le journal opérateur communiqué le 8 septembre 2026 ferme intégralement le gate demandé sur `0.3.11-pre.2` : + +```text +cargo fmt --all : terminé sans erreur +python3 scripts/audit_rust_workspace_rules.py : clean, export completeness 0 +python3 scripts/audit_markdown_tables.py ... : clean (340 tables, 776 files) +cargo check --workspace : terminé sans erreur +cargo clippy --workspace --all-targets --all-features -- -D warnings : terminé sans erreur +cargo test -p ksp-worker-raw-transaction-ingest-lib : 3 tests externes PASS, 0 échec +cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal : firewall attendu +cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features : Tokio limité à macros/rt/sync/time +``` + +Les arbres confirment notamment `ksp-store-lib` sans backend PostgreSQL transitif et l'absence de Config, Job/Backfill, Transport, provider SDK, `futures-util`, `reqwest`, `tonic` ou Yellowstone dans la crate Worker. `pre.003` peut donc être ouverte. + +### 14.2 Scope matérialisé dans `pre.003` + +La tranche ajoute uniquement : + +```text +RAW_TRANSACTION_INGEST_WORKER_KIND_CODE = "raw_transaction_ingest" +RawTransactionIngestSettings +ERROR_CODE_RAW_TRANSACTION_INGEST_SETTINGS_INVALID +constantes defaults/min/max pour queue, persistence concurrency et drain timeout +``` + +Le settings reste source-neutral et ne contient que : + +```text +network: RawNetworkId +worker_id: WorkerId +admission_queue_capacity: usize +persistence_concurrency: usize +shutdown_drain_timeout: Duration +``` + +Les valeurs V1 sont exactement : + +```text +queue : 1..=65_536, default 256 +persistence concurrency : 1..=64, default 8 +drain : 100 ms..=30 s, default 5 s +``` + +`RawNetworkId` reste construit/validé par `ksp-store-lib` et `WorkerId`/`WorkerKindCode` par `ksp-worker-api`. La crate concrète ne duplique pas leurs validateurs et n'introduit donc aucun nouveau format d'identité. Son erreur propre `worker_raw_transaction_ingest.settings_invalid` couvre seulement les bornes techniques qu'elle possède. + +### 14.3 Sécurité de diagnostic + +`Debug` de `RawTransactionIngestSettings` peut rendre le network sûr mais délègue l'identité Worker au `Debug` redacted de `WorkerId`. Le texte réel de `worker_id` n'est donc pas rendu. Les erreurs de bornes ajoutent uniquement le nom statique du champ invalide et aucune valeur caller-supplied. + +### 14.4 Frontière runtime conservée + +La tranche n'introduit aucun : + +```text +RawTransactionIngestWorker +start/spawn +JoinHandle/JoinSet +mpsc/watch +supervisor +Store write +canonicalisation RAW +source live +Transport +``` + +Ces responsabilités restent respectivement dans `pre.004+`. + +### 14.5 Preuves locales d'assemblage `pre.003` + +Exécuté dans l'environnement d'assemblage après correction des écarts de forme détectés au premier passage : + +```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 +Markdown table audit: clean (340 table(s), 776 file(s)) +``` + +L'environnement d'assemblage ne fournit ni `cargo`, ni `rustc`, ni `rustfmt`. Aucun gate Cargo `pre.003` n'est déclaré PASS localement. + +### 14.6 Gate opérateur demandé pour `pre.003` + +```bash +cargo fmt --all +python3 scripts/audit_rust_workspace_rules.py +python3 scripts/audit_markdown_tables.py README.md RULES.md ROADMAP.md CHANGELOG.md docs prompts crates deltas +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test -p ksp-worker-raw-transaction-ingest-lib +cargo tree -p ksp-worker-raw-transaction-ingest-lib --edges normal +cargo tree -p ksp-worker-raw-transaction-ingest-lib -e features +``` + +Critère de passage : defaults/bornes/erreurs/identités sont verts, le dependency firewall reste inchangé et aucun comportement runtime de `pre.004` n'apparaît.