From 91438e82144364ac053904e1a66d09793bf4e99a Mon Sep 17 00:00:00 2001 From: SinuS Von SifriduS Date: Wed, 9 Sep 2026 07:12:37 +0200 Subject: [PATCH] v0.3.12-pre.002 --- Cargo.toml | 4 +- .../Cargo.toml | 3 +- .../src/lib.rs | 9 +- .../src/runtime.rs | 22 +- .../src/runtime_resources.rs | 136 +++++++++ .../tests/dependency_boundary.rs | 30 +- .../tests/hardening.rs | 85 ++++-- .../tests/public_api.rs | 36 ++- .../tests/release_completeness.rs | 20 +- .../unit_tests/runtime_resources.rs | 252 +++++++++++++++++ deltas/0.3.12/pre.002.md | 261 ++++++++++++++++++ ...0_3_12_YELLOWSTONE_HYDRATION_CONTINUITY.md | 119 +++++++- 12 files changed, 937 insertions(+), 40 deletions(-) create mode 100644 crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs create mode 100644 crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs create mode 100644 deltas/0.3.12/pre.002.md diff --git a/Cargo.toml b/Cargo.toml index 6cfa4fd..1e9fb01 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 519 +# version: 520 [workspace] 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.12-pre.1" +version = "0.3.12-pre.2" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/Cargo.toml b/crates/ksp-worker-raw-transaction-ingest-lib/Cargo.toml index 429df37..ef44ff6 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/Cargo.toml +++ b/crates/ksp-worker-raw-transaction-ingest-lib/Cargo.toml @@ -1,5 +1,5 @@ # file: crates/ksp-worker-raw-transaction-ingest-lib/Cargo.toml -# version: 1 +# version: 2 [package] name = "ksp-worker-raw-transaction-ingest-lib" @@ -10,6 +10,7 @@ repository.workspace = true [dependencies] ksp-core-lib = { path = "../ksp-core-lib" } ksp-logging-lib = { path = "../ksp-logging-lib" } +ksp-onchain-transport-lib = { path = "../ksp-onchain-transport-lib" } ksp-raw-transaction-lib = { path = "../ksp-raw-transaction-lib" } ksp-store-lib = { path = "../ksp-store-lib", default-features = false } ksp-worker-api = { path = "../ksp-worker-api" } 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 37cb340..56b5d5f 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: 8 +// version: 9 #![warn(missing_docs)] #![deny(unreachable_pub)] @@ -11,13 +11,14 @@ //! and the caller-runtime-owned lifecycle with private child-task supervision. This tranche also //! owns bounded source-neutral admission, common RAW canonicalization/assembly and backend-neutral //! Store persistence in normal mode plus concrete latest-value snapshots projected onto Worker API; -//! no live source or Transport dependency exists. +//! the first validated Yellowstone/HTTP runtime-resource contract exists, but no live stream is opened yet. mod admission; mod error; mod identity; mod persistence; mod runtime; +mod runtime_resources; mod settings; mod snapshot; @@ -43,6 +44,10 @@ pub use self::runtime::RawTransactionIngestHandle; pub use self::runtime::RawTransactionIngestTerminalFuture; /// Entry point owning synchronous validation and task launch for one RAW transaction ingest Worker run. pub use self::runtime::RawTransactionIngestWorker; +/// Caller-composed runtime resources for the first Yellowstone + HTTP source family. +pub use self::runtime_resources::RawTransactionIngestRuntimeResources; +/// Validated Yellowstone + HTTP source contract owned by the continuous RAW transaction ingest Worker. +pub use self::runtime_resources::RawTransactionIngestYellowstoneSource; /// 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. diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/src/runtime.rs b/crates/ksp-worker-raw-transaction-ingest-lib/src/runtime.rs index 35a7500..0bebc64 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/src/runtime.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/src/runtime.rs @@ -1,5 +1,5 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/src/runtime.rs -// version: 7 +// version: 8 type PersistencePort = std::sync::Arc; type PersistenceTasks = tokio::task::JoinSet>; @@ -95,6 +95,26 @@ impl crate::RawTransactionIngestWorker { } return start_foundation(settings, runtime, std::option::Option::Some(store)); } + + /// Starts one Worker with caller-composed runtime resources after synchronous network validation, without opening the live source before its dedicated tranche. + pub fn start_with_runtime_resources( + settings: crate::RawTransactionIngestSettings, + store: std::sync::Arc, + runtime_resources: crate::RawTransactionIngestRuntimeResources, + ) -> ksp_core_lib::Result { + let runtime = match current_runtime_handle() { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + let store_snapshot = store.runtime_snapshot(); + if let std::result::Result::Err(error) = validate_store_network(&settings, store_snapshot.network()) { + return std::result::Result::Err(error); + } + if let std::result::Result::Err(error) = runtime_resources.validate_network(settings.network()) { + return std::result::Result::Err(error); + } + return start_foundation(settings, runtime, std::option::Option::Some(store)); + } } fn begin_stopping( diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs b/crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs new file mode 100644 index 0000000..a8cac1d --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs @@ -0,0 +1,136 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs +// version: 1 + +/// Validated Yellowstone plus HTTP runtime source owned by the continuous RAW transaction ingest Worker. +/// +/// The Transport-owned channel, subscribe request, HTTP pool and hydration role remain private. Construction validates only deterministic source-composition +/// invariants and performs no network I/O. +pub struct RawTransactionIngestYellowstoneSource { + yellowstone_channel: ksp_onchain_transport_lib::YellowstoneGrpcChannel, + subscribe_request: ksp_onchain_transport_lib::YellowstoneSubscribeRequest, + http_pool: ksp_onchain_transport_lib::HttpTransportPool, + hydration_role: ksp_onchain_transport_lib::HttpRoleName, +} + +impl crate::RawTransactionIngestYellowstoneSource { + /// Creates one validated Yellowstone source contract without opening a stream or issuing HTTP requests. + pub fn new( + yellowstone_channel: ksp_onchain_transport_lib::YellowstoneGrpcChannel, + subscribe_request: ksp_onchain_transport_lib::YellowstoneSubscribeRequest, + http_pool: ksp_onchain_transport_lib::HttpTransportPool, + hydration_role: ksp_onchain_transport_lib::HttpRoleName, + ) -> ksp_core_lib::Result { + if subscribe_request.validate().is_err() { + return std::result::Result::Err(crate::runtime_error("runtime_resources.yellowstone_request_invalid")); + } + if ingestion_filter_count(&subscribe_request) == 0 { + return std::result::Result::Err(crate::runtime_error("runtime_resources.ingestion_filter_missing")); + } + match subscribe_request.commitment() { + std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed) + | std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized) => {}, + std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Processed) | std::option::Option::None => { + return std::result::Result::Err(crate::runtime_error("runtime_resources.hydration_commitment_invalid")); + }, + } + let method = match ksp_onchain_transport_lib::find_http_rpc_method("getTransaction") { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Err(crate::runtime_error("runtime_resources.hydration_method_missing")), + }; + let compatible_http_routes = compatible_http_route_count(&http_pool, &hydration_role, method.request_kind(), yellowstone_channel.cluster().as_str()); + let compatible_http_routes = match compatible_http_routes { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => return std::result::Result::Err(error), + }; + if compatible_http_routes == 0 { + return std::result::Result::Err(crate::runtime_error("runtime_resources.hydration_role_unsupported")); + } + return std::result::Result::Ok(Self { yellowstone_channel, subscribe_request, http_pool, hydration_role }); + } +} + +impl std::fmt::Debug for crate::RawTransactionIngestYellowstoneSource { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let http_snapshot = self.http_pool.snapshot(); + return formatter + .debug_struct("RawTransactionIngestYellowstoneSource") + .field("yellowstone_endpoint_name", &self.yellowstone_channel.endpoint_name()) + .field("yellowstone_provider", &self.yellowstone_channel.provider().as_str()) + .field("network", &self.yellowstone_channel.cluster().as_str()) + .field("transaction_filter_count", &self.subscribe_request.transaction_filter_count()) + .field("transaction_status_filter_count", &self.subscribe_request.transaction_status_filter_count()) + .field("block_filter_count", &self.subscribe_request.block_filter_count()) + .field("commitment", &self.subscribe_request.commitment()) + .field("has_from_slot", &self.subscribe_request.from_slot().is_some()) + .field("hydration_role", &self.hydration_role.as_str()) + .field("http_endpoint_count", &http_snapshot.endpoint_count()) + .finish(); + } +} + +/// Caller-composed runtime resources accepted by the continuous RAW transaction ingest Worker. +/// +/// This first runtime-resource contract owns exactly one Yellowstone source. It deliberately does not expose a provider enum, source collection, callback, +/// enqueue surface or lower-layer client escape hatch. +pub struct RawTransactionIngestRuntimeResources { + yellowstone_source: crate::RawTransactionIngestYellowstoneSource, +} + +impl crate::RawTransactionIngestRuntimeResources { + /// Owns the first validated productive-source contract for a future runtime start. + #[must_use] + pub fn new(yellowstone_source: crate::RawTransactionIngestYellowstoneSource) -> Self { + return Self { yellowstone_source }; + } + + /// Validates that caller-owned Worker settings target the same logical network as the composed Yellowstone/HTTP source. + pub(crate) fn validate_network(&self, network: &ksp_store_lib::RawNetworkId) -> ksp_core_lib::Result<()> { + if self.yellowstone_source.yellowstone_channel.cluster().as_str() != network.as_str() { + return std::result::Result::Err(crate::runtime_error("runtime_resources.worker_network_mismatch")); + } + return std::result::Result::Ok(()); + } +} + +impl std::fmt::Debug for crate::RawTransactionIngestRuntimeResources { + fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + return formatter.debug_struct("RawTransactionIngestRuntimeResources").field("yellowstone_source", &self.yellowstone_source).finish(); + } +} + +fn compatible_http_route_count( + pool: &ksp_onchain_transport_lib::HttpTransportPool, + hydration_role: &ksp_onchain_transport_lib::HttpRoleName, + request_kind: &str, + expected_cluster: &str, +) -> ksp_core_lib::Result { + let snapshot = pool.snapshot(); + let mut compatible = 0_usize; + for endpoint in snapshot.endpoints() { + if !endpoint.enabled() { + continue; + } + for role in endpoint.roles() { + if !role.enabled() || role.role() != hydration_role.as_str() { + continue; + } + let supports_request = role.request_kinds().iter().any(|kind| return kind.as_str() == "*" || kind.as_str() == request_kind); + if !supports_request { + continue; + } + if endpoint.cluster() != expected_cluster { + return std::result::Result::Err(crate::runtime_error("runtime_resources.transport_network_mismatch")); + } + compatible = compatible.saturating_add(1); + } + } + return std::result::Result::Ok(compatible); +} + +fn ingestion_filter_count(request: &ksp_onchain_transport_lib::YellowstoneSubscribeRequest) -> usize { + return request.transaction_filter_count().saturating_add(request.transaction_status_filter_count()).saturating_add(request.block_filter_count()); +} + +#[cfg(test)] +#[path = "../unit_tests/runtime_resources.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 c219ebb..8ba56f0 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,5 +1,5 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/tests/dependency_boundary.rs -// version: 8 +// version: 9 //! Dependency firewall canaries for the RAW transaction ingest Worker foundation. @@ -8,10 +8,23 @@ fn pre_002_manifest_dependency_surface_is_exact() { let manifest = include_str!("../Cargo.toml"); let dependencies = dependency_section(manifest); let names = manifest_dependency_names(dependencies); - assert_eq!(names, vec!["ksp-core-lib", "ksp-logging-lib", "ksp-raw-transaction-lib", "ksp-store-lib", "ksp-worker-api", "sha2", "tokio",],); + assert_eq!( + names, + vec![ + "ksp-core-lib", + "ksp-logging-lib", + "ksp-onchain-transport-lib", + "ksp-raw-transaction-lib", + "ksp-store-lib", + "ksp-worker-api", + "sha2", + "tokio", + ], + ); for required in [ "ksp-core-lib = { path = \"../ksp-core-lib\" }", "ksp-logging-lib = { path = \"../ksp-logging-lib\" }", + "ksp-onchain-transport-lib = { path = \"../ksp-onchain-transport-lib\" }", "ksp-raw-transaction-lib = { path = \"../ksp-raw-transaction-lib\" }", "ksp-store-lib = { path = \"../ksp-store-lib\", default-features = false }", "ksp-worker-api = { path = \"../ksp-worker-api\" }", @@ -24,7 +37,7 @@ fn pre_002_manifest_dependency_surface_is_exact() { } #[test] -fn pre_002_manifest_keeps_forbidden_layers_and_live_sources_out() { +fn v0_3_12_pre_002_manifest_opens_only_the_onchain_transport_live_source_edge() { let manifest = include_str!("../Cargo.toml"); let dependencies = dependency_section(manifest); for forbidden in [ @@ -33,7 +46,6 @@ fn pre_002_manifest_keeps_forbidden_layers_and_live_sources_out() { "ksp-interface-lib", "ksp-job-api", "ksp-job-backfill-lib", - "ksp-onchain-transport-lib", "ksp-program-api", "ksp-store-api", "ksp-store-postgres-lib", @@ -52,12 +64,13 @@ fn pre_002_manifest_keeps_forbidden_layers_and_live_sources_out() { } #[test] -fn pre_009_source_surface_hardens_shutdown_and_faults_without_backend_or_live_source() { +fn v0_3_12_pre_002_source_surface_hardens_shutdown_and_faults_without_backend_or_premature_live_io() { let root = include_str!("../src/lib.rs"); let runtime = include_str!("../src/runtime.rs"); let admission = include_str!("../src/admission.rs"); let persistence = include_str!("../src/persistence.rs"); let snapshot = include_str!("../src/snapshot.rs"); + let runtime_resources = include_str!("../src/runtime_resources.rs"); for required in [ "tokio::sync::mpsc::channel", "canonicalize_raw_transaction", @@ -81,7 +94,8 @@ fn pre_009_source_surface_hardens_shutdown_and_faults_without_backend_or_live_so || runtime.contains(required) || admission.contains(required) || persistence.contains(required) - || snapshot.contains(required), + || snapshot.contains(required) + || runtime_resources.contains(required), "required pre.009 hardening contract missing: {required}" ); } @@ -91,7 +105,6 @@ fn pre_009_source_surface_hardens_shutdown_and_faults_without_backend_or_live_so "pub use self::persistence::RawTransactionIngestPersistencePort", "unbounded_channel", "ksp_store_postgres_lib::", - "ksp_onchain_transport_lib::", "ForceRehydrate", "ksp_config_lib::", "reqwest::", @@ -101,7 +114,8 @@ fn pre_009_source_surface_hardens_shutdown_and_faults_without_backend_or_live_so && !runtime.contains(forbidden) && !admission.contains(forbidden) && !persistence.contains(forbidden) - && !snapshot.contains(forbidden), + && !snapshot.contains(forbidden) + && !runtime_resources.contains(forbidden), "pre.009 crossed a forbidden runtime boundary: {forbidden}" ); } diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs b/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs index 2d812e7..4918754 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs @@ -1,5 +1,5 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs -// version: 2 +// version: 3 //! External public, security, redaction and release-boundary hardening canaries for `pre.010`. @@ -92,7 +92,7 @@ fn pre_010_debug_and_settings_errors_redact_worker_identity_and_invalid_values() } #[test] -fn pre_010_manifest_dependency_surface_remains_exact_source_neutral_and_backend_neutral() { +fn v0_3_12_pre_002_manifest_dependency_surface_opens_only_transport_and_remains_backend_neutral() { let manifest = include_str!("../Cargo.toml"); let mut section = ""; let mut normal = std::collections::BTreeSet::new(); @@ -121,7 +121,16 @@ fn pre_010_manifest_dependency_surface_remains_exact_source_neutral_and_backend_ } assert_eq!( normal, - std::collections::BTreeSet::from(["ksp-core-lib", "ksp-logging-lib", "ksp-raw-transaction-lib", "ksp-store-lib", "ksp-worker-api", "sha2", "tokio",]) + std::collections::BTreeSet::from([ + "ksp-core-lib", + "ksp-logging-lib", + "ksp-onchain-transport-lib", + "ksp-raw-transaction-lib", + "ksp-store-lib", + "ksp-worker-api", + "sha2", + "tokio", + ]) ); assert!(dev.is_empty()); assert!(build.is_empty()); @@ -131,7 +140,6 @@ fn pre_010_manifest_dependency_surface_remains_exact_source_neutral_and_backend_ "ksp-config-lib", "ksp-job-api", "ksp-job-backfill-lib", - "ksp-onchain-transport-lib", "ksp-store-api", "ksp-store-postgres-lib", "reqwest", @@ -146,11 +154,12 @@ fn pre_010_manifest_dependency_surface_remains_exact_source_neutral_and_backend_ #[test] fn pre_010_source_visibility_contract_uses_crate_root_for_shared_items() { - let source_contracts: [(&str, &[&str]); 4] = [ + let source_contracts: [(&str, &[&str]); 5] = [ (include_str!("../src/settings.rs"), &["RawTransactionIngestSettings"]), (include_str!("../src/runtime.rs"), &["RawTransactionIngestHandle", "RawTransactionIngestWorker"]), (include_str!("../src/snapshot.rs"), &["RawTransactionIngestSnapshot", "RawTransactionIngestSnapshotSource"]), (include_str!("../src/persistence.rs"), &["RawTransactionIngestPersistenceOutcome", "RawTransactionIngestPersistencePort"]), + (include_str!("../src/runtime_resources.rs"), &["RawTransactionIngestYellowstoneSource", "RawTransactionIngestRuntimeResources"]), ]; for (source, symbols) in source_contracts { for symbol in symbols { @@ -166,6 +175,7 @@ fn pre_010_source_visibility_contract_uses_crate_root_for_shared_items() { ("identity", include_str!("../src/identity.rs")), ("persistence", include_str!("../src/persistence.rs")), ("runtime", include_str!("../src/runtime.rs")), + ("runtime_resources", include_str!("../src/runtime_resources.rs")), ("settings", include_str!("../src/settings.rs")), ("snapshot", include_str!("../src/snapshot.rs")), ] { @@ -184,6 +194,7 @@ fn pre_010_production_surface_has_no_historical_backfill_or_retriever_contract() include_str!("../src/lib.rs"), include_str!("../src/persistence.rs"), include_str!("../src/runtime.rs"), + include_str!("../src/runtime_resources.rs"), include_str!("../src/settings.rs"), include_str!("../src/snapshot.rs"), ]; @@ -198,8 +209,6 @@ fn pre_010_production_surface_has_no_historical_backfill_or_retriever_contract() "checkpoint", "Discovery", "discovery", - "Hydration", - "hydration", "historical", ] { assert!(!source.contains(forbidden), "historical/retriever surface leaked into Worker production source: {forbidden}"); @@ -209,8 +218,39 @@ fn pre_010_production_surface_has_no_historical_backfill_or_retriever_contract() } #[test] -fn pre_010_production_sources_scan_clean_for_config_secrets_backend_and_transport() { - let sources = [ +fn v0_3_12_pre_002_production_sources_keep_transport_confined_to_runtime_resources() { + for source in [ + include_str!("../src/admission.rs"), + include_str!("../src/error.rs"), + include_str!("../src/identity.rs"), + include_str!("../src/lib.rs"), + include_str!("../src/persistence.rs"), + include_str!("../src/runtime.rs"), + include_str!("../src/runtime_resources.rs"), + include_str!("../src/settings.rs"), + include_str!("../src/snapshot.rs"), + ] { + let lower = source.to_ascii_lowercase(); + for forbidden in ["api_key", "api-key", "authorization", "bearer ", "password", "credential", "secret"] { + assert!(!lower.contains(forbidden), "secret-like material leaked into Worker production source: {forbidden}"); + } + } + let transport_source = include_str!("../src/runtime_resources.rs"); + assert!(transport_source.contains("ksp_onchain_transport_lib::")); + for forbidden in [ + "ksp_config_lib::", + "ksp_store_postgres_lib::", + "ksp_offchain_transport_lib::", + "reqwest::", + "tokio_tungstenite::", + "tonic::", + "yellowstone_grpc_proto::", + "postgresql://", + "postgres://", + ] { + assert!(!transport_source.contains(forbidden), "forbidden implementation detail leaked into runtime resources: {forbidden}"); + } + for source in [ include_str!("../src/admission.rs"), include_str!("../src/error.rs"), include_str!("../src/identity.rs"), @@ -219,16 +259,11 @@ fn pre_010_production_sources_scan_clean_for_config_secrets_backend_and_transpor include_str!("../src/runtime.rs"), include_str!("../src/settings.rs"), include_str!("../src/snapshot.rs"), - ]; - for source in sources { - let lower = source.to_ascii_lowercase(); - for forbidden in ["api_key", "api-key", "authorization", "bearer ", "password", "credential", "secret"] { - assert!(!lower.contains(forbidden), "secret-like material leaked into Worker production source: {forbidden}"); - } + ] { + assert!(!source.contains("ksp_onchain_transport_lib::"), "Transport dependency escaped runtime_resources.rs"); for forbidden in [ "ksp_config_lib::", "ksp_store_postgres_lib::", - "ksp_onchain_transport_lib::", "ksp_offchain_transport_lib::", "reqwest::", "tokio_tungstenite::", @@ -237,12 +272,26 @@ fn pre_010_production_sources_scan_clean_for_config_secrets_backend_and_transpor "postgresql://", "postgres://", ] { - assert!(!source.contains(forbidden), "Config/backend/Transport implementation leaked into Worker production source: {forbidden}"); + assert!(!source.contains(forbidden), "forbidden implementation detail leaked into Worker production source: {forbidden}"); } } return; } +#[test] +fn v0_3_12_pre_002_runtime_resource_contract_performs_no_live_io_or_source_spawn() { + let resources = include_str!("../src/runtime_resources.rs"); + let runtime = include_str!("../src/runtime.rs"); + for forbidden in ["open_standard_subscribe", "next_update", "get_transaction_observed", "get_block_observed", "tokio::spawn", "JoinSet"] { + assert!(!resources.contains(forbidden), "pre.002 runtime-resource contract opened premature live behavior: {forbidden}"); + } + assert!(runtime.contains("start_with_runtime_resources")); + for forbidden in ["open_standard_subscribe", "next_update", "get_transaction_observed", "get_block_observed"] { + assert!(!runtime.contains(forbidden), "pre.002 runtime start opened premature live behavior: {forbidden}"); + } + return; +} + #[test] fn pre_010_lower_layers_have_no_dependency_return_to_concrete_worker() { for manifest in [ @@ -260,7 +309,7 @@ fn pre_010_lower_layers_have_no_dependency_return_to_concrete_worker() { } #[test] -fn pre_010_public_root_exposes_no_runtime_backend_or_live_source_implementation_types() { +fn v0_3_12_pre_002_public_root_exposes_contract_types_without_transport_implementation_paths() { let root = include_str!("../src/lib.rs"); for forbidden in [ "pub mod ", 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 index c41f321..423891d 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs @@ -1,5 +1,5 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs -// version: 8 +// version: 9 //! External public-surface proofs for the RAW transaction ingest Worker foundation. @@ -135,3 +135,37 @@ fn pre_009_source_and_drain_timeout_error_codes_are_public_and_stable() { assert_eq!(ksp_worker_raw_transaction_ingest_lib::ERROR_CODE_RAW_TRANSACTION_INGEST_SOURCE_FAILED.code(), "source_failed"); return; } + +#[test] +fn v0_3_12_pre_002_runtime_resource_types_are_consumable_without_client_escape_hatch() { + let _source_new: fn( + ksp_onchain_transport_lib::YellowstoneGrpcChannel, + ksp_onchain_transport_lib::YellowstoneSubscribeRequest, + ksp_onchain_transport_lib::HttpTransportPool, + ksp_onchain_transport_lib::HttpRoleName, + ) -> ksp_core_lib::Result = + ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestYellowstoneSource::new; + let _resources_new: fn( + ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestYellowstoneSource, + ) -> ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestRuntimeResources = + ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestRuntimeResources::new; + let _start_with_resources: fn( + ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestSettings, + std::sync::Arc, + ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestRuntimeResources, + ) -> ksp_core_lib::Result = + ksp_worker_raw_transaction_ingest_lib::RawTransactionIngestWorker::start_with_runtime_resources; + let source = include_str!("../src/runtime_resources.rs"); + for forbidden in [ + "pub fn channel(", + "pub fn http_pool(", + "pub fn subscribe_request(", + "pub fn hydration_role(", + "pub fn enqueue(", + "pub fn send(", + "pub fn inner(", + ] { + assert!(!source.contains(forbidden), "runtime-resource implementation escape hatch present: {forbidden}"); + } + return; +} diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs b/crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs index f9e622f..e43ce5a 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs @@ -1,5 +1,5 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs -// version: 1 +// version: 2 //! Release-completeness canaries through the `pre.010` public/release/security hardening tranche. @@ -32,7 +32,10 @@ fn pre_010_production_module_inventory_is_exact() -> std::io::Result<()> { } } names.sort_unstable(); - assert_eq!(names, std::vec!["admission.rs", "error.rs", "identity.rs", "lib.rs", "persistence.rs", "runtime.rs", "settings.rs", "snapshot.rs",]); + assert_eq!( + names, + std::vec!["admission.rs", "error.rs", "identity.rs", "lib.rs", "persistence.rs", "runtime.rs", "runtime_resources.rs", "settings.rs", "snapshot.rs",] + ); return std::result::Result::Ok(()); } @@ -74,12 +77,14 @@ fn pre_010_public_root_export_inventory_is_exact() { "MIN_RAW_TRANSACTION_INGEST_SHUTDOWN_DRAIN_TIMEOUT", "RAW_TRANSACTION_INGEST_WORKER_KIND_CODE", "RawTransactionIngestHandle", + "RawTransactionIngestRuntimeResources", "RawTransactionIngestSettings", "RawTransactionIngestSnapshot", "RawTransactionIngestSnapshotFuture", "RawTransactionIngestSnapshotSource", "RawTransactionIngestTerminalFuture", "RawTransactionIngestWorker", + "RawTransactionIngestYellowstoneSource", ] ); assert!(!root.contains("pub mod ")); @@ -92,22 +97,25 @@ fn pre_010_external_hardening_suite_is_present_and_scoped() { for required in [ "pre_010_external_error_codes_are_stable_unique_and_domain_scoped", "pre_010_debug_and_settings_errors_redact_worker_identity_and_invalid_values", - "pre_010_manifest_dependency_surface_remains_exact_source_neutral_and_backend_neutral", + "v0_3_12_pre_002_manifest_dependency_surface_opens_only_transport_and_remains_backend_neutral", "pre_010_source_visibility_contract_uses_crate_root_for_shared_items", "pre_010_production_surface_has_no_historical_backfill_or_retriever_contract", - "pre_010_production_sources_scan_clean_for_config_secrets_backend_and_transport", + "v0_3_12_pre_002_production_sources_keep_transport_confined_to_runtime_resources", + "v0_3_12_pre_002_runtime_resource_contract_performs_no_live_io_or_source_spawn", "pre_010_lower_layers_have_no_dependency_return_to_concrete_worker", - "pre_010_public_root_exposes_no_runtime_backend_or_live_source_implementation_types", + "v0_3_12_pre_002_public_root_exposes_contract_types_without_transport_implementation_paths", ] { assert!(hardening.contains(required), "required pre.010 hardening canary missing: {required}"); } let dependency_boundary = include_str!("dependency_boundary.rs"); assert!(dependency_boundary.contains("pre_002_manifest_dependency_surface_is_exact")); - assert!(dependency_boundary.contains("pre_009_source_surface_hardens_shutdown_and_faults_without_backend_or_live_source")); + assert!(dependency_boundary.contains("v0_3_12_pre_002_source_surface_hardens_shutdown_and_faults_without_backend_or_premature_live_io")); + assert!(dependency_boundary.contains("v0_3_12_pre_002_manifest_opens_only_the_onchain_transport_live_source_edge")); let public_api = include_str!("public_api.rs"); assert!(public_api.contains("pre_003_kind_code_and_settings_are_consumable_from_crate_root")); assert!(public_api.contains("pre_004_start_handle_and_terminal_future_are_consumable_without_public_join_handle")); assert!(public_api.contains("pre_008_snapshot_surface_and_common_projection_are_public_and_stable")); assert!(public_api.contains("pre_009_source_and_drain_timeout_error_codes_are_public_and_stable")); + assert!(public_api.contains("v0_3_12_pre_002_runtime_resource_types_are_consumable_without_client_escape_hatch")); return; } diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs b/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs new file mode 100644 index 0000000..6804f13 --- /dev/null +++ b/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs @@ -0,0 +1,252 @@ +// file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs +// version: 1 + +fn grpc_endpoint(cluster: &str) -> std::option::Option { + let url = match ksp_onchain_transport_lib::YellowstoneGrpcEndpointUrl::parse("http://127.0.0.1:10000/GRPC-SECRET-CANARY") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::option::Option::None, + }; + return std::option::Option::Some(ksp_onchain_transport_lib::YellowstoneGrpcEndpointSettings::new( + "yellowstone-fixture", + true, + ksp_onchain_transport_lib::YellowstoneGrpcProviderName::new("fixture-provider"), + ksp_onchain_transport_lib::YellowstoneGrpcClusterName::new(cluster), + url, + ksp_onchain_transport_lib::YellowstoneGrpcSessionSettings::default(), + )); +} + +fn http_pool(cluster: &str, role_name: &str, request_kind: &str) -> std::option::Option { + let url = match ksp_onchain_transport_lib::HttpEndpointUrl::parse("https://fixture.invalid/rpc?token=HTTP-SECRET-CANARY") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::option::Option::None, + }; + let role = ksp_onchain_transport_lib::HttpEndpointRoleSettings::new( + ksp_onchain_transport_lib::HttpRoleName::new(role_name), + true, + std::vec![ksp_onchain_transport_lib::HttpRequestKind::new(request_kind)], + 10, + ksp_onchain_transport_lib::HttpRoleLimits::new( + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + std::option::Option::None, + ), + ); + let endpoint = ksp_onchain_transport_lib::HttpEndpointSettings::new( + "http-fixture", + true, + ksp_onchain_transport_lib::HttpProviderName::new("fixture-provider"), + ksp_onchain_transport_lib::HttpClusterName::new(cluster), + url, + std::time::Duration::from_secs(1), + std::time::Duration::from_secs(2), + std::option::Option::Some(4), + std::vec![role], + ); + let settings = ksp_onchain_transport_lib::HttpTransportSettings::new( + std::vec![endpoint], + ksp_onchain_transport_lib::HttpRetrySettings::new(1, std::time::Duration::from_millis(10), std::time::Duration::from_millis(20)), + ); + return match ksp_onchain_transport_lib::HttpTransportPool::new(settings) { + std::result::Result::Ok(value) => std::option::Option::Some(value), + std::result::Result::Err(_) => std::option::Option::None, + }; +} + +fn transaction_request( + commitment: std::option::Option, +) -> std::option::Option { + let name = match ksp_onchain_transport_lib::YellowstoneSubscribeFilterName::new("tx-fixture") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return std::option::Option::None, + }; + let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new(); + if request.insert_transaction_filter(name, ksp_onchain_transport_lib::YellowstoneSubscribeTransactionFilter::new()).is_err() { + return std::option::Option::None; + } + request.set_commitment(commitment); + return std::option::Option::Some(request); +} + +#[tokio::test(flavor = "current_thread")] +async fn pre_002_source_accepts_matching_confirmed_transaction_and_get_transaction_route_without_io() { + let endpoint = match grpc_endpoint("devnet") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let pool = match http_pool("devnet", "hydration", "get_transaction") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let source = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")); + assert!(source.is_ok()); + return; +} + +#[tokio::test(flavor = "current_thread")] +async fn pre_002_source_rejects_processed_and_implicit_commitment() { + for commitment in [std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Processed), std::option::Option::None] { + let endpoint = match grpc_endpoint("devnet") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let request = match transaction_request(commitment) { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let pool = match http_pool("devnet", "hydration", "get_transaction") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let source = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")); + let error = match source { + std::result::Result::Ok(_) => return, + std::result::Result::Err(error) => error, + }; + assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID); + assert!(error.context().iter().any(|context| return context.value() == "runtime_resources.hydration_commitment_invalid")); + } + return; +} + +#[tokio::test(flavor = "current_thread")] +async fn pre_002_source_requires_ingestion_bearing_filter_family() { + let endpoint = match grpc_endpoint("devnet") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let mut request = ksp_onchain_transport_lib::YellowstoneSubscribeRequest::new(); + request.set_commitment(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Finalized)); + let pool = match http_pool("devnet", "hydration", "get_transaction") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let source = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")); + let error = match source { + std::result::Result::Ok(_) => return, + std::result::Result::Err(error) => error, + }; + assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID); + assert!(error.context().iter().any(|context| return context.value() == "runtime_resources.ingestion_filter_missing")); + return; +} + +#[tokio::test(flavor = "current_thread")] +async fn pre_002_source_rejects_missing_hydration_capability_and_cross_transport_network() { + for (http_cluster, request_kind, expected_condition) in [ + ("devnet", "get_balance", "runtime_resources.hydration_role_unsupported"), + ("mainnet", "get_transaction", "runtime_resources.transport_network_mismatch"), + ] { + let endpoint = match grpc_endpoint("devnet") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let pool = match http_pool(http_cluster, "hydration", request_kind) { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let source = crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")); + let error = match source { + std::result::Result::Ok(_) => return, + std::result::Result::Err(error) => error, + }; + assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID); + assert!(error.context().iter().any(|context| return context.value() == expected_condition)); + } + return; +} + +#[tokio::test(flavor = "current_thread")] +async fn pre_002_runtime_resource_debug_is_safe_and_exposes_no_client_inner() { + let endpoint = match grpc_endpoint("devnet") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let pool = match http_pool("devnet", "hydration", "get_transaction") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let source = match crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let resources = crate::RawTransactionIngestRuntimeResources::new(source); + let debug = std::format!("{resources:?}"); + assert!(debug.contains("RawTransactionIngestRuntimeResources")); + assert!(debug.contains("devnet")); + assert!(!debug.contains("GRPC-SECRET-CANARY")); + assert!(!debug.contains("HTTP-SECRET-CANARY")); + assert!(!debug.contains("127.0.0.1")); + assert!(!debug.contains("fixture.invalid")); + assert!(!debug.contains("tx-fixture")); + return; +} + +#[tokio::test(flavor = "current_thread")] +async fn pre_002_runtime_resources_reject_worker_network_mismatch_without_starting_source() { + let endpoint = match grpc_endpoint("devnet") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let channel = match ksp_onchain_transport_lib::YellowstoneGrpcChannel::prepare(&endpoint) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let request = match transaction_request(std::option::Option::Some(ksp_onchain_transport_lib::SolanaCommitment::Confirmed)) { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let pool = match http_pool("devnet", "hydration", "get_transaction") { + std::option::Option::Some(value) => value, + std::option::Option::None => return, + }; + let source = match crate::RawTransactionIngestYellowstoneSource::new(channel, request, pool, ksp_onchain_transport_lib::HttpRoleName::new("hydration")) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let resources = crate::RawTransactionIngestRuntimeResources::new(source); + let network = match ksp_store_lib::RawNetworkId::new("mainnet") { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return, + }; + let error = match resources.validate_network(&network) { + std::result::Result::Ok(()) => return, + std::result::Result::Err(error) => error, + }; + assert_eq!(error.code(), crate::ERROR_CODE_RAW_TRANSACTION_INGEST_RUNTIME_INVALID); + assert!(error.context().iter().any(|context| return context.value() == "runtime_resources.worker_network_mismatch")); + return; +} diff --git a/deltas/0.3.12/pre.002.md b/deltas/0.3.12/pre.002.md new file mode 100644 index 0000000..d930eab --- /dev/null +++ b/deltas/0.3.12/pre.002.md @@ -0,0 +1,261 @@ + + + +# Delta `0.3.12-pre.002` — edge Transport + contrats runtime/source Yellowstone + +## Base requise + +```text +0.3.12-pre.001 +workspace.package.version = 0.3.12-pre.1 +``` + +Le gate opérateur communiqué pour `pre.001` est vert sur : + +```text +cargo fmt --all +Rust rule audit +Markdown table audit +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +``` + +Le journal ne contient pas de `cargo test` ni de `cargo tree` ; ces commandes ne sont pas inventées comme PASS. + +## Objectif + +Ouvrir uniquement le nouvel edge de production décidé en `pre.001` : + +```text +ksp-worker-raw-transaction-ingest-lib -> ksp-onchain-transport-lib +``` + +et matérialiser les contrats Worker-owned nécessaires à la future source Yellowstone + hydration HTTP, sans ouvrir de stream ni exécuter de requête réseau. + +## Version + +Cette tranche est une prerelease non-fix : + +```text +workspace.package.version = 0.3.12-pre.2 +``` + +## Graphe Worker normal attendu + +```text +ksp-core-lib +ksp-logging-lib +ksp-onchain-transport-lib +ksp-raw-transaction-lib +ksp-store-lib default-features=false +ksp-worker-api +sha2 +tokio macros,rt,sync,time +``` + +Toujours interdits directement au Worker : + +```text +ksp-config-lib +ksp-job-api +ksp-job-backfill-lib +ksp-store-api +ksp-store-postgres-lib +reqwest +tokio-tungstenite +tonic +yellowstone-grpc-proto +SDK provider +``` + +## Nouveau contrat `RawTransactionIngestYellowstoneSource` + +Le type public à champs privés possède : + +```text +YellowstoneGrpcChannel +YellowstoneSubscribeRequest +HttpTransportPool +HttpRoleName d'hydration +``` + +`RawTransactionIngestYellowstoneSource::new(...)` valide sans I/O : + +```text +request Yellowstone valide +au moins une famille transactions / transactions_status / blocks +commitment explicite confirmed ou finalized +présence d'une route HTTP configurée compatible get_transaction +cohérence cluster entre Yellowstone et toutes les routes HTTP compatibles +``` + +Les erreurs de composition sont projetées sur `worker_raw_transaction_ingest.runtime_invalid` avec uniquement une condition stable et sûre. + +## Nouveau contrat `RawTransactionIngestRuntimeResources` + +`RawTransactionIngestRuntimeResources::new(yellowstone_source)` possède exactement cette première famille runtime. + +Aucune collection multi-source, enum provider, closure source, callback public, `enqueue`, `send`, getter de client interne ou backend n'est exposé. + +La ressource valide également la cohérence entre le réseau du source composé et `RawTransactionIngestSettings.network` avant le démarrage. + +## Nouveau start de composition + +Ajout : + +```text +RawTransactionIngestWorker::start_with_runtime_resources( + settings, + Arc, + RawTransactionIngestRuntimeResources, +) +``` + +La méthode conserve : + +```text +runtime Tokio caller-owned +validation réseau Store/settings +validation réseau Worker/source +handle/snapshot/shutdown existants +``` + +En `pre.002`, les ressources sont volontairement validées mais ne sont pas activées avant délégation au runtime source-neutral existant. Cela ferme l'API de composition sans anticiper `pre.006`. + +## Absence volontaire de comportement live + +Les canaris interdisent dans cette tranche : + +```text +open_standard_subscribe +next_update +get_transaction_observed +get_block_observed +source task spawn spécifique +nouveau JoinSet +HTTP hydration +persistence issue du réseau +``` + +`pre.003` reste donc libre d'introduire uniquement les adapters déterministes `Transaction`/`TransactionStatus`, tandis que l'activation productive de la source reste réservée à `pre.006`. + +## Sécurité et redaction + +Le `Debug` des ressources expose uniquement des métadonnées sûres : + +```text +logical Yellowstone endpoint name +provider +network +filter counts +commitment +presence from_slot +hydration role +HTTP endpoint count +``` + +Il n'expose ni URL, token, header, filter name ni client interne. + +Tests déterministes ajoutés : + +```text +acceptation confirmed + transaction + get_transaction +rejet processed +rejet commitment implicite +rejet absence de famille ingestion-bearing +rejet rôle HTTP non compatible +rejet mismatch Yellowstone/HTTP cluster +rejet mismatch Worker/source network +Debug redaction +public API/root contract +manifest/dependency firewall +Transport confiné à runtime_resources.rs +absence de live I/O en pre.002 +``` + +## Décisions prises + +```text +un seul nouvel edge productif Worker -> Onchain Transport +composition fournie par l'appelant, sans dépendance Worker -> Config +ressources Transport encapsulées par des types Worker-owned à champs privés +commitment de la future hydration limité à confirmed/finalized dès la composition +aucune activation réseau avant la tranche productive prévue +``` + +## Questions ouvertes + +Aucune question ouverte n'est bloquante pour `pre.002`. Les choix volontairement reportés restent ceux du plan actif : forme exacte du signal privé `Transaction`/`TransactionStatus` en `pre.003`, provenance Yellowstone + hydration en `pre.004`, puis continuité/replay dans les tranches dédiées. + +## Fichiers ajoutés + +```text +crates/ksp-worker-raw-transaction-ingest-lib/src/runtime_resources.rs +crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/runtime_resources.rs +deltas/0.3.12/pre.002.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +crates/ksp-worker-raw-transaction-ingest-lib/Cargo.toml +crates/ksp-worker-raw-transaction-ingest-lib/src/lib.rs +crates/ksp-worker-raw-transaction-ingest-lib/src/runtime.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/dependency_boundary.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/public_api.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs +docs/validation/029-V0_3_12_YELLOWSTONE_HYDRATION_CONTINUITY.md +``` + +## Fichiers supprimés + +Aucun. + +## Validations exécutées dans l'environnement d'assemblage + +```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), 802 file(s)) +``` + +`cargo`, `rustc` et `rustfmt` ne sont pas installés dans l'environnement d'assemblage. Aucun gate Cargo local n'est déclaré PASS. + +## 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 +cargo tree --duplicates +``` + +## Non-claims + +Cette tranche ne revendique pas : + +```text +session Yellowstone ouverte +signal Transaction/TransactionStatus produit +hydration HTTP exécutée +provenance composite assemblée +coalescence d'hydration +frontier/replay/repair +smoke live Worker +réconciliation documentaire finale README/USAGE/architecture +``` + +## Suite + +Après gate opérateur vert, `pre.003` adapte uniquement `YellowstoneTransactionUpdate` et `YellowstoneTransactionStatusUpdate` vers le signal Worker privé prévu par le plan, sans HTTP ni persistence réseau. diff --git a/docs/validation/029-V0_3_12_YELLOWSTONE_HYDRATION_CONTINUITY.md b/docs/validation/029-V0_3_12_YELLOWSTONE_HYDRATION_CONTINUITY.md index 4fb3781..dbf76e4 100644 --- a/docs/validation/029-V0_3_12_YELLOWSTONE_HYDRATION_CONTINUITY.md +++ b/docs/validation/029-V0_3_12_YELLOWSTONE_HYDRATION_CONTINUITY.md @@ -1,5 +1,5 @@ - + # Validation v0.3.12 — Yellowstone + hydration HTTP + continuité de run du Worker RawTransaction @@ -531,3 +531,120 @@ maintien/split décidé plan + validation + delta créés audits statiques post-modification verts ``` + +## 22. Gate opérateur reçu pour `pre.001` + +L'opérateur a exécuté après application de `pre.001` : + +```text +cargo fmt --all : sans erreur visible +audit Rust : clean +Rust export completeness : 0 candidate +KSP workspace Rust rule audit : clean +audit Markdown : clean (340 tables, 801 fichiers) +cargo check --workspace : PASS visible +cargo clippy --workspace --all-targets --all-features -- -D warnings : PASS visible +``` + +Aucun `cargo test` ou `cargo tree` n'est présent dans ce journal ; ils ne sont donc pas revendiqués pour ce gate. + +Résultat : `pre.001` est accepté comme base technique de `pre.002`. + +## 23. Gate `pre.002` — edge Transport + contrats runtime/source + +La tranche matérialise exactement : + +```text +workspace.package.version = 0.3.12-pre.2 +Worker -> ksp-onchain-transport-lib +RawTransactionIngestYellowstoneSource +RawTransactionIngestRuntimeResources +RawTransactionIngestWorker::start_with_runtime_resources(...) +``` + +Le constructeur `RawTransactionIngestYellowstoneSource::new` effectue uniquement des validations déterministes sans I/O : + +```text +YellowstoneSubscribeRequest::validate +au moins une famille transactions / transactions_status / blocks +commitment explicite confirmed ou finalized +rôle HTTP configuré compatible avec le request kind get_transaction +cluster identique entre Yellowstone et toutes les routes HTTP compatibles +``` + +`RawTransactionIngestRuntimeResources` conserve un seul source Yellowstone à champs privés et vérifie la cohérence avec `RawTransactionIngestSettings.network` avant le démarrage. + +`start_with_runtime_resources` préserve également le gate Store/settings existant. En `pre.002`, il ne démarre volontairement encore aucun source live : les ressources sont validées mais ne sont pas activées, puis le démarrage délègue au runtime source-neutral existant. L'activation du stream reste réservée à `pre.006` conformément au plan. + +## 24. Canaris déterministes ajoutés en `pre.002` + +Les preuves statiques/unitaires ajoutées couvrent : + +```text +manifest Worker exact avec un seul nouvel edge Transport +aucun Config / Job / backend Store / tonic / reqwest / yellowstone-grpc-proto direct +source confirmed + transaction + getTransaction route accepté +processed et commitment implicite rejetés +absence de famille d'ingestion rejetée +rôle HTTP sans get_transaction rejeté +mismatch cluster Yellowstone/HTTP rejeté +mismatch réseau Worker/source rejeté +Debug sans URL, token, filter name ou client inner +API publique des deux resource types disponible depuis crate root +start_with_runtime_resources disponible depuis crate root +aucun enqueue/send/inner/client getter public +aucun open_standard_subscribe / next_update / get_transaction_observed dans la tranche +Transport confiné à runtime_resources.rs dans les sources Worker +``` + +Aucun nouveau code Config, Store backend, Backfill ou provider n'est introduit. + +## 25. Validation locale après modification de `pre.002` + +Exécuté dans l'environnement d'assemblage : + +```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 +``` + +L'audit Markdown est rejoué après création du delta final afin d'inclure le nouveau fichier `deltas/0.3.12/pre.002.md`. + +`cargo`, `rustc` et `rustfmt` ne sont pas installés dans cet environnement ; aucun gate Cargo local n'est revendiqué. + +Gate opérateur demandé après application du delta : + +```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 +cargo tree --duplicates +``` + +## 26. Non-claims `pre.002` + +`pre.002` ne prétend pas avoir : + +```text +ouvert une session Yellowstone +lu une update Transaction/TransactionStatus/Block +émis un signal Worker privé +appelé getTransaction pour hydration +assemblé une provenance composite +persisté une transaction issue du réseau +coalescé des hydrations +modifié la processing frontier +branché from_slot/replay info +exécuté un smoke live Worker +réconcilié README/USAGE/architecture pour la surface finale 0.3.12 +``` + +La tranche suivante reste `pre.003` : adaptation déterministe `Transaction` + `TransactionStatus` vers le signal Worker privé, sans HTTP ni persistence réseau. +