qualia_core_db/governance/
webizen_validator.rs1use crate::NQuin;
12use std::collections::HashSet;
13
14const OP_MATCH_SUBJ: u8 = 0x01;
15const OP_MATCH_PRED: u8 = 0x02;
16const OP_MATCH_OBJ: u8 = 0x03;
17
18const OP_BIND_VAR: u8 = 0x10;
19const SLOT_SUBJ: u8 = 0x00;
20const SLOT_PRED: u8 = 0x01;
21const SLOT_OBJ: u8 = 0x02;
22
23const OP_HALT: u8 = 0xFF;
24
25#[derive(Debug, Default)]
27struct VmRegisters {
28 values: [u64; 16],
29 bound_flags: u16,
30}
31
32impl VmRegisters {
33 fn bind(&mut self, id: u8, value: u64) {
34 if (id as usize) < self.values.len() {
35 self.values[id as usize] = value;
36 self.bound_flags |= 1 << id;
37 }
38 }
39}
40
41pub fn execute_query_program(program: &[u8], dataset: &[NQuin]) -> Result<Vec<u64>, &'static str> {
48 let mut pc = 0;
49 let mut registers = VmRegisters::default();
50 let mut candidate_set: Vec<&NQuin> = dataset.iter().collect();
51
52 while pc < program.len() {
53 let opcode = program[pc];
54 pc += 1;
55
56 match opcode {
57 OP_MATCH_SUBJ => {
58 if pc + 8 > program.len() {
59 return Err("Unexpected EOF in OP_MATCH_SUBJ");
60 }
61 let target_hash = u64::from_le_bytes(program[pc..pc + 8].try_into().unwrap());
62 pc += 8;
63 candidate_set.retain(|quin| quin.subject == target_hash);
64 }
65 OP_MATCH_PRED => {
66 if pc + 8 > program.len() {
67 return Err("Unexpected EOF in OP_MATCH_PRED");
68 }
69 let target_hash = u64::from_le_bytes(program[pc..pc + 8].try_into().unwrap());
70 pc += 8;
71 candidate_set.retain(|quin| quin.predicate == target_hash);
72 }
73 OP_MATCH_OBJ => {
74 if pc + 8 > program.len() {
75 return Err("Unexpected EOF in OP_MATCH_OBJ");
76 }
77 let target_hash = u64::from_le_bytes(program[pc..pc + 8].try_into().unwrap());
78 pc += 8;
79 candidate_set.retain(|quin| quin.object == target_hash);
80 }
81 OP_BIND_VAR => {
82 if pc + 2 > program.len() {
83 return Err("Unexpected EOF in OP_BIND_VAR");
84 }
85 let slot = program[pc];
86 let reg_id = program[pc + 1];
87 pc += 2;
88 if let Some(q) = candidate_set.first() {
89 let value = match slot {
90 SLOT_SUBJ => q.subject,
91 SLOT_PRED => q.predicate,
92 SLOT_OBJ => q.object,
93 _ => return Err("Invalid slot identifier"),
94 };
95 registers.bind(reg_id, value);
96 }
97 }
98 OP_HALT => break,
99 _ => return Err("Unknown Opcode"),
100 }
101 }
102
103 let mut results = Vec::new();
104 for i in 0..16 {
105 if registers.bound_flags & (1 << i) != 0 {
106 results.push(registers.values[i]);
107 }
108 }
109 Ok(results)
110}
111
112pub type WebRuleId = u64;
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum WebRuleType {
120 Shape,
121 Confidence,
122 Domain,
123 Structural,
124}
125
126#[repr(C)]
128#[derive(Debug, Clone)]
129pub struct WebRule {
130 pub rule_id: WebRuleId,
131 pub rule_type: WebRuleType,
132 pub context_id: u64,
134 pub predicate_hash: u64,
136 pub min_confidence: f32,
137 pub required_domain: u64,
138}
139
140impl WebRule {
141 pub fn new(rule_id: WebRuleId, rule_type: WebRuleType) -> Self {
142 Self {
143 rule_id,
144 rule_type,
145 context_id: 0,
146 predicate_hash: 0,
147 min_confidence: 0.0,
148 required_domain: 0,
149 }
150 }
151
152 pub fn with_context(mut self, context_id: u64) -> Self {
153 self.context_id = context_id;
154 self
155 }
156
157 pub fn with_predicate(mut self, predicate_hash: u64) -> Self {
158 self.predicate_hash = predicate_hash;
159 self
160 }
161
162 pub fn with_confidence(mut self, min_confidence: f32) -> Self {
163 self.min_confidence = min_confidence;
164 self
165 }
166
167 pub fn with_domain(mut self, required_domain: u64) -> Self {
168 self.required_domain = required_domain;
169 self
170 }
171}
172
173#[derive(Debug, Clone, PartialEq, Eq)]
175pub enum WebVerdict {
176 Pass,
177 Fail(String),
178 PassWithWarning(String),
179}
180
181pub struct WebValidator {
185 rules: Vec<WebRule>,
186 active_rule_ids: HashSet<WebRuleId>,
187 confidence_threshold: f32,
188}
189
190impl WebValidator {
191 pub fn new() -> Self {
192 Self {
193 rules: Vec::new(),
194 active_rule_ids: HashSet::new(),
195 confidence_threshold: 0.5,
196 }
197 }
198
199 pub fn set_confidence_threshold(&mut self, threshold: f32) {
200 self.confidence_threshold = threshold;
201 }
202
203 pub fn add_rule(&mut self, rule: WebRule) {
204 self.active_rule_ids.insert(rule.rule_id);
205 self.rules.push(rule);
206 }
207
208 pub fn needs_validation(&self, quin: &NQuin) -> bool {
211 for rule in &self.rules {
212 if rule.context_id == 0 || rule.context_id == quin.context {
213 if rule.predicate_hash == 0 || rule.predicate_hash == quin.predicate {
214 return true;
215 }
216 }
217 }
218 false
219 }
220
221 pub fn validate(&self, quin: &NQuin) -> WebVerdict {
223 let confidence = (quin.metadata & 0xFFFFFFFF) as f32 / u32::MAX as f32;
224
225 if confidence < self.confidence_threshold {
226 return WebVerdict::Fail(format!(
227 "Confidence {} below threshold {}",
228 confidence, self.confidence_threshold
229 ));
230 }
231
232 for rule in &self.rules {
233 if rule.context_id != 0 && rule.context_id != quin.context {
234 continue;
235 }
236 if rule.predicate_hash != 0 && rule.predicate_hash != quin.predicate {
237 continue;
238 }
239
240 match rule.rule_type {
241 WebRuleType::Confidence => {
242 if confidence < rule.min_confidence {
243 return WebVerdict::Fail(format!(
244 "Confidence {} below rule threshold {}",
245 confidence, rule.min_confidence
246 ));
247 }
248 }
249 WebRuleType::Domain => {
250 if rule.required_domain != 0 && quin.subject != rule.required_domain {
251 return WebVerdict::Fail("Subject not in required domain".to_string());
252 }
253 }
254 WebRuleType::Shape | WebRuleType::Structural => {}
255 }
256 }
257
258 WebVerdict::Pass
259 }
260
261 pub fn validate_embedded_triple(
263 &self,
264 virtual_id: u64,
265 components: &[u64; 3],
266 context_id: u64,
267 ) -> WebVerdict {
268 const TAG_EMBEDDED: u64 = 0x1;
269
270 if virtual_id & TAG_EMBEDDED != TAG_EMBEDDED {
271 return WebVerdict::Fail("Not a Virtual ID".to_string());
272 }
273
274 for rule in &self.rules {
275 if rule.context_id == 0 || rule.context_id == context_id {
276 if let WebRuleType::Structural = rule.rule_type {
277 if components[0] == 0 || components[1] == 0 || components[2] == 0 {
278 return WebVerdict::Fail("Embedded triple has zero component".to_string());
279 }
280 }
281 }
282 }
283
284 WebVerdict::Pass
285 }
286}
287
288impl Default for WebValidator {
289 fn default() -> Self {
290 Self::new()
291 }
292}
293
294#[repr(C)]
298#[derive(Debug, Clone)]
299pub struct WebRuleStorage {
300 pub block_id: u64,
301 pub rule_count: u32,
302 pub reserved: u32,
303}
304
305impl WebRuleStorage {
306 const RULE_FLAG: u64 = 0x1 << 62;
307
308 pub fn rules_to_quins(rules: &[WebRule]) -> Vec<NQuin> {
309 rules
310 .iter()
311 .map(|rule| NQuin {
312 subject: rule.rule_id,
313 predicate: (rule.rule_type as u64) << 8 | (rule.context_id & 0xFF),
314 object: rule.predicate_hash,
315 context: rule.context_id,
316 metadata: Self::RULE_FLAG
317 | ((rule.min_confidence as u64) & 0xFFFFFFFF)
318 | (rule.required_domain << 32),
319 parity: 0,
320 })
321 .collect()
322 }
323
324 pub fn quins_to_rules(quins: &[NQuin]) -> Vec<WebRule> {
325 quins
326 .iter()
327 .filter(|q| q.metadata & Self::RULE_FLAG != 0)
328 .map(|q| {
329 let rule_type = match (q.predicate >> 8) % 4 {
330 0 => WebRuleType::Shape,
331 1 => WebRuleType::Confidence,
332 2 => WebRuleType::Domain,
333 _ => WebRuleType::Structural,
334 };
335 WebRule {
336 rule_id: q.subject,
337 rule_type,
338 context_id: q.context,
339 predicate_hash: q.object,
340 min_confidence: (q.metadata & 0xFFFFFFFF) as f32 / u32::MAX as f32,
341 required_domain: (q.metadata >> 32) & 0x0FFF_FFFF,
342 }
343 })
344 .collect()
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351
352 #[test]
353 fn rule_creation() {
354 let rule = WebRule::new(123, WebRuleType::Confidence).with_confidence(0.8);
355 assert_eq!(rule.rule_id, 123);
356 assert_eq!(rule.min_confidence, 0.8);
357 }
358
359 #[test]
360 fn validator_no_rules_needs_no_validation() {
361 let validator = WebValidator::new();
362 assert!(!validator.needs_validation(&NQuin {
363 subject: 1,
364 predicate: 2,
365 object: 3,
366 context: 0,
367 metadata: 0,
368 parity: 0
369 }));
370 }
371
372 #[test]
373 fn validator_add_rule_triggers_needs_validation() {
374 let mut validator = WebValidator::new();
375 let rule = WebRule::new(123, WebRuleType::Confidence)
376 .with_predicate(456)
377 .with_context(789);
378 validator.add_rule(rule);
379 let quin = NQuin {
380 subject: 1,
381 predicate: 456,
382 object: 3,
383 context: 789,
384 metadata: 0,
385 parity: 0,
386 };
387 assert!(validator.needs_validation(&quin));
388 }
389
390 #[test]
391 fn validate_pass_high_confidence() {
392 let mut validator = WebValidator::new();
393 validator.set_confidence_threshold(0.5);
394 let quin = NQuin {
395 subject: 1,
396 predicate: 2,
397 object: 3,
398 context: 0,
399 metadata: u32::MAX as u64,
400 parity: 0,
401 };
402 assert_eq!(validator.validate(&quin), WebVerdict::Pass);
403 }
404
405 #[test]
406 fn validate_fail_zero_confidence() {
407 let mut validator = WebValidator::new();
408 validator.set_confidence_threshold(0.5);
409 let quin = NQuin {
410 subject: 1,
411 predicate: 2,
412 object: 3,
413 context: 0,
414 metadata: 0,
415 parity: 0,
416 };
417 assert!(matches!(validator.validate(&quin), WebVerdict::Fail(_)));
418 }
419
420 #[test]
421 fn validate_embedded_triple_pass() {
422 let validator = WebValidator::new();
423 let virtual_id = 0x1 | 0x123456789ABCDEF0;
424 let components = [1u64, 2, 3];
425 assert_eq!(
426 validator.validate_embedded_triple(virtual_id, &components, 0),
427 WebVerdict::Pass
428 );
429 }
430
431 #[test]
432 fn storage_round_trip() {
433 let rules = vec![
434 WebRule::new(123, WebRuleType::Confidence).with_confidence(0.8),
435 WebRule::new(456, WebRuleType::Domain).with_domain(789),
436 ];
437 let quins = WebRuleStorage::rules_to_quins(&rules);
438 assert_eq!(quins.len(), 2);
439 let back = WebRuleStorage::quins_to_rules(&quins);
440 assert_eq!(back.len(), 2);
441 }
442
443 #[test]
444 fn execute_query_program_bind_var() {
445 let quin = NQuin {
446 subject: 0xAABB,
447 predicate: 0xCCDD,
448 object: 0xEEFF,
449 context: 0,
450 metadata: 0,
451 parity: 0,
452 };
453 let mut program = vec![OP_MATCH_SUBJ];
455 program.extend_from_slice(&0xAABBu64.to_le_bytes());
456 program.extend_from_slice(&[OP_BIND_VAR, SLOT_OBJ, 0, OP_HALT]);
457 let results = execute_query_program(&program, &[quin]).unwrap();
458 assert_eq!(results, vec![0xEEFF]);
459 }
460}