1use crate::coherence::{CoherenceConfig, CoherenceTracker};
21use crate::hnsw::{HnswConfig, HnswRegion};
22use crate::proof_policy::{ProofPolicy, ProofVerifier};
23use crate::witness::WitnessLog;
24use crate::Result;
25
26use ruvix_region::backing::MemoryBacking;
27use ruvix_region::slab::{SlabAllocator, SlotHandle};
28use ruvix_types::{
29 CapRights, Capability, CoherenceMeta, KernelError, ProofAttestation, ProofToken, RegionHandle,
30 VectorKey, VectorStoreHandle,
31};
32
33const VECTOR_ENTRY_HEADER_SIZE: usize = 8 + core::mem::size_of::<CoherenceMeta>();
35
36#[derive(Debug, Clone)]
38pub struct VectorEntry {
39 pub key: VectorKey,
41
42 pub coherence: CoherenceMeta,
44
45 pub data: Vec<f32>,
47}
48
49impl VectorEntry {
50 #[inline]
52 #[must_use]
53 pub fn new(key: VectorKey, data: Vec<f32>, coherence: CoherenceMeta) -> Self {
54 Self {
55 key,
56 coherence,
57 data,
58 }
59 }
60}
61
62pub struct VectorStoreBuilder {
64 dimensions: u32,
65 capacity: u32,
66 hnsw_config: HnswConfig,
67 coherence_config: CoherenceConfig,
68 proof_policy: ProofPolicy,
69}
70
71impl VectorStoreBuilder {
72 #[inline]
74 #[must_use]
75 pub fn new(dimensions: u32, capacity: u32) -> Self {
76 Self {
77 dimensions,
78 capacity,
79 hnsw_config: HnswConfig::default(),
80 coherence_config: CoherenceConfig::default(),
81 proof_policy: ProofPolicy::standard(),
82 }
83 }
84
85 #[inline]
87 #[must_use]
88 pub fn with_hnsw_config(mut self, config: HnswConfig) -> Self {
89 self.hnsw_config = config;
90 self
91 }
92
93 #[inline]
95 #[must_use]
96 pub fn with_coherence_config(mut self, config: CoherenceConfig) -> Self {
97 self.coherence_config = config;
98 self
99 }
100
101 #[inline]
103 #[must_use]
104 pub fn with_proof_policy(mut self, policy: ProofPolicy) -> Self {
105 self.proof_policy = policy;
106 self
107 }
108
109 pub fn build<B: MemoryBacking>(
125 self,
126 data_backing: B,
127 hnsw_backing: B,
128 witness_backing: B,
129 data_handle: RegionHandle,
130 hnsw_handle: RegionHandle,
131 witness_handle: RegionHandle,
132 store_id: u32,
133 ) -> Result<KernelVectorStore<B>> {
134 KernelVectorStore::new(
135 data_backing,
136 hnsw_backing,
137 witness_backing,
138 data_handle,
139 hnsw_handle,
140 witness_handle,
141 self.dimensions,
142 self.capacity,
143 self.hnsw_config,
144 self.coherence_config,
145 self.proof_policy,
146 store_id,
147 )
148 }
149}
150
151struct KeyMap {
153 entries: [(VectorKey, SlotHandle); 256],
156 count: usize,
157}
158
159impl KeyMap {
160 fn new() -> Self {
161 Self {
162 entries: [(VectorKey::new(0), SlotHandle::invalid()); 256],
163 count: 0,
164 }
165 }
166
167 fn get(&self, key: VectorKey) -> Option<SlotHandle> {
168 for i in 0..self.count {
169 if self.entries[i].0 == key {
170 return Some(self.entries[i].1);
171 }
172 }
173 None
174 }
175
176 fn insert(&mut self, key: VectorKey, handle: SlotHandle) -> Result<()> {
177 for i in 0..self.count {
179 if self.entries[i].0 == key {
180 self.entries[i].1 = handle;
181 return Ok(());
182 }
183 }
184
185 if self.count >= 256 {
187 return Err(KernelError::LimitExceeded);
188 }
189
190 self.entries[self.count] = (key, handle);
191 self.count += 1;
192 Ok(())
193 }
194
195 fn remove(&mut self, key: VectorKey) -> Option<SlotHandle> {
196 for i in 0..self.count {
197 if self.entries[i].0 == key {
198 let handle = self.entries[i].1;
199 self.entries[i] = self.entries[self.count - 1];
201 self.entries[self.count - 1] = (VectorKey::new(0), SlotHandle::invalid());
202 self.count -= 1;
203 return Some(handle);
204 }
205 }
206 None
207 }
208
209 fn len(&self) -> usize {
210 self.count
211 }
212}
213
214pub struct KernelVectorStore<B: MemoryBacking> {
218 data_slab: SlabAllocator<B>,
220
221 hnsw_region: HnswRegion<B>,
223
224 witness_log: WitnessLog<B>,
226
227 data_handle: RegionHandle,
229 hnsw_handle: RegionHandle,
230
231 key_map: KeyMap,
233
234 coherence_tracker: CoherenceTracker,
236
237 proof_verifier: ProofVerifier,
239
240 dimensions: u32,
242
243 capacity: u32,
245
246 store_id: u32,
248
249 handle: VectorStoreHandle,
251}
252
253impl<B: MemoryBacking> KernelVectorStore<B> {
254 #[allow(clippy::too_many_arguments)]
256 pub fn new(
257 data_backing: B,
258 hnsw_backing: B,
259 witness_backing: B,
260 data_handle: RegionHandle,
261 hnsw_handle: RegionHandle,
262 witness_handle: RegionHandle,
263 dimensions: u32,
264 capacity: u32,
265 hnsw_config: HnswConfig,
266 coherence_config: CoherenceConfig,
267 proof_policy: ProofPolicy,
268 store_id: u32,
269 ) -> Result<Self> {
270 let slot_size = VECTOR_ENTRY_HEADER_SIZE + (dimensions as usize) * 4;
273
274 let data_slab = SlabAllocator::new(data_backing, slot_size, capacity as usize)?;
275 let hnsw_region = HnswRegion::new(hnsw_backing, hnsw_config, capacity as usize)?;
276 let witness_log = WitnessLog::new(
277 witness_backing,
278 capacity as usize * 2, witness_handle,
280 store_id,
281 )?;
282
283 Ok(Self {
284 data_slab,
285 hnsw_region,
286 witness_log,
287 data_handle,
288 hnsw_handle,
289 key_map: KeyMap::new(),
290 coherence_tracker: CoherenceTracker::new(coherence_config),
291 proof_verifier: ProofVerifier::new(proof_policy),
292 dimensions,
293 capacity,
294 store_id,
295 handle: VectorStoreHandle::new(store_id, 0),
296 })
297 }
298
299 #[inline]
301 #[must_use]
302 pub const fn handle(&self) -> VectorStoreHandle {
303 self.handle
304 }
305
306 #[inline]
308 #[must_use]
309 pub fn len(&self) -> usize {
310 self.key_map.len()
311 }
312
313 #[inline]
315 #[must_use]
316 pub fn is_empty(&self) -> bool {
317 self.key_map.len() == 0
318 }
319
320 #[inline]
322 #[must_use]
323 pub const fn capacity(&self) -> u32 {
324 self.capacity
325 }
326
327 #[inline]
329 #[must_use]
330 pub const fn dimensions(&self) -> u32 {
331 self.dimensions
332 }
333
334 pub fn vector_get(
348 &self,
349 key: VectorKey,
350 capability: &Capability,
351 ) -> Result<(Vec<f32>, CoherenceMeta)> {
352 if !capability.has_rights(CapRights::READ) {
354 return Err(KernelError::InsufficientRights);
355 }
356
357 let slot_handle = self.key_map.get(key).ok_or(KernelError::NotFound)?;
359
360 let slot_size = VECTOR_ENTRY_HEADER_SIZE + (self.dimensions as usize) * 4;
362 let mut buf = vec![0u8; slot_size];
363 self.data_slab.read(slot_handle, &mut buf)?;
364
365 let key_bytes: [u8; 8] = buf[0..8].try_into().map_err(|_| KernelError::InternalError)?;
367 let stored_key = VectorKey::new(u64::from_le_bytes(key_bytes));
368
369 if stored_key != key {
370 return Err(KernelError::InternalError);
371 }
372
373 let coherence_bytes = &buf[8..8 + core::mem::size_of::<CoherenceMeta>()];
375 let coherence = parse_coherence_meta(coherence_bytes)?;
376
377 let data_start = VECTOR_ENTRY_HEADER_SIZE;
379 let mut data = Vec::with_capacity(self.dimensions as usize);
380 for i in 0..self.dimensions as usize {
381 let offset = data_start + i * 4;
382 let bytes: [u8; 4] = buf[offset..offset + 4]
383 .try_into()
384 .map_err(|_| KernelError::InternalError)?;
385 data.push(f32::from_le_bytes(bytes));
386 }
387
388 Ok((data, coherence))
389 }
390
391 pub fn vector_put_proved(
408 &mut self,
409 key: VectorKey,
410 data: &[f32],
411 proof: &ProofToken,
412 capability: &Capability,
413 current_time_ns: u64,
414 ) -> Result<ProofAttestation> {
415 if data.len() != self.dimensions as usize {
417 return Err(KernelError::InvalidArgument);
418 }
419
420 let mutation_hash = compute_vector_mutation_hash(key, data);
422
423 let attestation =
425 self.proof_verifier
426 .verify(proof, &mutation_hash, current_time_ns, capability)?;
427
428 let is_update = self.key_map.get(key).is_some();
430
431 if is_update {
432 let slot_handle = self.key_map.get(key).unwrap();
434
435 let slot_size = VECTOR_ENTRY_HEADER_SIZE + (self.dimensions as usize) * 4;
437 let mut buf = vec![0u8; slot_size];
438 self.data_slab.read(slot_handle, &mut buf)?;
439
440 let old_coherence_bytes = &buf[8..8 + core::mem::size_of::<CoherenceMeta>()];
441 let old_coherence = parse_coherence_meta(old_coherence_bytes)?;
442
443 let new_coherence = self.coherence_tracker.on_entry_mutated(
445 &old_coherence,
446 self.coherence_tracker.config().initial_coherence,
447 attestation.proof_term_hash,
448 );
449
450 self.write_vector_slot(slot_handle, key, data, &new_coherence)?;
452 } else {
453 let slot_handle = self.data_slab.alloc()?;
455
456 let coherence = self
458 .coherence_tracker
459 .create_initial_meta(attestation.proof_term_hash);
460
461 self.write_vector_slot(slot_handle, key, data, &coherence)?;
463
464 self.key_map.insert(key, slot_handle)?;
466
467 let _hnsw_handle = self.hnsw_region.alloc_node(0, slot_handle)?;
469
470 self.coherence_tracker
472 .on_entry_added(coherence.coherence_score);
473 }
474
475 self.witness_log
477 .record_vector_mutation(key, attestation, current_time_ns)?;
478
479 Ok(attestation)
480 }
481
482 fn write_vector_slot(
484 &mut self,
485 slot_handle: SlotHandle,
486 key: VectorKey,
487 data: &[f32],
488 coherence: &CoherenceMeta,
489 ) -> Result<()> {
490 let slot_size = VECTOR_ENTRY_HEADER_SIZE + (self.dimensions as usize) * 4;
491 let mut buf = vec![0u8; slot_size];
492
493 buf[0..8].copy_from_slice(&key.raw().to_le_bytes());
495
496 serialize_coherence_meta(coherence, &mut buf[8..8 + core::mem::size_of::<CoherenceMeta>()]);
498
499 let data_start = VECTOR_ENTRY_HEADER_SIZE;
501 for (i, &value) in data.iter().enumerate() {
502 let offset = data_start + i * 4;
503 buf[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
504 }
505
506 self.data_slab.write(slot_handle, &buf)?;
507 Ok(())
508 }
509
510 #[inline]
512 #[must_use]
513 pub const fn proof_policy(&self) -> &ProofPolicy {
514 self.proof_verifier.policy()
515 }
516
517 #[inline]
519 #[must_use]
520 pub const fn coherence_tracker(&self) -> &CoherenceTracker {
521 &self.coherence_tracker
522 }
523
524 #[inline]
526 #[must_use]
527 pub fn witness_entry_count(&self) -> usize {
528 self.witness_log.entry_count()
529 }
530
531 #[inline]
533 #[must_use]
534 pub fn witness_fill_ratio(&self) -> f32 {
535 self.witness_log.fill_ratio()
536 }
537}
538
539fn compute_vector_mutation_hash(key: VectorKey, data: &[f32]) -> [u8; 32] {
541 let mut hash = [0u8; 32];
544
545 let key_bytes = key.raw().to_le_bytes();
547 hash[0..8].copy_from_slice(&key_bytes);
548
549 for (i, &value) in data.iter().enumerate() {
551 let bytes = value.to_le_bytes();
552 let offset = (8 + (i * 4)) % 24; for j in 0..4 {
554 hash[offset + j] ^= bytes[j];
555 }
556 }
557
558 hash
559}
560
561fn parse_coherence_meta(bytes: &[u8]) -> Result<CoherenceMeta> {
563 if bytes.len() < core::mem::size_of::<CoherenceMeta>() {
564 return Err(KernelError::BufferTooSmall);
565 }
566
567 let coherence_score = u16::from_le_bytes([bytes[0], bytes[1]]);
568 let mutation_epoch = u64::from_le_bytes(bytes[2..10].try_into().unwrap());
569
570 let mut proof_attestation_hash = [0u8; 32];
571 proof_attestation_hash.copy_from_slice(&bytes[10..42]);
572
573 let last_access_ns = u64::from_le_bytes(bytes[42..50].try_into().unwrap());
574 let access_count = u32::from_le_bytes(bytes[50..54].try_into().unwrap());
575
576 Ok(CoherenceMeta {
577 coherence_score,
578 mutation_epoch,
579 proof_attestation_hash,
580 last_access_ns,
581 access_count,
582 })
583}
584
585fn serialize_coherence_meta(meta: &CoherenceMeta, buf: &mut [u8]) {
587 buf[0..2].copy_from_slice(&meta.coherence_score.to_le_bytes());
588 buf[2..10].copy_from_slice(&meta.mutation_epoch.to_le_bytes());
589 buf[10..42].copy_from_slice(&meta.proof_attestation_hash);
590 buf[42..50].copy_from_slice(&meta.last_access_ns.to_le_bytes());
591 buf[50..54].copy_from_slice(&meta.access_count.to_le_bytes());
592}
593
594#[cfg(test)]
595mod tests {
596 use super::*;
597 use ruvix_region::backing::StaticBacking;
598 use ruvix_types::{ObjectType, ProofPayload, ProofTier};
599
600 fn create_test_capability() -> Capability {
601 Capability::new(
602 1,
603 ObjectType::VectorStore,
604 CapRights::READ | CapRights::WRITE | CapRights::PROVE,
605 0,
606 1,
607 )
608 }
609
610 fn create_test_proof(data: &[f32], key: VectorKey, valid_until_ns: u64, nonce: u64) -> ProofToken {
611 let mutation_hash = compute_vector_mutation_hash(key, data);
612 ProofToken::new(
613 mutation_hash,
614 ProofTier::Standard,
615 ProofPayload::Hash { hash: mutation_hash },
616 valid_until_ns,
617 nonce,
618 )
619 }
620
621 #[test]
622 fn test_vector_store_creation() {
623 let data_backing = StaticBacking::<16384>::new();
624 let hnsw_backing = StaticBacking::<16384>::new();
625 let witness_backing = StaticBacking::<16384>::new();
626
627 let store = VectorStoreBuilder::new(4, 10) .build(
629 data_backing,
630 hnsw_backing,
631 witness_backing,
632 RegionHandle::new(1, 0),
633 RegionHandle::new(2, 0),
634 RegionHandle::new(3, 0),
635 1,
636 )
637 .unwrap();
638
639 assert_eq!(store.dimensions(), 4);
640 assert_eq!(store.capacity(), 10);
641 assert!(store.is_empty());
642 }
643
644 #[test]
645 fn test_vector_put_and_get() {
646 let data_backing = StaticBacking::<16384>::new();
647 let hnsw_backing = StaticBacking::<16384>::new();
648 let witness_backing = StaticBacking::<16384>::new();
649
650 let mut store = VectorStoreBuilder::new(4, 10)
651 .with_proof_policy(ProofPolicy::reflex())
652 .build(
653 data_backing,
654 hnsw_backing,
655 witness_backing,
656 RegionHandle::new(1, 0),
657 RegionHandle::new(2, 0),
658 RegionHandle::new(3, 0),
659 1,
660 )
661 .unwrap();
662
663 let cap = create_test_capability();
664 let key = VectorKey::new(42);
665 let data = vec![1.0, 2.0, 3.0, 4.0];
666 let proof = create_test_proof(&data, key, 1_000_000_000, 1);
667
668 let attestation = store
670 .vector_put_proved(key, &data, &proof, &cap, 500_000_000)
671 .unwrap();
672
673 assert_eq!(store.len(), 1);
674 assert!(attestation.verification_timestamp_ns > 0);
675
676 let (retrieved_data, coherence) = store.vector_get(key, &cap).unwrap();
678
679 assert_eq!(retrieved_data, data);
680 assert_eq!(coherence.coherence_score, 10000); }
682
683 #[test]
684 fn test_vector_put_proof_rejected() {
685 let data_backing = StaticBacking::<16384>::new();
686 let hnsw_backing = StaticBacking::<16384>::new();
687 let witness_backing = StaticBacking::<16384>::new();
688
689 let mut store = VectorStoreBuilder::new(4, 10)
690 .build(
691 data_backing,
692 hnsw_backing,
693 witness_backing,
694 RegionHandle::new(1, 0),
695 RegionHandle::new(2, 0),
696 RegionHandle::new(3, 0),
697 1,
698 )
699 .unwrap();
700
701 let cap = create_test_capability();
702 let key = VectorKey::new(42);
703 let data = vec![1.0, 2.0, 3.0, 4.0];
704
705 let wrong_proof = ProofToken::new(
707 [0u8; 32], ProofTier::Standard,
709 ProofPayload::Hash { hash: [0u8; 32] },
710 1_000_000_000,
711 1,
712 );
713
714 let result = store.vector_put_proved(key, &data, &wrong_proof, &cap, 500_000_000);
715
716 assert_eq!(result, Err(KernelError::ProofRejected));
717 assert!(store.is_empty()); }
719
720 #[test]
721 fn test_vector_put_expired_proof() {
722 let data_backing = StaticBacking::<16384>::new();
723 let hnsw_backing = StaticBacking::<16384>::new();
724 let witness_backing = StaticBacking::<16384>::new();
725
726 let mut store = VectorStoreBuilder::new(4, 10)
727 .build(
728 data_backing,
729 hnsw_backing,
730 witness_backing,
731 RegionHandle::new(1, 0),
732 RegionHandle::new(2, 0),
733 RegionHandle::new(3, 0),
734 1,
735 )
736 .unwrap();
737
738 let cap = create_test_capability();
739 let key = VectorKey::new(42);
740 let data = vec![1.0, 2.0, 3.0, 4.0];
741
742 let expired_proof = create_test_proof(&data, key, 100_000_000, 1);
744
745 let result = store.vector_put_proved(key, &data, &expired_proof, &cap, 500_000_000);
747
748 assert_eq!(result, Err(KernelError::ProofRejected));
749 }
750
751 #[test]
752 fn test_vector_put_nonce_reuse() {
753 let data_backing = StaticBacking::<16384>::new();
754 let hnsw_backing = StaticBacking::<16384>::new();
755 let witness_backing = StaticBacking::<16384>::new();
756
757 let mut store = VectorStoreBuilder::new(4, 10)
758 .with_proof_policy(ProofPolicy::reflex())
759 .build(
760 data_backing,
761 hnsw_backing,
762 witness_backing,
763 RegionHandle::new(1, 0),
764 RegionHandle::new(2, 0),
765 RegionHandle::new(3, 0),
766 1,
767 )
768 .unwrap();
769
770 let cap = create_test_capability();
771 let key1 = VectorKey::new(1);
772 let key2 = VectorKey::new(2);
773 let data1 = vec![1.0, 2.0, 3.0, 4.0];
774 let data2 = vec![5.0, 6.0, 7.0, 8.0];
775
776 let proof1 = create_test_proof(&data1, key1, 1_000_000_000, 42);
778 store
779 .vector_put_proved(key1, &data1, &proof1, &cap, 500_000_000)
780 .unwrap();
781
782 let proof2 = create_test_proof(&data2, key2, 1_000_000_000, 42);
784 let result = store.vector_put_proved(key2, &data2, &proof2, &cap, 500_000_001);
785
786 assert_eq!(result, Err(KernelError::ProofRejected));
787 }
788
789 #[test]
790 fn test_vector_get_not_found() {
791 let data_backing = StaticBacking::<16384>::new();
792 let hnsw_backing = StaticBacking::<16384>::new();
793 let witness_backing = StaticBacking::<16384>::new();
794
795 let store = VectorStoreBuilder::new(4, 10)
796 .build(
797 data_backing,
798 hnsw_backing,
799 witness_backing,
800 RegionHandle::new(1, 0),
801 RegionHandle::new(2, 0),
802 RegionHandle::new(3, 0),
803 1,
804 )
805 .unwrap();
806
807 let cap = create_test_capability();
808 let result = store.vector_get(VectorKey::new(999), &cap);
809
810 assert_eq!(result, Err(KernelError::NotFound));
811 }
812
813 #[test]
814 fn test_vector_get_insufficient_rights() {
815 let data_backing = StaticBacking::<16384>::new();
816 let hnsw_backing = StaticBacking::<16384>::new();
817 let witness_backing = StaticBacking::<16384>::new();
818
819 let store = VectorStoreBuilder::new(4, 10)
820 .build(
821 data_backing,
822 hnsw_backing,
823 witness_backing,
824 RegionHandle::new(1, 0),
825 RegionHandle::new(2, 0),
826 RegionHandle::new(3, 0),
827 1,
828 )
829 .unwrap();
830
831 let cap = Capability::new(1, ObjectType::VectorStore, CapRights::WRITE, 0, 1);
833
834 let result = store.vector_get(VectorKey::new(1), &cap);
835
836 assert_eq!(result, Err(KernelError::InsufficientRights));
837 }
838
839 #[test]
840 fn test_witness_log_recording() {
841 let data_backing = StaticBacking::<16384>::new();
842 let hnsw_backing = StaticBacking::<16384>::new();
843 let witness_backing = StaticBacking::<16384>::new();
844
845 let mut store = VectorStoreBuilder::new(4, 10)
846 .with_proof_policy(ProofPolicy::reflex())
847 .build(
848 data_backing,
849 hnsw_backing,
850 witness_backing,
851 RegionHandle::new(1, 0),
852 RegionHandle::new(2, 0),
853 RegionHandle::new(3, 0),
854 1,
855 )
856 .unwrap();
857
858 let cap = create_test_capability();
859
860 for i in 0..5 {
862 let key = VectorKey::new(i);
863 let data = vec![i as f32; 4];
864 let proof = create_test_proof(&data, key, 1_000_000_000, i);
865 store
866 .vector_put_proved(key, &data, &proof, &cap, 500_000_000)
867 .unwrap();
868 }
869
870 assert_eq!(store.witness_entry_count(), 5);
871 }
872}