v0.3.9-pre.003

This commit is contained in:
2026-09-04 14:45:05 +02:00
parent 84647c12ea
commit bb7ee3cc3a
9 changed files with 946 additions and 24 deletions

View File

@@ -0,0 +1,161 @@
// file: crates/ksp-worker-api/tests/release_completeness.rs
// version: 1
//! Exact frozen-surface and domain-firewall canaries for `ksp-worker-api`.
#[test]
fn pre_003_crate_root_export_inventory_is_exact() {
let crate_root = include_str!("../src/lib.rs");
let mut actual = std::vec::Vec::new();
for line in crate_root.lines() {
let trimmed = line.trim();
if trimmed.starts_with("pub use ") {
actual.push(trimmed);
}
}
actual.sort_unstable();
let mut expected = std::vec![
"pub use self::error::ERROR_CODE_WORKER_ID_INVALID;",
"pub use self::error::ERROR_CODE_WORKER_KIND_INVALID;",
"pub use self::error::ERROR_CODE_WORKER_SNAPSHOT_SEQUENCE_EXHAUSTED;",
"pub use self::error::ERROR_CODE_WORKER_TRANSITION_INVALID;",
"pub use self::identity::MAX_WORKER_ID_BYTES;",
"pub use self::identity::MAX_WORKER_KIND_CODE_BYTES;",
"pub use self::identity::WorkerId;",
"pub use self::identity::WorkerKindCode;",
"pub use self::lifecycle::WorkerActivity;",
"pub use self::lifecycle::WorkerHealth;",
"pub use self::lifecycle::WorkerLifecycle;",
"pub use self::lifecycle::WorkerState;",
"pub use self::snapshot::WorkerSnapshot;",
"pub use self::snapshot::WorkerSnapshotFuture;",
"pub use self::snapshot::WorkerSnapshotSequence;",
"pub use self::snapshot::WorkerSnapshotSource;",
"pub use self::stop::WorkerStopToken;",
"pub use ksp_core_lib::Error;",
"pub use ksp_core_lib::ErrorCode;",
"pub use ksp_core_lib::ErrorContext;",
"pub use ksp_core_lib::Result;",
];
expected.sort_unstable();
assert_eq!(actual, expected);
assert!(!crate_root.contains("pub mod "));
return;
}
#[test]
fn pre_003_production_module_inventory_is_exact() -> std::io::Result<()> {
let source_root = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("src");
let entries = match std::fs::read_dir(source_root) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let mut names = std::vec::Vec::new();
for entry in entries {
let entry = match entry {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let file_type = match entry.file_type() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
if !file_type.is_file() {
continue;
}
let name = match entry.file_name().into_string() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => continue,
};
if name.ends_with(".rs") {
names.push(name);
}
}
names.sort_unstable();
assert_eq!(names, std::vec!["error.rs", "identity.rs", "lib.rs", "lifecycle.rs", "snapshot.rs", "stop.rs"]);
return std::result::Result::Ok(());
}
#[test]
fn pre_003_fixed_snapshot_shape_contains_only_common_worker_dimensions() {
let snapshot = include_str!("../src/snapshot.rs");
let struct_start = match snapshot.find("pub struct WorkerSnapshot {") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let after_start = &snapshot[struct_start..];
let struct_end = match after_start.find("\n}") {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let definition = &after_start[..struct_end];
for required in ["id:", "kind:", "sequence:", "state:", "health:", "activity:"] {
assert!(definition.contains(required), "missing frozen Worker snapshot field: {required}");
}
for forbidden in ["String", "Vec<", "payload", "slot", "signature", "provider", "endpoint", "checkpoint", "backfill", "transaction"] {
assert!(!definition.contains(forbidden), "domain or arbitrary payload leaked into frozen Worker snapshot: {forbidden}");
}
let fields = definition.lines().filter(|line| {
let line = line.trim_start();
return line.starts_with("id:")
|| line.starts_with("kind:")
|| line.starts_with("sequence:")
|| line.starts_with("state:")
|| line.starts_with("health:")
|| line.starts_with("activity:");
});
assert_eq!(fields.count(), 6);
return;
}
#[test]
fn pre_003_surface_remains_runtime_neutral_job_independent_and_domain_free() {
let sources = [
include_str!("../src/error.rs"),
include_str!("../src/identity.rs"),
include_str!("../src/lib.rs"),
include_str!("../src/lifecycle.rs"),
include_str!("../src/snapshot.rs"),
include_str!("../src/stop.rs"),
];
for source in sources {
for forbidden in [
"Backfill",
"RawTransaction",
"WorkerHandle",
"checkpoint",
"endpoint",
"futures::",
"futures_util::",
"ksp_config_lib::",
"ksp_interface_lib::",
"ksp_job_api::",
"ksp_logging_lib::",
"ksp_offchain_transport_lib::",
"ksp_onchain_transport_lib::",
"ksp_store_api::",
"ksp_store_lib::",
"provider",
"reqwest::",
"serde::",
"serde_json::",
"slot",
"solana_",
"tauri::",
"tokio::",
"tonic::",
concat!("tracing", "::"),
] {
assert!(!source.contains(forbidden), "forbidden frozen Worker API concern detected: {forbidden}");
}
}
let snapshot = include_str!("../src/snapshot.rs");
assert!(snapshot.contains("pub trait WorkerSnapshotSource: std::marker::Send + std::marker::Sync"));
assert!(snapshot.contains("std::future::Future"));
assert!(!snapshot.contains("std::sync::mpsc"));
assert!(!snapshot.contains("VecDeque"));
let lifecycle = include_str!("../src/lifecycle.rs");
assert!(lifecycle.contains("#[derive(Eq, PartialEq)]\npub struct WorkerLifecycle"));
assert!(!lifecycle.contains("#[derive(Clone, Eq, PartialEq)]\npub struct WorkerLifecycle"));
return;
}

View File

@@ -0,0 +1,185 @@
// file: crates/ksp-worker-api/tests/security_hardening.rs
// version: 1
//! Adversarial lifecycle, stop and redaction canaries for the frozen Worker API.
const HOSTILE_MARKER: &str = "WORKER-IDENTITY-SECRET-CANARY";
const TEST_FAULT: ksp_worker_api::ErrorCode = ksp_worker_api::ErrorCode::new("worker_test", "fault");
fn running_lifecycle(id_value: &str) -> std::option::Option<ksp_worker_api::WorkerLifecycle> {
let id = match ksp_worker_api::WorkerId::new(id_value) {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let kind = match ksp_worker_api::WorkerKindCode::new("continuous_worker") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let mut lifecycle = ksp_worker_api::WorkerLifecycle::new(id, kind);
if lifecycle.start().is_err() {
return std::option::Option::None;
}
if lifecycle.mark_running().is_err() {
return std::option::Option::None;
}
return std::option::Option::Some(lifecycle);
}
fn assert_all_mutators_reject(lifecycle: &mut ksp_worker_api::WorkerLifecycle, terminal: ksp_worker_api::WorkerState) {
assert!(lifecycle.start().is_err());
assert!(lifecycle.mark_running().is_err());
assert!(lifecycle.mark_stopping().is_err());
assert!(lifecycle.mark_stopped().is_err());
assert!(lifecycle.fault(TEST_FAULT).is_err());
assert_eq!(lifecycle.state(), terminal);
return;
}
#[test]
fn pre_003_worker_identity_lifecycle_and_snapshot_debug_redact_hostile_identity() {
let lifecycle = running_lifecycle(HOSTILE_MARKER);
assert!(lifecycle.is_some());
let lifecycle = match lifecycle {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
let id = lifecycle.id();
assert_eq!(std::format!("{id:?}"), "WorkerId(..)");
let lifecycle_debug = std::format!("{lifecycle:?}");
assert!(lifecycle_debug.contains("WorkerLifecycle"));
assert!(lifecycle_debug.contains("continuous_worker"));
assert!(lifecycle_debug.contains("Running"));
assert!(!lifecycle_debug.contains(HOSTILE_MARKER));
let snapshot = ksp_worker_api::WorkerSnapshot::new(
lifecycle.id().clone(),
lifecycle.kind().clone(),
ksp_worker_api::WorkerSnapshotSequence::initial(),
lifecycle.state(),
ksp_worker_api::WorkerHealth::Healthy,
ksp_worker_api::WorkerActivity::Active,
);
let snapshot_debug = std::format!("{snapshot:?}");
assert!(snapshot_debug.contains("WorkerSnapshot"));
assert!(snapshot_debug.contains("continuous_worker"));
assert!(snapshot_debug.contains("Healthy"));
assert!(snapshot_debug.contains("Active"));
assert!(!snapshot_debug.contains(HOSTILE_MARKER));
return;
}
#[test]
fn pre_003_identity_errors_never_echo_hostile_values() {
let hostile = "WORKER-SECRET/CANARY";
let rejected = ksp_worker_api::WorkerId::new(hostile);
assert!(rejected.is_err());
let error = match rejected {
std::result::Result::Err(value) => value,
std::result::Result::Ok(_) => return,
};
assert_eq!(error.code(), ksp_worker_api::ERROR_CODE_WORKER_ID_INVALID);
assert!(!std::format!("{error}").contains(hostile));
assert!(!std::format!("{error:?}").contains(hostile));
for context in error.context() {
assert!(!context.value().contains(hostile));
}
return;
}
#[test]
fn pre_003_stopped_and_faulted_are_immutable_under_all_public_mutators() {
let id = match ksp_worker_api::WorkerId::new("terminal-created") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let kind = match ksp_worker_api::WorkerKindCode::new("terminal_worker") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
let mut stopped = ksp_worker_api::WorkerLifecycle::new(id, kind);
assert!(stopped.mark_stopped().is_ok());
assert_all_mutators_reject(&mut stopped, ksp_worker_api::WorkerState::Stopped);
let faulted = running_lifecycle("terminal-faulted");
assert!(faulted.is_some());
let mut faulted = match faulted {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(faulted.fault(TEST_FAULT).is_ok());
assert_all_mutators_reject(&mut faulted, ksp_worker_api::WorkerState::Faulted(TEST_FAULT));
return;
}
#[test]
fn pre_003_stop_fault_terminal_orders_are_first_valid_terminal_wins() {
let direct_fault = running_lifecycle("race-direct-fault");
assert!(direct_fault.is_some());
let mut direct_fault = match direct_fault {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(direct_fault.fault(TEST_FAULT).is_ok());
assert!(direct_fault.mark_stopping().is_err());
assert_eq!(direct_fault.state(), ksp_worker_api::WorkerState::Faulted(TEST_FAULT));
let stopping_fault = running_lifecycle("race-stopping-fault");
assert!(stopping_fault.is_some());
let mut stopping_fault = match stopping_fault {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(stopping_fault.mark_stopping().is_ok());
assert!(stopping_fault.fault(TEST_FAULT).is_ok());
assert!(stopping_fault.mark_stopped().is_err());
assert_eq!(stopping_fault.state(), ksp_worker_api::WorkerState::Faulted(TEST_FAULT));
let stopping_stopped = running_lifecycle("race-stopping-stopped");
assert!(stopping_stopped.is_some());
let mut stopping_stopped = match stopping_stopped {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(stopping_stopped.mark_stopping().is_ok());
assert!(stopping_stopped.mark_stopped().is_ok());
assert!(stopping_stopped.fault(TEST_FAULT).is_err());
assert_eq!(stopping_stopped.state(), ksp_worker_api::WorkerState::Stopped);
return;
}
#[test]
fn pre_003_stop_token_is_send_sync_cross_thread_and_cannot_reanimate_terminal_lifecycle() {
fn require_send_sync<T: std::marker::Send + std::marker::Sync>() {}
require_send_sync::<ksp_worker_api::WorkerStopToken>();
let lifecycle = running_lifecycle("cross-thread-stop");
assert!(lifecycle.is_some());
let mut lifecycle = match lifecycle {
std::option::Option::Some(value) => value,
std::option::Option::None => return,
};
assert!(lifecycle.fault(TEST_FAULT).is_ok());
let token = ksp_worker_api::WorkerStopToken::new();
let worker_token = token.clone();
let thread = std::thread::spawn(move || return worker_token.request_stop());
let first = match thread.join() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return,
};
assert!(first);
assert!(token.is_stop_requested());
assert!(!token.request_stop());
assert!(lifecycle.mark_stopping().is_err());
assert_eq!(lifecycle.state(), ksp_worker_api::WorkerState::Faulted(TEST_FAULT));
return;
}
#[test]
fn pre_003_public_worker_primitives_keep_promised_send_sync_contracts() {
fn require_send_sync<T: std::marker::Send + std::marker::Sync>() {}
require_send_sync::<ksp_worker_api::WorkerId>();
require_send_sync::<ksp_worker_api::WorkerKindCode>();
require_send_sync::<ksp_worker_api::WorkerState>();
require_send_sync::<ksp_worker_api::WorkerHealth>();
require_send_sync::<ksp_worker_api::WorkerActivity>();
require_send_sync::<ksp_worker_api::WorkerLifecycle>();
require_send_sync::<ksp_worker_api::WorkerSnapshotSequence>();
require_send_sync::<ksp_worker_api::WorkerSnapshot>();
require_send_sync::<ksp_worker_api::WorkerStopToken>();
return;
}

View File

@@ -0,0 +1,219 @@
// file: crates/ksp-worker-api/tests/snapshot_source.rs
// version: 1
//! External std-only latest-value source, object-safety and resynchronization canaries.
#[derive(Clone)]
struct TestSnapshotSource {
state: std::sync::Arc<TestState>,
}
struct TestState {
current: std::sync::Mutex<TestInner>,
}
struct TestInner {
snapshot: ksp_worker_api::WorkerSnapshot,
waiters: std::vec::Vec<std::task::Waker>,
}
impl TestSnapshotSource {
fn new(snapshot: ksp_worker_api::WorkerSnapshot) -> Self {
return Self {
state: std::sync::Arc::new(TestState { current: std::sync::Mutex::new(TestInner { snapshot, waiters: std::vec::Vec::new() }) }),
};
}
fn publish(&self, state: ksp_worker_api::WorkerState, health: ksp_worker_api::WorkerHealth, activity: ksp_worker_api::WorkerActivity) -> bool {
let mut current = match self.state.current.lock() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(poisoned) => poisoned.into_inner(),
};
let sequence = match current.snapshot.sequence().next() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return false,
};
let id = current.snapshot.id().clone();
let kind = current.snapshot.kind().clone();
current.snapshot = ksp_worker_api::WorkerSnapshot::new(id, kind, sequence, state, health, activity);
let waiters = std::mem::take(&mut current.waiters);
std::mem::drop(current);
for waiter in waiters {
waiter.wake();
}
return true;
}
}
struct TestWaitFuture<'a> {
source: &'a TestSnapshotSource,
observed: ksp_worker_api::WorkerSnapshotSequence,
}
impl std::future::Future for TestWaitFuture<'_> {
type Output = ksp_worker_api::WorkerSnapshot;
fn poll(self: std::pin::Pin<&mut Self>, context: &mut std::task::Context<'_>) -> std::task::Poll<Self::Output> {
let mut current = match self.source.state.current.lock() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(poisoned) => poisoned.into_inner(),
};
if current.snapshot.sequence().is_after(self.observed) {
return std::task::Poll::Ready(current.snapshot.clone());
}
if !current.waiters.iter().any(|registered| return registered.will_wake(context.waker())) {
current.waiters.push(context.waker().clone());
}
return std::task::Poll::Pending;
}
}
impl ksp_worker_api::WorkerSnapshotSource for TestSnapshotSource {
fn current(&self) -> ksp_worker_api::WorkerSnapshot {
let current = match self.state.current.lock() {
std::result::Result::Ok(value) => value,
std::result::Result::Err(poisoned) => poisoned.into_inner(),
};
return current.snapshot.clone();
}
fn wait_for_change(&self, observed: ksp_worker_api::WorkerSnapshotSequence) -> ksp_worker_api::WorkerSnapshotFuture<'_> {
return std::boxed::Box::pin(TestWaitFuture { source: self, observed });
}
}
struct WakeProbe {
woken: std::sync::atomic::AtomicBool,
}
impl WakeProbe {
fn new() -> Self {
return Self { woken: std::sync::atomic::AtomicBool::new(false) };
}
fn is_woken(&self) -> bool {
return self.woken.load(std::sync::atomic::Ordering::Acquire);
}
}
impl std::task::Wake for WakeProbe {
fn wake(self: std::sync::Arc<Self>) {
self.woken.store(true, std::sync::atomic::Ordering::Release);
return;
}
}
fn poll_snapshot(future: &mut ksp_worker_api::WorkerSnapshotFuture<'_>, wake: &std::sync::Arc<WakeProbe>) -> std::task::Poll<ksp_worker_api::WorkerSnapshot> {
let waker = std::task::Waker::from(wake.clone());
let mut context = std::task::Context::from_waker(&waker);
return std::future::Future::poll(future.as_mut(), &mut context);
}
fn initial_snapshot() -> std::option::Option<ksp_worker_api::WorkerSnapshot> {
let id = match ksp_worker_api::WorkerId::new("external-source-001") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
let kind = match ksp_worker_api::WorkerKindCode::new("external_test_worker") {
std::result::Result::Ok(value) => value,
std::result::Result::Err(_) => return std::option::Option::None,
};
return std::option::Option::Some(ksp_worker_api::WorkerSnapshot::new(
id,
kind,
ksp_worker_api::WorkerSnapshotSequence::initial(),
ksp_worker_api::WorkerState::Running,
ksp_worker_api::WorkerHealth::Healthy,
ksp_worker_api::WorkerActivity::Idle,
));
}
#[test]
fn pre_003_snapshot_source_is_object_safe_send_sync_and_externally_implementable() {
fn require_send_sync<T: std::marker::Send + std::marker::Sync>() {}
require_send_sync::<TestSnapshotSource>();
let snapshot = initial_snapshot();
assert!(snapshot.is_some());
let source = match snapshot {
std::option::Option::Some(value) => TestSnapshotSource::new(value),
std::option::Option::None => return,
};
let object: &dyn ksp_worker_api::WorkerSnapshotSource = &source;
let current = object.current();
assert_eq!(current.sequence().value(), 0);
let mut wait = object.wait_for_change(current.sequence());
fn require_send<T: std::marker::Send>(_: &T) {}
require_send(&wait);
let wake = std::sync::Arc::new(WakeProbe::new());
assert!(matches!(poll_snapshot(&mut wait, &wake), std::task::Poll::Pending));
assert!(source.publish(ksp_worker_api::WorkerState::Running, ksp_worker_api::WorkerHealth::Healthy, ksp_worker_api::WorkerActivity::Active));
assert!(wake.is_woken());
let changed = poll_snapshot(&mut wait, &wake);
assert!(matches!(changed, std::task::Poll::Ready(_)));
return;
}
#[test]
fn pre_003_slow_and_independent_listeners_coalesce_to_latest_value() {
let snapshot = initial_snapshot();
assert!(snapshot.is_some());
let source = match snapshot {
std::option::Option::Some(value) => TestSnapshotSource::new(value),
std::option::Option::None => return,
};
let observed = ksp_worker_api::WorkerSnapshotSource::current(&source).sequence();
let mut slow = ksp_worker_api::WorkerSnapshotSource::wait_for_change(&source, observed);
let mut fast = ksp_worker_api::WorkerSnapshotSource::wait_for_change(&source, observed);
let slow_wake = std::sync::Arc::new(WakeProbe::new());
let fast_wake = std::sync::Arc::new(WakeProbe::new());
assert!(matches!(poll_snapshot(&mut slow, &slow_wake), std::task::Poll::Pending));
assert!(matches!(poll_snapshot(&mut fast, &fast_wake), std::task::Poll::Pending));
assert!(source.publish(ksp_worker_api::WorkerState::Running, ksp_worker_api::WorkerHealth::Degraded, ksp_worker_api::WorkerActivity::Active));
assert!(slow_wake.is_woken());
assert!(fast_wake.is_woken());
let fast_value = match poll_snapshot(&mut fast, &fast_wake) {
std::task::Poll::Ready(value) => value,
std::task::Poll::Pending => return,
};
assert_eq!(fast_value.sequence().value(), 1);
assert_eq!(fast_value.health(), ksp_worker_api::WorkerHealth::Degraded);
assert!(source.publish(ksp_worker_api::WorkerState::Running, ksp_worker_api::WorkerHealth::Healthy, ksp_worker_api::WorkerActivity::Idle));
let slow_value = match poll_snapshot(&mut slow, &slow_wake) {
std::task::Poll::Ready(value) => value,
std::task::Poll::Pending => return,
};
assert_eq!(slow_value.sequence().value(), 2);
assert_eq!(slow_value.health(), ksp_worker_api::WorkerHealth::Healthy);
assert_eq!(slow_value.activity(), ksp_worker_api::WorkerActivity::Idle);
return;
}
#[test]
fn pre_003_late_listener_resynchronizes_and_terminal_snapshot_remains_current() {
let snapshot = initial_snapshot();
assert!(snapshot.is_some());
let source = match snapshot {
std::option::Option::Some(value) => TestSnapshotSource::new(value),
std::option::Option::None => return,
};
assert!(source.publish(ksp_worker_api::WorkerState::Running, ksp_worker_api::WorkerHealth::Degraded, ksp_worker_api::WorkerActivity::Active));
assert!(source.publish(ksp_worker_api::WorkerState::Stopping, ksp_worker_api::WorkerHealth::Healthy, ksp_worker_api::WorkerActivity::Idle));
let current = ksp_worker_api::WorkerSnapshotSource::current(&source);
assert_eq!(current.sequence().value(), 2);
assert_eq!(current.state(), ksp_worker_api::WorkerState::Stopping);
let mut wait = ksp_worker_api::WorkerSnapshotSource::wait_for_change(&source, current.sequence());
let wake = std::sync::Arc::new(WakeProbe::new());
assert!(matches!(poll_snapshot(&mut wait, &wake), std::task::Poll::Pending));
assert!(source.publish(ksp_worker_api::WorkerState::Stopped, ksp_worker_api::WorkerHealth::Healthy, ksp_worker_api::WorkerActivity::Idle));
assert!(wake.is_woken());
let terminal = match poll_snapshot(&mut wait, &wake) {
std::task::Poll::Ready(value) => value,
std::task::Poll::Pending => return,
};
assert_eq!(terminal.sequence().value(), 3);
assert_eq!(terminal.state(), ksp_worker_api::WorkerState::Stopped);
assert!(terminal.state().is_terminal());
let retained = ksp_worker_api::WorkerSnapshotSource::current(&source);
assert_eq!(retained, terminal);
return;
}