Skip to main content

dsfb_densor_runtime/
lib.rs

1//! `dsfb-densor-runtime` — a thin, deterministic execution-substrate skeleton for DSFB densor pipelines.
2//!
3//! Its only job is to standardise how DSFB evidence objects ("densors") are **loaded, validated against frozen
4//! authority hashes, executed stage by stage, sealed, and reported** — so future DSFB work shares one disciplined
5//! spine instead of re-implementing it. The flow is exactly:
6//!
7//! ```text
8//! load manifest  ->  validate authority hashes  ->  execute stages  ->  seal evidence  ->  emit receipt
9//! ```
10//!
11//! **Discipline (the substrate is deliberately small and strict):**
12//! - no network, no hidden downloads, no filesystem side effects in the core;
13//! - no probabilistic runtime behaviour — every output is a pure function of the inputs;
14//! - no authority mutation during execution (authorities are a frozen allow-list);
15//! - **no claim without an authority hash** — a stage that declares no authority is refused.
16//!
17//! **Non-claim.** This crate is a *mechanism*, not an authority. It carries **no chemical-engineering content and
18//! makes no cross-domain claims** — the meaning of any pipeline lives entirely in the authorities a run cites and
19//! in the domain crate that defines the stages (e.g. `dsfb-chemical-engineering-edge`). The runtime only attests
20//! *which stages ran over which densors against which frozen authorities, sealing to which digest*.
21//!
22//! See [`runtime::Runtime`] for the spine, [`receipt::RuntimeReceiptV1`] for the sealed output, and the worked
23//! `tests` below for a complete two-stage example (with determinism + tamper + authority-gate coverage).
24
25#![forbid(unsafe_code)]
26
27pub mod authority;
28pub mod densor;
29pub mod errors;
30pub mod index;
31pub mod manifest;
32pub mod receipt;
33pub mod runtime;
34pub mod seal;
35pub mod stage;
36
37pub use authority::AuthorityHash;
38pub use densor::{Densor, DensorKind};
39pub use errors::{DensorError, RuntimeError};
40pub use index::RuntimeIndex;
41pub use manifest::{DensorEntry, DensorManifest};
42pub use receipt::RuntimeReceiptV1;
43pub use runtime::Runtime;
44pub use stage::{RuntimeStage, StageReceipt};
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49    use crate::seal::{sha256, CanonicalHasher};
50
51    // ── A worked two-stage example pipeline over a residual-like Vec<i64> ──────────────────────────────
52    // Stage A scales the input by a fixed factor (a "scale policy"); Stage B counts entries beyond a band
53    // (an "envelope"). Both are pure + deterministic, each bound to one frozen authority. This is illustrative
54    // only — it carries no chemical meaning; it exercises the substrate's load/validate/execute/seal/emit spine.
55
56    fn scale_authority() -> AuthorityHash {
57        AuthorityHash::new("scale_policy_v1", sha256(b"scale=1000000"))
58    }
59    fn envelope_authority() -> AuthorityHash {
60        AuthorityHash::new("envelope_v1", sha256(b"abs_band=3"))
61    }
62
63    fn hash_i64s(label: &str, xs: &[i64]) -> [u8; 32] {
64        let mut h = CanonicalHasher::new();
65        h.field("schema", label.as_bytes());
66        for x in xs {
67            h.u64("x", *x as u64);
68        }
69        h.finalize()
70    }
71
72    struct ScaleStage {
73        factor: i64,
74        authorities: Vec<AuthorityHash>,
75    }
76    impl RuntimeStage<Vec<i64>, Vec<i64>> for ScaleStage {
77        fn stage_id(&self) -> &str {
78            "scale"
79        }
80        fn authority_hashes(&self) -> &[AuthorityHash] {
81            &self.authorities
82        }
83        fn execute(&self, input: Vec<i64>) -> Result<StageReceipt<Vec<i64>>, RuntimeError> {
84            let input_hash = hash_i64s("scale.in", &input);
85            let output: Vec<i64> = input
86                .iter()
87                .map(|x| x.saturating_mul(self.factor))
88                .collect();
89            let output_hash = hash_i64s("scale.out", &output);
90            Ok(StageReceipt {
91                stage_id: self.stage_id().to_string(),
92                input_hash,
93                output_hash,
94                authority_hashes: self.authorities.clone(),
95                output,
96            })
97        }
98    }
99
100    struct CountBeyondStage {
101        band: i64,
102        authorities: Vec<AuthorityHash>,
103    }
104    impl RuntimeStage<Vec<i64>, usize> for CountBeyondStage {
105        fn stage_id(&self) -> &str {
106            "count_beyond"
107        }
108        fn authority_hashes(&self) -> &[AuthorityHash] {
109            &self.authorities
110        }
111        fn execute(&self, input: Vec<i64>) -> Result<StageReceipt<usize>, RuntimeError> {
112            let input_hash = hash_i64s("count.in", &input);
113            let output = input
114                .iter()
115                .filter(|x| x.unsigned_abs() as i64 > self.band)
116                .count();
117            let mut h = CanonicalHasher::new();
118            h.u64("count", output as u64);
119            Ok(StageReceipt {
120                stage_id: self.stage_id().to_string(),
121                input_hash,
122                output_hash: h.finalize(),
123                authority_hashes: self.authorities.clone(),
124                output,
125            })
126        }
127    }
128
129    fn manifest() -> DensorManifest {
130        DensorManifest {
131            pipeline_id: "example_residual_pipeline".into(),
132            densors: vec![DensorEntry {
133                id: "residual_vec".into(),
134                kind: DensorKind::Residual,
135                evidence_hash: sha256(b"residual_vec"),
136            }],
137            authorities: vec![scale_authority(), envelope_authority()],
138        }
139    }
140
141    fn run_example() -> RuntimeReceiptV1 {
142        let m = manifest();
143        let mut rt = Runtime::start(&m).unwrap();
144        let scaled = rt
145            .run_stage(
146                &ScaleStage {
147                    factor: 2,
148                    authorities: vec![scale_authority()],
149                },
150                vec![1, -5, 2],
151            )
152            .unwrap();
153        let _count = rt
154            .run_stage(
155                &CountBeyondStage {
156                    band: 3,
157                    authorities: vec![envelope_authority()],
158                },
159                scaled,
160            )
161            .unwrap();
162        rt.seal()
163    }
164
165    #[test]
166    fn pipeline_runs_seals_and_is_deterministic() {
167        let a = run_example();
168        let b = run_example();
169        assert_eq!(
170            a.receipt_hash, b.receipt_hash,
171            "the sealed run receipt must be deterministic"
172        );
173        assert_eq!(a.receipt_hash.len(), 64);
174        assert_eq!(a.stages.len(), 2);
175        assert!(
176            a.verify(&manifest()),
177            "the receipt must self-verify against its manifest"
178        );
179    }
180
181    #[test]
182    fn tampering_a_stage_breaks_the_seal() {
183        let mut r = run_example();
184        // Forge a different output hash on the first stage → the seal must no longer verify.
185        r.stages[0].output_hash = sha256(b"forged");
186        assert!(
187            !r.verify(&manifest()),
188            "a tampered stage summary must fail verification"
189        );
190    }
191
192    #[test]
193    fn stage_without_authority_is_refused() {
194        // No claim without an authority anchor: a stage declaring no authorities is rejected by the gate.
195        let m = manifest();
196        let mut rt = Runtime::start(&m).unwrap();
197        let err = rt
198            .run_stage(
199                &ScaleStage {
200                    factor: 2,
201                    authorities: vec![],
202                },
203                vec![1, 2],
204            )
205            .unwrap_err();
206        assert!(matches!(err, RuntimeError::MissingAuthority { .. }));
207    }
208
209    #[test]
210    fn stage_citing_an_unpinned_authority_is_refused() {
211        // A stage may only cite authorities the manifest froze; an unknown authority is rejected.
212        let m = manifest();
213        let mut rt = Runtime::start(&m).unwrap();
214        let rogue = AuthorityHash::new("rogue_v1", sha256(b"not-in-manifest"));
215        let err = rt
216            .run_stage(
217                &ScaleStage {
218                    factor: 2,
219                    authorities: vec![rogue],
220                },
221                vec![1, 2],
222            )
223            .unwrap_err();
224        assert!(matches!(err, RuntimeError::AuthorityMismatch { .. }));
225    }
226
227    #[test]
228    fn empty_pipeline_id_manifest_is_invalid() {
229        let mut m = manifest();
230        m.pipeline_id = "  ".into();
231        assert!(matches!(
232            Runtime::start(&m),
233            Err(RuntimeError::ManifestInvalid(_))
234        ));
235    }
236
237    #[test]
238    fn runtime_index_summarises_the_run() {
239        let r = run_example();
240        let idx = RuntimeIndex::of(&r);
241        assert_eq!(idx.stage_ids, vec!["scale", "count_beyond"]);
242        assert_eq!(idx.authorities, vec!["envelope_v1", "scale_policy_v1"]); // sorted, de-duped
243        assert!(idx.summary_line().contains("example_residual_pipeline"));
244    }
245}