diff --git a/Cargo.toml b/Cargo.toml index 5011b3c..63f3145 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ # file: Cargo.toml -# version: 571 +# version: 572 [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.14-pre.4" +version = "0.3.14-pre.5" edition = "2024" license = "MIT" repository = "https://git.sasedev.com/Sasedev/khadhroony-solana-project" diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs b/crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs index 9d8838b..86ee013 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs @@ -1,6 +1,8 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs -// version: 4 +// version: 5 +/// Maximum number of proven redundant coverage epochs retained by one run-local continuity contract. +const MAX_RAW_TRANSACTION_INGEST_COVERAGE_EPOCHS: usize = 256; /// Maximum number of simultaneously retained non-repaired run-local gaps. const MAX_RAW_TRANSACTION_INGEST_OPEN_REPAIR_GAPS: usize = 64; /// Maximum number of run-local gaps that may actively repair at once. @@ -23,6 +25,12 @@ pub(crate) enum RawTransactionIngestCoverageScope { KnownReferences([u8; 32]), } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum RawTransactionIngestCoverageRelation { + Exact, + Superset, +} + impl crate::RawTransactionIngestCoverageScope { /// Creates one provider-neutral exact source scope from a stable family code and semantic fingerprint. pub(crate) const fn exact_source_scope(family_code: &'static str, fingerprint: [u8; 32]) -> Self { @@ -54,6 +62,19 @@ impl crate::RawTransactionIngestCoverageScope { fn is_known_references(&self) -> bool { return matches!(self, Self::KnownReferences(_)); } + + fn relation_to(&self, requirement: &Self) -> std::option::Option { + return match (self, requirement) { + (Self::FullLedgerTransactions, Self::FullLedgerTransactions) => std::option::Option::Some(RawTransactionIngestCoverageRelation::Exact), + (Self::FullLedgerTransactions, Self::ExactSourceScope(_, _)) => std::option::Option::Some(RawTransactionIngestCoverageRelation::Superset), + (Self::ExactSourceScope(family_code, fingerprint), Self::ExactSourceScope(required_family_code, required_fingerprint)) + if family_code == required_family_code && fingerprint == required_fingerprint => + { + std::option::Option::Some(RawTransactionIngestCoverageRelation::Exact) + }, + _ => std::option::Option::None, + }; + } } #[derive(Clone, Debug, Eq, PartialEq)] @@ -424,14 +445,14 @@ impl RawTransactionIngestTargetCoverage { if scope.is_known_references() { return std::result::Result::Err(crate::runtime_error("continuity.known_references_not_target_scope")); } - if matches!(&scope, crate::RawTransactionIngestCoverageScope::FullLedgerTransactions) { - self.requirements.retain(|requirement| return requirement.commitment != commitment); - self.requirements.push(RawTransactionIngestCoverageRequirement { commitment, scope }); - return std::result::Result::Ok(()); - } + self.requirements.retain(|requirement| { + if requirement.commitment != commitment { + return true; + } + return scope.relation_to(&requirement.scope) != std::option::Option::Some(RawTransactionIngestCoverageRelation::Superset); + }); if self.requirements.iter().any(|requirement| { - return requirement.commitment == commitment - && (requirement.scope == crate::RawTransactionIngestCoverageScope::FullLedgerTransactions || requirement.scope == scope); + return requirement.commitment == commitment && requirement.scope.relation_to(&scope).is_some(); }) { return std::result::Result::Ok(()); } @@ -440,9 +461,133 @@ impl RawTransactionIngestTargetCoverage { } } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct RawTransactionIngestCoverageRange { + end_slot: u64, + start_slot: u64, +} + +impl RawTransactionIngestCoverageRange { + fn new(start_slot: u64, end_slot: u64) -> ksp_core_lib::Result { + if end_slot < start_slot { + return std::result::Result::Err(crate::runtime_error("continuity.coverage_range_reversed")); + } + return std::result::Result::Ok(Self { end_slot, start_slot }); + } + + const fn contains_gap(self, gap: RawTransactionIngestGapRange) -> bool { + return self.start_slot <= gap.start_slot() && self.end_slot >= gap.end_slot(); + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +struct RawTransactionIngestCoverageEpoch { + commitment: ksp_onchain_transport_lib::SolanaCommitment, + epoch_id: u64, + range: RawTransactionIngestCoverageRange, + scope: crate::RawTransactionIngestCoverageScope, + source_key: [u8; 32], +} + +impl RawTransactionIngestCoverageEpoch { + fn new( + epoch_id: u64, + source_key: [u8; 32], + commitment: ksp_onchain_transport_lib::SolanaCommitment, + scope: crate::RawTransactionIngestCoverageScope, + range: RawTransactionIngestCoverageRange, + ) -> ksp_core_lib::Result { + if epoch_id == 0 { + return std::result::Result::Err(crate::runtime_error("continuity.coverage_epoch_id_invalid")); + } + if let std::result::Result::Err(error) = scope.validate_configured_target_scope() { + return std::result::Result::Err(error); + } + return std::result::Result::Ok(Self { commitment, epoch_id, range, scope, source_key }); + } + + fn relation_to_requirement( + &self, + target_source_key: [u8; 32], + requirement: &RawTransactionIngestCoverageRequirement, + gap: RawTransactionIngestGapRange, + ) -> std::option::Option { + if self.source_key == target_source_key || self.commitment != requirement.commitment || !self.range.contains_gap(gap) { + return std::option::Option::None; + } + return self.scope.relation_to(&requirement.scope); + } +} + +struct RawTransactionIngestCoverageEpochLedger { + epochs: std::vec::Vec, + next_epoch_id: u64, +} + +impl RawTransactionIngestCoverageEpochLedger { + fn new() -> Self { + return Self { epochs: std::vec::Vec::new(), next_epoch_id: 1 }; + } + + fn redundant_relation_for_gap( + &self, + target_source_key: [u8; 32], + requirement: &RawTransactionIngestCoverageRequirement, + gap: RawTransactionIngestGapRange, + ) -> std::option::Option { + let mut exact = false; + for epoch in &self.epochs { + match epoch.relation_to_requirement(target_source_key, requirement, gap) { + std::option::Option::Some(RawTransactionIngestCoverageRelation::Superset) => { + return std::option::Option::Some(RawTransactionIngestCoverageRelation::Superset); + }, + std::option::Option::Some(RawTransactionIngestCoverageRelation::Exact) => exact = true, + std::option::Option::None => {}, + } + } + if exact { + return std::option::Option::Some(RawTransactionIngestCoverageRelation::Exact); + } + return std::option::Option::None; + } + + fn validate_invariants(&self, capabilities: &[crate::RawTransactionIngestContinuityCapabilityDescriptor]) -> ksp_core_lib::Result<()> { + if self.next_epoch_id == 0 || self.epochs.len() > MAX_RAW_TRANSACTION_INGEST_COVERAGE_EPOCHS { + return std::result::Result::Err(crate::runtime_error("continuity.coverage_epoch_inventory_invalid")); + } + let mut ids = std::collections::BTreeSet::new(); + let mut highest_epoch_id = 0_u64; + for epoch in &self.epochs { + if epoch.epoch_id == 0 || !ids.insert(epoch.epoch_id) { + return std::result::Result::Err(crate::runtime_error("continuity.coverage_epoch_id_invalid")); + } + highest_epoch_id = highest_epoch_id.max(epoch.epoch_id); + if let std::result::Result::Err(error) = epoch.scope.validate_configured_target_scope() { + return std::result::Result::Err(error); + } + if RawTransactionIngestCoverageRange::new(epoch.range.start_slot, epoch.range.end_slot).is_err() { + return std::result::Result::Err(crate::runtime_error("continuity.coverage_epoch_range_invalid")); + } + let matching_capability = capabilities.iter().find(|capability| return capability.source_key == epoch.source_key); + let matching_capability = match matching_capability { + std::option::Option::Some(value) => value, + std::option::Option::None => return std::result::Result::Err(crate::runtime_error("continuity.coverage_epoch_source_unknown")), + }; + if matching_capability.commitment != epoch.commitment || matching_capability.source_scope != epoch.scope { + return std::result::Result::Err(crate::runtime_error("continuity.coverage_epoch_capability_mismatch")); + } + } + if !self.epochs.is_empty() && self.next_epoch_id <= highest_epoch_id { + return std::result::Result::Err(crate::runtime_error("continuity.coverage_epoch_next_id_invalid")); + } + return std::result::Result::Ok(()); + } +} + /// Run-local continuity contracts prepared from the exact caller-composed live-source aggregate before productive source execution. pub(crate) struct RawTransactionIngestContinuityContracts { capabilities: std::vec::Vec, + coverage_epochs: RawTransactionIngestCoverageEpochLedger, gap_ledger: RawTransactionIngestGapLedger, target_coverage: RawTransactionIngestTargetCoverage, } @@ -470,11 +615,15 @@ impl crate::RawTransactionIngestContinuityContracts { std::result::Result::Ok(value) => value, std::result::Result::Err(error) => return std::result::Result::Err(error), }; + let coverage_epochs = RawTransactionIngestCoverageEpochLedger::new(); + if let std::result::Result::Err(error) = coverage_epochs.validate_invariants(capabilities.as_slice()) { + return std::result::Result::Err(error); + } let gap_ledger = RawTransactionIngestGapLedger::new(network); if let std::result::Result::Err(error) = gap_ledger.validate_invariants() { return std::result::Result::Err(error); } - if !coverage_scope_catalog_is_complete() { + if !coverage_scope_catalog_is_complete() || !coverage_relation_catalog_is_complete() { return std::result::Result::Err(crate::runtime_error("continuity.coverage_scope_catalog_invalid")); } if MAX_RAW_TRANSACTION_INGEST_REPAIR_BLOCK_FETCH_IN_FLIGHT == 0 @@ -483,7 +632,7 @@ impl crate::RawTransactionIngestContinuityContracts { { return std::result::Result::Err(crate::runtime_error("continuity.repair_bounds_invalid")); } - return std::result::Result::Ok(Self { capabilities, gap_ledger, target_coverage }); + return std::result::Result::Ok(Self { capabilities, coverage_epochs, gap_ledger, target_coverage }); } /// Revalidates that the run-local contracts still correspond exactly to the caller-composed source count before tasks are spawned. @@ -491,6 +640,9 @@ impl crate::RawTransactionIngestContinuityContracts { if self.capabilities.len() != expected_source_count || self.target_coverage.requirements.is_empty() { return std::result::Result::Err(crate::runtime_error("continuity.source_inventory_mismatch")); } + if let std::result::Result::Err(error) = self.coverage_epochs.validate_invariants(self.capabilities.as_slice()) { + return std::result::Result::Err(error); + } if let std::result::Result::Err(error) = self.gap_ledger.validate_invariants() { return std::result::Result::Err(error); } @@ -510,6 +662,68 @@ fn coverage_scope_catalog_is_complete() -> bool { && known.is_known_references(); } +fn coverage_relation_catalog_is_complete() -> bool { + let target_scope = crate::RawTransactionIngestCoverageScope::ExactSourceScope("standard_logs", [7_u8; 32]); + let target_requirement = + RawTransactionIngestCoverageRequirement { commitment: ksp_onchain_transport_lib::SolanaCommitment::Confirmed, scope: target_scope.clone() }; + let gap = match RawTransactionIngestGapRange::new(100, 110) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return false, + }; + let full_range = match RawTransactionIngestCoverageRange::new(90, 120) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return false, + }; + let partial_range = match RawTransactionIngestCoverageRange::new(100, 109) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return false, + }; + let exact_epoch = + match RawTransactionIngestCoverageEpoch::new(1, [2_u8; 32], ksp_onchain_transport_lib::SolanaCommitment::Confirmed, target_scope, full_range) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return false, + }; + let full_epoch = match RawTransactionIngestCoverageEpoch::new( + 2, + [3_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + crate::RawTransactionIngestCoverageScope::FullLedgerTransactions, + full_range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return false, + }; + let cross_family_epoch = match RawTransactionIngestCoverageEpoch::new( + 3, + [4_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + crate::RawTransactionIngestCoverageScope::ExactSourceScope("helius_transaction", [7_u8; 32]), + full_range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return false, + }; + let partial_epoch = match RawTransactionIngestCoverageEpoch::new( + 4, + [5_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + crate::RawTransactionIngestCoverageScope::FullLedgerTransactions, + partial_range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(_) => return false, + }; + let ledger = RawTransactionIngestCoverageEpochLedger { + epochs: std::vec![exact_epoch.clone(), full_epoch.clone(), cross_family_epoch.clone(), partial_epoch.clone()], + next_epoch_id: 5, + }; + return ledger.redundant_relation_for_gap([1_u8; 32], &target_requirement, gap) + == std::option::Option::Some(RawTransactionIngestCoverageRelation::Superset) + && exact_epoch.relation_to_requirement([2_u8; 32], &target_requirement, gap).is_none() + && cross_family_epoch.relation_to_requirement([1_u8; 32], &target_requirement, gap).is_none() + && partial_epoch.relation_to_requirement([1_u8; 32], &target_requirement, gap).is_none(); +} + #[cfg(test)] #[path = "../unit_tests/continuity.rs"] mod tests; 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 62d9599..28761aa 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs @@ -1,7 +1,7 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs -// version: 28 +// version: 29 -//! External public, security, redaction and release-boundary hardening canaries through `v0.3.14-pre.004`. +//! External public, security, redaction and release-boundary hardening canaries through `v0.3.14-pre.005`. fn network(value: &'static str) -> std::option::Option { let result = ksp_store_lib::RawNetworkId::new(value); @@ -1054,3 +1054,39 @@ fn v0_3_14_pre_004_yellowstone_replay_evidence_is_transport_owned_and_fail_close assert!(!transport.contains("replay_coverage_proven_count")); return; } + +#[test] +fn v0_3_14_pre_005_redundant_coverage_requires_exact_or_superset_proof_over_full_range() { + let continuity = include_str!("../src/continuity.rs"); + let root = include_str!("../src/lib.rs"); + for required in [ + "RawTransactionIngestCoverageRelation", + "RawTransactionIngestCoverageEpoch", + "RawTransactionIngestCoverageEpochLedger", + "MAX_RAW_TRANSACTION_INGEST_COVERAGE_EPOCHS", + "self.source_key == target_source_key", + "self.commitment != requirement.commitment", + "!self.range.contains_gap(gap)", + "family_code == required_family_code && fingerprint == required_fingerprint", + "RawTransactionIngestCoverageRelation::Superset", + ] { + assert!(continuity.contains(required), "required pre.005 conservative coverage guard missing: {required}"); + } + for forbidden in [ + "standard_logs\", \"helius_transaction", + "helius_transaction\", \"standard_logs", + "commitment >= requirement.commitment", + "commitment <= requirement.commitment", + "source_key == target_source_key &&", + ] { + assert!(!continuity.contains(forbidden), "pre.005 introduced opportunistic coverage equivalence: {forbidden}"); + } + for forbidden_public in [ + "pub use self::continuity::RawTransactionIngestCoverageRelation", + "pub use self::continuity::RawTransactionIngestCoverageEpoch", + "pub use self::continuity::RawTransactionIngestTargetCoverage", + ] { + assert!(!root.contains(forbidden_public), "pre.005 leaked private continuity proof type: {forbidden_public}"); + } + 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 4f3e8d4..1da5541 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,7 +1,7 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs -// version: 23 +// version: 24 -//! Release-completeness canaries through the `v0.3.14-pre.004` Yellowstone native replay-evidence tranche. +//! Release-completeness canaries through the `v0.3.14-pre.005` redundant-coverage proof tranche. #[test] fn pre_010_production_module_inventory_is_exact() -> std::io::Result<()> { @@ -263,3 +263,22 @@ fn v0_3_14_pre_004_native_replay_evidence_canaries_are_present_without_public_re assert!(!root.contains("set_from_slot")); return; } + +#[test] +fn v0_3_14_pre_005_redundant_coverage_canaries_are_present_without_public_surface_growth() { + let continuity_tests = include_str!("../unit_tests/continuity.rs"); + let hardening = include_str!("hardening.rs"); + let root = include_str!("../src/lib.rs"); + for required in [ + "pre_005_scope_relations_are_exact_superset_and_cross_family_conservative", + "pre_005_coverage_epoch_requires_distinct_source_same_commitment_and_full_range", + "pre_005_cross_family_exact_scopes_never_become_redundant_by_fingerprint_alone", + "pre_005_coverage_epoch_ledger_is_bounded_monotone_and_capability_bound", + ] { + assert!(continuity_tests.contains(required), "required pre.005 continuity proof canary missing: {required}"); + } + assert!(hardening.contains("v0_3_14_pre_005_redundant_coverage_requires_exact_or_superset_proof_over_full_range")); + assert!(!root.contains("pub use self::continuity::RawTransactionIngestCoverageEpoch")); + assert!(!root.contains("pub use self::continuity::RawTransactionIngestCoverageRelation")); + return; +} diff --git a/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs b/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs index b809e48..277eb53 100644 --- a/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs +++ b/crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs @@ -1,5 +1,5 @@ // file: crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs -// version: 3 +// version: 4 fn network() -> std::option::Option { return match ksp_store_lib::RawNetworkId::new("mainnet") { @@ -275,3 +275,181 @@ fn pre_003_websocket_incident_anchor_rejects_missing_reason_reversal_and_oversiz assert!(oversized.close_at(100 + super::MAX_RAW_TRANSACTION_INGEST_REPAIR_RANGE_SLOTS).is_err()); return; } + +#[test] +fn pre_005_scope_relations_are_exact_superset_and_cross_family_conservative() { + let exact = exact_scope("standard_logs", 7); + let same = exact_scope("standard_logs", 7); + let different_filter = exact_scope("standard_logs", 8); + let different_family = exact_scope("helius_transaction", 7); + let full = crate::RawTransactionIngestCoverageScope::full_ledger_transactions(); + assert_eq!(exact.relation_to(&same), std::option::Option::Some(super::RawTransactionIngestCoverageRelation::Exact)); + assert_eq!(full.relation_to(&exact), std::option::Option::Some(super::RawTransactionIngestCoverageRelation::Superset)); + assert_eq!(full.relation_to(&full), std::option::Option::Some(super::RawTransactionIngestCoverageRelation::Exact)); + assert!(exact.relation_to(&full).is_none()); + assert!(exact.relation_to(&different_filter).is_none()); + assert!(exact.relation_to(&different_family).is_none()); + assert!(crate::RawTransactionIngestCoverageScope::KnownReferences([7_u8; 32]).relation_to(&exact).is_none()); + return; +} + +#[test] +fn pre_005_coverage_epoch_requires_distinct_source_same_commitment_and_full_range() { + let target_requirement = super::RawTransactionIngestCoverageRequirement { + commitment: ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + scope: exact_scope("standard_logs", 9), + }; + let gap = match super::RawTransactionIngestGapRange::new(100, 110) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected gap fixture failure: {error}"), + }; + let full_range = match super::RawTransactionIngestCoverageRange::new(90, 120) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected coverage range failure: {error}"), + }; + let partial_range = match super::RawTransactionIngestCoverageRange::new(100, 109) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected partial coverage range failure: {error}"), + }; + let exact_epoch = match super::RawTransactionIngestCoverageEpoch::new( + 1, + [2_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + exact_scope("standard_logs", 9), + full_range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected exact epoch failure: {error}"), + }; + let full_epoch = match super::RawTransactionIngestCoverageEpoch::new( + 2, + [3_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + crate::RawTransactionIngestCoverageScope::full_ledger_transactions(), + full_range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected full epoch failure: {error}"), + }; + let partial_epoch = match super::RawTransactionIngestCoverageEpoch::new( + 3, + [4_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + crate::RawTransactionIngestCoverageScope::full_ledger_transactions(), + partial_range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected partial epoch failure: {error}"), + }; + let finalized_epoch = match super::RawTransactionIngestCoverageEpoch::new( + 4, + [5_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Finalized, + crate::RawTransactionIngestCoverageScope::full_ledger_transactions(), + full_range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected finalized epoch failure: {error}"), + }; + assert_eq!( + exact_epoch.relation_to_requirement([1_u8; 32], &target_requirement, gap), + std::option::Option::Some(super::RawTransactionIngestCoverageRelation::Exact), + ); + assert_eq!( + full_epoch.relation_to_requirement([1_u8; 32], &target_requirement, gap), + std::option::Option::Some(super::RawTransactionIngestCoverageRelation::Superset), + ); + assert!(exact_epoch.relation_to_requirement([2_u8; 32], &target_requirement, gap).is_none()); + assert!(partial_epoch.relation_to_requirement([1_u8; 32], &target_requirement, gap).is_none()); + assert!(finalized_epoch.relation_to_requirement([1_u8; 32], &target_requirement, gap).is_none()); + return; +} + +#[test] +fn pre_005_cross_family_exact_scopes_never_become_redundant_by_fingerprint_alone() { + let target_requirement = super::RawTransactionIngestCoverageRequirement { + commitment: ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + scope: exact_scope("standard_logs", 11), + }; + let gap = match super::RawTransactionIngestGapRange::new(500, 510) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected gap fixture failure: {error}"), + }; + let range = match super::RawTransactionIngestCoverageRange::new(490, 520) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected coverage range failure: {error}"), + }; + let epoch = match super::RawTransactionIngestCoverageEpoch::new( + 1, + [2_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + exact_scope("helius_transaction", 11), + range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected epoch failure: {error}"), + }; + assert!(epoch.relation_to_requirement([1_u8; 32], &target_requirement, gap).is_none()); + return; +} + +#[test] +fn pre_005_coverage_epoch_ledger_is_bounded_monotone_and_capability_bound() { + assert_eq!(super::MAX_RAW_TRANSACTION_INGEST_COVERAGE_EPOCHS, 256); + let matching = match capability(2, ksp_onchain_transport_lib::SolanaCommitment::Confirmed, exact_scope("standard_logs", 5)) { + std::option::Option::Some(value) => value, + std::option::Option::None => panic!("matching capability fixture unavailable"), + }; + let range = match super::RawTransactionIngestCoverageRange::new(100, 200) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected coverage range failure: {error}"), + }; + let epoch = match super::RawTransactionIngestCoverageEpoch::new( + 1, + [2_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + exact_scope("standard_logs", 5), + range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected coverage epoch failure: {error}"), + }; + let valid = super::RawTransactionIngestCoverageEpochLedger { epochs: std::vec![epoch.clone()], next_epoch_id: 2 }; + assert!(valid.validate_invariants(std::slice::from_ref(&matching)).is_ok()); + let requirement = super::RawTransactionIngestCoverageRequirement { + commitment: ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + scope: exact_scope("standard_logs", 5), + }; + let gap = match super::RawTransactionIngestGapRange::new(120, 130) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected ledger gap failure: {error}"), + }; + assert_eq!( + valid.redundant_relation_for_gap([1_u8; 32], &requirement, gap), + std::option::Option::Some(super::RawTransactionIngestCoverageRelation::Exact), + ); + assert!(valid.redundant_relation_for_gap([2_u8; 32], &requirement, gap).is_none()); + let stale = super::RawTransactionIngestCoverageEpochLedger { epochs: std::vec![epoch.clone()], next_epoch_id: 1 }; + assert!(stale.validate_invariants(std::slice::from_ref(&matching)).is_err()); + let unknown = super::RawTransactionIngestCoverageEpochLedger { epochs: std::vec![epoch], next_epoch_id: 2 }; + assert!(unknown.validate_invariants(&[]).is_err()); + let mut too_many = std::vec::Vec::new(); + for index in 0..=super::MAX_RAW_TRANSACTION_INGEST_COVERAGE_EPOCHS { + let epoch_id = (index as u64) + 1; + let epoch = match super::RawTransactionIngestCoverageEpoch::new( + epoch_id, + [2_u8; 32], + ksp_onchain_transport_lib::SolanaCommitment::Confirmed, + exact_scope("standard_logs", 5), + range, + ) { + std::result::Result::Ok(value) => value, + std::result::Result::Err(error) => panic!("unexpected bounded epoch failure: {error}"), + }; + too_many.push(epoch); + } + let overflow = + super::RawTransactionIngestCoverageEpochLedger { epochs: too_many, next_epoch_id: (super::MAX_RAW_TRANSACTION_INGEST_COVERAGE_EPOCHS as u64) + 2 }; + assert!(overflow.validate_invariants(std::slice::from_ref(&matching)).is_err()); + return; +} diff --git a/deltas/0.3.14/pre.005.md b/deltas/0.3.14/pre.005.md new file mode 100644 index 0000000..4e4f147 --- /dev/null +++ b/deltas/0.3.14/pre.005.md @@ -0,0 +1,320 @@ + + + +# Delta `0.3.14-pre.005` — preuve conservative de coverage redondante + +## Base requise + +```text +0.3.14-pre.004 +workspace.package.version = 0.3.14-pre.4 +deltas/0.3.14/pre.004.md présent +``` + +## Gate de la base + +Le gate opérateur de `0.3.14-pre.004` est validé avant ouverture de cette tranche : + +```text +cargo fmt --all : PASS +cargo fmt --all -- --check : PASS +audit Rust workspace rules : PASS +audit Markdown tables : PASS +cargo check --workspace : PASS +cargo clippy --workspace --all-targets --all-features -- -D warnings : PASS +cargo test -p ksp-onchain-transport-lib --all-targets --all-features : PASS +cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features : PASS +``` + +Le gate Transport comprend notamment : + +```text +391 unit tests : PASS +public_api : 53 PASS +release_completeness : 46 PASS +smokes live : ignorés par défaut conformément au contrat existant +``` + +Le gate Worker comprend notamment : + +```text +120 unit tests : PASS +cross_layer_completeness : 8 PASS +dependency_boundary : 19 PASS +hardening : 29 PASS +public_api : 20 PASS +release_completeness : 6 PASS +``` + +## Objectif + +Implémenter strictement la tranche `pre.005` du plan `035` : + +```text +matérialiser les relations conservative Exact / Superset +matérialiser des epochs et ranges de coverage run-local bornés +n'autoriser une preuve redondante que depuis une source distincte +exiger un commitment strictement identique +exiger que l'epoch couvre intégralement la plage du gap +interdire toute équivalence opportuniste entre familles exactes différentes +conserver KnownReferences impropre à TargetCoverage +ne modifier encore aucune décision runtime de supervisor +ne déclencher aucun repair HTTP et aucun replay supplémentaire +``` + +## Relations de coverage + +`RawTransactionIngestCoverageScope` possède maintenant une relation privée et directionnelle : + +```text +FullLedgerTransactions -> FullLedgerTransactions : Exact +FullLedgerTransactions -> ExactSourceScope(...) : Superset +ExactSourceScope(family, fingerprint) + -> ExactSourceScope(même family, même fingerprint) : Exact +autres combinaisons : aucune relation prouvée +``` + +Cette relation est volontairement conservative. + +Deux scopes exacts qui partagent le même fingerprint mais pas le même `family_code` ne sont jamais considérés équivalents. Ainsi, par exemple, aucune correspondance `standard_logs <-> helius_transaction` ou `standard_block <-> yellowstone` n'est inventée à partir d'une similarité de sélecteur. + +La relation est également indépendante de l'identité provider/endpoint : cette indépendance vient uniquement du scope sémantique déjà normalisé, tandis que l'obligation de redondance exige une `source_key` différente. + +## Commitments + +La redondance requiert un commitment strictement identique : + +```text +Confirmed couvre seulement Confirmed +Finalized couvre seulement Finalized +``` + +Aucune hiérarchie `Confirmed <-> Finalized` n'est inférée dans cette tranche. + +## TargetCoverage + +La construction de `RawTransactionIngestTargetCoverage` réutilise désormais la relation conservative plutôt qu'une comparaison ad hoc : + +```text +un FullLedgerTransactions au même commitment subsume les requirements exacts +un requirement exact identique est dédupliqué +un scope exact cross-family reste distinct +un commitment différent reste distinct +KnownReferences reste interdit comme target scope +``` + +Aucune broadening cross-family n'est introduite. + +## Epochs et ranges de coverage + +La tranche matérialise des contrats privés : + +```text +RawTransactionIngestCoverageRange +RawTransactionIngestCoverageEpoch +RawTransactionIngestCoverageEpochLedger +RawTransactionIngestCoverageRelation +``` + +Un `CoverageRange` est inclusif et rejette uniquement une plage inversée. Contrairement au `GapRange`, il n'est pas limité artificiellement à `MAX_RAW_TRANSACTION_INGEST_REPAIR_RANGE_SLOTS`, car un epoch de continuité saine peut être plus long qu'une seule obligation de repair. + +Un epoch conserve : + +```text +epoch_id +source_key +commitment +scope +range inclusif +``` + +Le ledger est run-local et borné à : + +```text +MAX_RAW_TRANSACTION_INGEST_COVERAGE_EPOCHS = 256 +``` + +Il vérifie : + +```text +IDs non nuls et uniques +next_epoch_id strictement monotone +borne de cardinalité +range valide +scope admissible comme configured target scope +source_key présente dans l'inventaire exact des capabilities +commitment et scope strictement identiques à la capability de cette source +``` + +Le ledger est initialisé vide avec les autres continuity contracts. Aucune coverage n'est pré-déclarée à partir de la simple configuration d'une source. + +## Preuve de redondance + +Un epoch peut prouver une obligation uniquement si toutes les conditions suivantes sont vraies : + +```text +source de preuve != source cible +commitment de preuve == commitment requis +range de preuve contient intégralement le GapRange inclusif +scope de preuve relation Exact ou Superset avec le scope requis +``` + +Un epoch partiel, une source identique, un commitment différent, un filtre différent ou une famille différente ne ferment donc aucune obligation. + +`FullLedgerTransactions` peut fournir une preuve `Superset` d'un scope exact au même commitment, parce qu'il représente toutes les transactions de tous les blocs produits. L'inverse est interdit. + +## Activation runtime + +Cette tranche ne change pas encore la politique de source-loss/reconciliation : + +```text +aucun gap n'est fermé automatiquement +aucune source n'est maintenue en vie grâce à cette preuve +aucune terminalité fail-closed de pre.003/pre.004 n'est encore remplacée +``` + +`pre.008` restera responsable de l'intégration de ces preuves dans le supervisor et le gap ledger. `pre.005` fournit uniquement le modèle conservative nécessaire. + +## Tests ajoutés + +Les unit tests Worker couvrent : + +```text +Exact pour même famille + même fingerprint +Superset uniquement FullLedger -> exact +absence de relation exact -> FullLedger +absence de relation pour filtre exact différent +absence de relation cross-family malgré fingerprint identique +source distincte obligatoire +commitment strictement identique +range complet obligatoire +ledger borné à 256 epochs +next_epoch_id monotone +capability source/scope/commitment obligatoire +``` + +Les canaris `hardening` et `release_completeness` garantissent en plus : + +```text +présence des contrats Exact/Superset/epoch +absence d'équivalence cross-family codée en dur +absence de hiérarchie opportuniste de commitment +absence d'exposition publique des nouveaux types de preuve +``` + +## Fichiers ajoutés + +```text +deltas/0.3.14/pre.005.md +``` + +## Fichiers modifiés + +```text +Cargo.toml +crates/ksp-worker-raw-transaction-ingest-lib/src/continuity.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs +crates/ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs +crates/ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs +``` + +## Fichiers supprimés + +```text +aucun +``` + +## Version Cargo + +Conformément à `VER-ID-009`, cette nouvelle prerelease synchronise la version workspace : + +```text +header Cargo.toml : 571 -> 572 +workspace.package.version : 0.3.14-pre.4 -> 0.3.14-pre.5 +``` + +Versions des fichiers modifiés : + +```text +ksp-worker-raw-transaction-ingest-lib/src/continuity.rs : 4 -> 5 +ksp-worker-raw-transaction-ingest-lib/tests/hardening.rs : 28 -> 29 +ksp-worker-raw-transaction-ingest-lib/tests/release_completeness.rs : 23 -> 24 +ksp-worker-raw-transaction-ingest-lib/unit_tests/continuity.rs : 3 -> 4 +``` + +## Frontières préservées + +```text +aucun changement Transport +aucun changement Config +aucun changement Store +aucun changement Job Backfill +aucune nouvelle dépendance +aucune nouvelle feature +aucun repair HTTP +aucun replay Worker +aucun nouveau socket/actor/retry +aucun set_from_slot dans Worker +aucune nouvelle surface publique Worker +aucune équivalence cross-family opportuniste +aucune hiérarchie implicite de commitment +``` + +## Validations exécutées dans le sandbox de préparation + +```text +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 +scan des frontières et de la surface crate-root Worker +comparaison exacte 0.3.14-pre.004 -> 0.3.14-pre.005 +contrôle des versions de fichiers modifiés +contrôle du contenu de l'archive delta +unzip -t de l'archive delta +réapplication de l'archive sur la base puis comparaison byte-exacte +``` + +## Validations non exécutées dans le sandbox de préparation + +Le sandbox ne fournit pas le toolchain Cargo/Rust. Les gates suivants restent à exécuter côté opérateur : + +```text +cargo fmt --all +cargo fmt --all -- --check +cargo check --workspace +cargo clippy --workspace --all-targets --all-features -- -D warnings +cargo test -p ksp-worker-raw-transaction-ingest-lib --all-targets --all-features +``` + +## Décisions prises + +```text +coverage relationnelle directionnelle, jamais symétrisée implicitement +FullLedger est un superset d'un exact scope au même commitment +un exact scope n'est équivalent qu'à même family + même fingerprint +une redondance doit provenir d'une source_key distincte +un epoch doit contenir toute la plage du gap +aucune coverage initiale n'est créée depuis la configuration seule +pre.005 ne modifie pas encore la terminalité runtime +``` + +## Questions ouvertes + +```text +aucune pour pre.005 +``` + +## Tranche suivante + +`pre.006` reste dédiée au scan HTTP de réparation : détection des capabilities run-wide réellement disponibles, fenêtres bornées, découverte `getBlocks`/`getBlocksWithLimit` conservative et récupération `getBlock observed`, sans broadening de scope. + +## Gate opérateur après application + +```bash +cargo fmt --all +cargo fmt --all -- --check +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 --all-targets --all-features +```