#![allow(missing_docs)]
use crate::quad::Quad;
use crate::ring::Ring;
pub fn awareness_fingerprint(som_quads: &[&Quad]) -> [u8; 32] {
assert_eq!(
som_quads.len(),
24,
"awareness fingerprint requires exactly 24 SOM Quads (6 units × 4 layers)"
);
let mut hasher = blake3::Hasher::new();
for quad in som_quads {
hasher.update(&quad.root);
hasher.update(&quad.pointer);
for (key, value) in &quad.tree {
hasher.update(key.as_bytes());
hasher.update(&(value.len() as u64).to_le_bytes());
hasher.update(value);
}
}
*hasher.finalize().as_bytes()
}
pub fn system_fingerprint(som_quads: &[&Quad], soma_quads: &[&Quad]) -> [u8; 32] {
assert_eq!(som_quads.len(), 24);
assert_eq!(soma_quads.len(), 6);
let mut hasher = blake3::Hasher::new();
for quad in som_quads {
hasher.update(&quad.root);
hasher.update(&quad.pointer);
for (key, value) in &quad.tree {
hasher.update(key.as_bytes());
hasher.update(&(value.len() as u64).to_le_bytes());
hasher.update(value);
}
}
for quad in soma_quads {
hasher.update(&quad.root);
hasher.update(&quad.pointer);
for (key, value) in &quad.tree {
hasher.update(key.as_bytes());
hasher.update(&(value.len() as u64).to_le_bytes());
hasher.update(value);
}
}
*hasher.finalize().as_bytes()
}
pub fn ring_awareness_fingerprint(ring: &Ring) -> [u8; 32] {
let quads = ring.all_som_quads();
awareness_fingerprint(&quads)
}
pub fn ring_system_fingerprint(ring: &Ring) -> [u8; 32] {
let som = ring.all_som_quads();
let soma = ring.all_soma_quads();
system_fingerprint(&som, &soma)
}
pub fn ring_fingerprint(system_fingerprint: &[u8; 32], boundary_key: &[u8; 32]) -> [u8; 32] {
let mut hasher = blake3::Hasher::new();
hasher.update(b"ring:");
hasher.update(system_fingerprint);
hasher.update(boundary_key);
*hasher.finalize().as_bytes()
}
pub fn position_hash(quad: &Quad) -> [u8; 32] {
quad.content_hash()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::quad::Tree;
use crate::ring::Ring;
use crate::types::{Layer, UnitId};
fn make_populated_ring() -> Ring {
let mut ring = Ring::new();
for &unit in &UnitId::ALL {
let us = ring.unit_mut(unit);
us.soma_quad = Quad::from_strings(
&format!("{unit}.soma.root"),
&format!("{unit}.soma.ptr"),
Tree::new(),
);
for &layer in &Layer::ALL {
let mut tree = Tree::new();
tree.insert(
format!("{unit}.{layer}.data"),
format!("value-{unit}-{layer}").into_bytes(),
);
*us.som_quad_mut(layer) = Quad::from_strings(
&format!("{unit}.{layer}.root"),
&format!("{unit}.{layer}.ptr"),
tree,
);
}
}
ring
}
#[test]
fn awareness_fingerprint_is_deterministic() {
let ring = make_populated_ring();
let fp1 = ring_awareness_fingerprint(&ring);
let fp2 = ring_awareness_fingerprint(&ring);
assert_eq!(fp1, fp2);
}
#[test]
fn system_fingerprint_is_deterministic() {
let ring = make_populated_ring();
let fp1 = ring_system_fingerprint(&ring);
let fp2 = ring_system_fingerprint(&ring);
assert_eq!(fp1, fp2);
}
#[test]
fn system_fingerprint_differs_from_awareness() {
let ring = make_populated_ring();
let a = ring_awareness_fingerprint(&ring);
let s = ring_system_fingerprint(&ring);
assert_ne!(a, s);
}
#[test]
fn criterion_2_single_position_change_changes_awareness_fingerprint() {
let ring = make_populated_ring();
let original_fp = ring_awareness_fingerprint(&ring);
for &unit in &UnitId::ALL {
for &layer in &Layer::ALL {
let mut modified_ring = ring.clone();
let quad = modified_ring.unit_mut(unit).som_quad_mut(layer);
quad.root[0] ^= 0xFF;
let new_fp = ring_awareness_fingerprint(&modified_ring);
assert_ne!(
original_fp, new_fp,
"Changing root at {unit}.{layer} must change the fingerprint"
);
let mut modified_ring = ring.clone();
let quad = modified_ring.unit_mut(unit).som_quad_mut(layer);
quad.pointer[0] ^= 0xFF;
let new_fp = ring_awareness_fingerprint(&modified_ring);
assert_ne!(
original_fp, new_fp,
"Changing pointer at {unit}.{layer} must change the fingerprint"
);
let mut modified_ring = ring.clone();
let quad = modified_ring.unit_mut(unit).som_quad_mut(layer);
quad.tree.insert("mutated".into(), vec![0xFF]);
let new_fp = ring_awareness_fingerprint(&modified_ring);
assert_ne!(
original_fp, new_fp,
"Changing tree at {unit}.{layer} must change the fingerprint"
);
}
}
}
#[test]
fn criterion_2_all_72_positions_produce_distinct_fingerprints() {
let ring = make_populated_ring();
let mut fingerprints = Vec::with_capacity(72);
for &unit in &UnitId::ALL {
for &layer in &Layer::ALL {
let mut mr = ring.clone();
mr.unit_mut(unit).som_quad_mut(layer).root[0] ^= 0xFF;
fingerprints.push(ring_awareness_fingerprint(&mr));
let mut mr = ring.clone();
mr.unit_mut(unit).som_quad_mut(layer).pointer[0] ^= 0xFF;
fingerprints.push(ring_awareness_fingerprint(&mr));
let mut mr = ring.clone();
mr.unit_mut(unit)
.som_quad_mut(layer)
.tree
.insert("_criterion2_probe".into(), vec![unit as u8, layer as u8]);
fingerprints.push(ring_awareness_fingerprint(&mr));
}
}
let count = fingerprints.len();
fingerprints.sort();
fingerprints.dedup();
assert_eq!(
fingerprints.len(),
count,
"All single-position mutations must produce distinct fingerprints"
);
}
#[test]
fn ring_fingerprint_deterministic() {
let sys = [0xAAu8; 32];
let bkey = [0xBBu8; 32];
let fp1 = ring_fingerprint(&sys, &bkey);
let fp2 = ring_fingerprint(&sys, &bkey);
assert_eq!(fp1, fp2);
}
#[test]
fn ring_fingerprint_differs_on_system() {
let bkey = [0xBBu8; 32];
let fp1 = ring_fingerprint(&[0x01u8; 32], &bkey);
let fp2 = ring_fingerprint(&[0x02u8; 32], &bkey);
assert_ne!(fp1, fp2);
}
#[test]
fn ring_fingerprint_differs_on_boundary_key() {
let sys = [0xAAu8; 32];
let fp1 = ring_fingerprint(&sys, &[0x01u8; 32]);
let fp2 = ring_fingerprint(&sys, &[0x02u8; 32]);
assert_ne!(fp1, fp2);
}
#[test]
fn ring_fingerprint_domain_separated() {
let sys = [0xAAu8; 32];
let bkey = [0xBBu8; 32];
let fp = ring_fingerprint(&sys, &bkey);
let mut raw_hasher = blake3::Hasher::new();
raw_hasher.update(&sys);
raw_hasher.update(&bkey);
let raw = *raw_hasher.finalize().as_bytes();
assert_ne!(fp, raw, "domain prefix must differentiate the hash");
}
}