dsfb_densor_runtime/
lib.rs1#![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 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 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 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 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"]); assert!(idx.summary_line().contains("example_residual_pipeline"));
244 }
245}