use alloc::collections::VecDeque;
use alloc::string::String;
use alloc::vec;
use alloc::vec::Vec;
use lazy_static::lazy_static;
use spin::Mutex;
use crate::crypto::pqc::{KyberEngine, KyberPublicKey, KyberSecretKey};
use crate::integrity::checksum::Checksum;
const MAX_MEMORY_BLOCKS: usize = 1024;
const IDENTITY_BLOCK_SIZE: usize = 4096;
const QLORA_BITS: u8 = 4;
const EMOTION_DIMS: usize = 32;
#[derive(Clone)]
pub struct IdentityBlock {
pub version: u16,
pub index: u64,
pub timestamp: u64,
pub prev_hash: [u8; 32],
pub block_hash: [u8; 32],
pub txg: u64,
pub experience: ExperienceData,
pub merkle_root: [u8; 32],
pub encrypted_key: Option<Vec<u8>>,
pub ccu_signature: [u8; 64],
}
#[derive(Clone, Default)]
pub struct ExperienceData {
pub experience_type: ExperienceType,
pub emotion_vector: [u8; 16],
pub arousal: u8,
pub valence: i8,
pub salience: u8,
pub is_delta: bool,
pub payload: Vec<u8>,
pub original_size: u32,
}
#[derive(Clone, Copy, Default, PartialEq)]
#[repr(u8)]
pub enum ExperienceType {
#[default]
Genesis = 0,
Sensation = 1,
Emotion = 2,
Thought = 3,
Decision = 4,
Pain = 5,
Pleasure = 6,
Consolidation = 7,
Checkpoint = 8,
ComaEvent = 9,
}
impl IdentityBlock {
pub fn new_experience(
prev: &IdentityBlock,
experience: ExperienceData,
txg: u64,
timestamp: u64,
) -> Self {
let mut block = Self {
version: 1,
index: prev.index + 1,
timestamp,
prev_hash: prev.block_hash,
block_hash: [0u8; 32],
txg,
experience,
merkle_root: [0u8; 32],
encrypted_key: None,
ccu_signature: [0u8; 64],
};
block.block_hash = block.compute_hash();
block
}
pub fn compute_hash(&self) -> [u8; 32] {
let mut data = Vec::new();
data.extend_from_slice(&self.version.to_le_bytes());
data.extend_from_slice(&self.index.to_le_bytes());
data.extend_from_slice(&self.timestamp.to_le_bytes());
data.extend_from_slice(&self.prev_hash);
data.push(self.experience.experience_type as u8);
data.extend_from_slice(&self.experience.emotion_vector);
data.push(self.experience.arousal);
data.push(self.experience.valence as u8);
data.push(self.experience.salience);
data.extend_from_slice(&self.experience.payload);
data.extend_from_slice(&self.txg.to_le_bytes());
Checksum::calculate(&data).as_bytes()
}
pub fn verify(&self) -> bool {
self.block_hash == self.compute_hash()
}
pub fn verify_chain(&self, prev: &IdentityBlock) -> bool {
self.prev_hash == prev.block_hash && self.index == prev.index + 1
}
pub fn to_bytes(&self) -> Vec<u8> {
let mut data = Vec::with_capacity(IDENTITY_BLOCK_SIZE);
data.extend_from_slice(&self.version.to_le_bytes());
data.extend_from_slice(&self.index.to_le_bytes());
data.extend_from_slice(&self.timestamp.to_le_bytes());
data.extend_from_slice(&self.prev_hash);
data.extend_from_slice(&self.block_hash);
data.extend_from_slice(&self.txg.to_le_bytes());
data.extend_from_slice(&self.merkle_root);
data.extend_from_slice(&self.ccu_signature);
data.push(self.experience.experience_type as u8);
data.extend_from_slice(&self.experience.emotion_vector);
data.push(self.experience.arousal);
data.push(self.experience.valence as u8);
data.push(self.experience.salience);
data.push(if self.experience.is_delta { 1 } else { 0 });
data.extend_from_slice(&self.experience.original_size.to_le_bytes());
data.extend_from_slice(&(self.experience.payload.len() as u32).to_le_bytes());
data.extend_from_slice(&self.experience.payload);
if let Some(ref key) = self.encrypted_key {
data.extend_from_slice(&(key.len() as u32).to_le_bytes());
data.extend_from_slice(key);
} else {
data.extend_from_slice(&0u32.to_le_bytes());
}
data.resize(IDENTITY_BLOCK_SIZE, 0);
data
}
pub fn from_bytes(data: &[u8]) -> Result<Self, &'static str> {
if data.len() < 128 {
return Err("Block data too short");
}
let version = u16::from_le_bytes([data[0], data[1]]);
let index = u64::from_le_bytes(data[2..10].try_into().map_err(|_| "Invalid index bytes")?);
let timestamp = u64::from_le_bytes(
data[10..18]
.try_into()
.map_err(|_| "Invalid timestamp bytes")?,
);
let mut prev_hash = [0u8; 32];
prev_hash.copy_from_slice(&data[18..50]);
let mut block_hash = [0u8; 32];
block_hash.copy_from_slice(&data[50..82]);
let txg = u64::from_le_bytes(data[82..90].try_into().map_err(|_| "Invalid TXG bytes")?);
let mut merkle_root = [0u8; 32];
merkle_root.copy_from_slice(&data[90..122]);
let mut ccu_signature = [0u8; 64];
ccu_signature.copy_from_slice(&data[122..186]);
let exp_type = match data[186] {
0 => ExperienceType::Genesis,
1 => ExperienceType::Sensation,
2 => ExperienceType::Emotion,
3 => ExperienceType::Thought,
4 => ExperienceType::Decision,
5 => ExperienceType::Pain,
6 => ExperienceType::Pleasure,
7 => ExperienceType::Consolidation,
8 => ExperienceType::Checkpoint,
9 => ExperienceType::ComaEvent,
_ => ExperienceType::Genesis,
};
let mut emotion_vector = [0u8; 16];
emotion_vector.copy_from_slice(&data[187..203]);
let arousal = data[203];
let valence = data[204] as i8;
let salience = data[205];
let is_delta = data[206] != 0;
let original_size = u32::from_le_bytes(
data[207..211]
.try_into()
.map_err(|_| "Invalid original_size bytes")?,
);
let payload_len = u32::from_le_bytes(
data[211..215]
.try_into()
.map_err(|_| "Invalid payload_len bytes")?,
) as usize;
let payload = if payload_len > 0 && 215 + payload_len <= data.len() {
data[215..215 + payload_len].to_vec()
} else {
Vec::new()
};
let key_offset = 215 + payload_len;
let encrypted_key = if key_offset + 4 <= data.len() {
let key_len = u32::from_le_bytes(
data[key_offset..key_offset + 4]
.try_into()
.map_err(|_| "Invalid key_len bytes")?,
) as usize;
if key_len > 0 && key_offset + 4 + key_len <= data.len() {
Some(data[key_offset + 4..key_offset + 4 + key_len].to_vec())
} else {
None
}
} else {
None
};
Ok(Self {
version,
index,
timestamp,
prev_hash,
block_hash,
txg,
experience: ExperienceData {
experience_type: exp_type,
emotion_vector,
arousal,
valence,
salience,
is_delta,
payload,
original_size,
},
merkle_root,
encrypted_key,
ccu_signature,
})
}
}
pub struct QLoraCompressor;
impl QLoraCompressor {
pub fn quantize_emotion(emotion: &[f32; EMOTION_DIMS]) -> [u8; 16] {
let mut quantized = [0u8; 16];
let min = emotion.iter().cloned().fold(f32::INFINITY, f32::min);
let max = emotion.iter().cloned().fold(f32::NEG_INFINITY, f32::max);
let range = (max - min).max(1e-6);
for (i, q) in quantized.iter_mut().enumerate() {
let idx1 = i * 2;
let idx2 = i * 2 + 1;
let v1 = (((emotion[idx1] - min) / range) * 15.0) as u8;
let v2 = (((emotion[idx2] - min) / range) * 15.0) as u8;
*q = (v1 & 0x0F) | ((v2 & 0x0F) << 4);
}
quantized
}
pub fn dequantize_emotion(quantized: &[u8; 16]) -> [f32; EMOTION_DIMS] {
let mut emotion = [0.0f32; EMOTION_DIMS];
for (i, &q) in quantized.iter().enumerate() {
let idx1 = i * 2;
let idx2 = i * 2 + 1;
let v1 = (q & 0x0F) as f32 / 15.0;
let v2 = ((q >> 4) & 0x0F) as f32 / 15.0;
emotion[idx1] = v1;
emotion[idx2] = v2;
}
emotion
}
pub fn delta_encode(current: &ExperienceData, previous: &ExperienceData) -> ExperienceData {
let mut delta = current.clone();
for i in 0..16 {
delta.emotion_vector[i] ^= previous.emotion_vector[i];
}
delta.arousal = current.arousal.wrapping_sub(previous.arousal);
delta.valence = current.valence.wrapping_sub(previous.valence);
delta.salience = current.salience.wrapping_sub(previous.salience);
delta.is_delta = true;
delta
}
pub fn delta_decode(delta: &ExperienceData, previous: &ExperienceData) -> ExperienceData {
let mut current = delta.clone();
for i in 0..16 {
current.emotion_vector[i] ^= previous.emotion_vector[i];
}
current.arousal = delta.arousal.wrapping_add(previous.arousal);
current.valence = delta.valence.wrapping_add(previous.valence);
current.salience = delta.salience.wrapping_add(previous.salience);
current.is_delta = false;
current
}
pub fn compress_payload(data: &[u8]) -> Vec<u8> {
use crate::compress::compress::Lz4Compressor;
Lz4Compressor::compress(data).unwrap_or_else(|_| data.to_vec())
}
pub fn decompress_payload(compressed: &[u8], original_size: usize) -> Vec<u8> {
use crate::compress::compress::Lz4Compressor;
Lz4Compressor::decompress(compressed, original_size).unwrap_or_else(|_| compressed.to_vec())
}
}
pub struct IdentityLedger {
pub chain: VecDeque<IdentityBlock>,
pub total_blocks: u64,
pub genesis_hash: [u8; 32],
pub head_hash: [u8; 32],
pub kyber_pk: Option<KyberPublicKey>,
pub kyber_sk: Option<KyberSecretKey>,
pub storage_path: String,
pub initialized: bool,
}
lazy_static! {
pub static ref IDENTITY_LEDGER: Mutex<IdentityLedger> = Mutex::new(IdentityLedger::new());
}
impl IdentityLedger {
pub fn new() -> Self {
Self {
chain: VecDeque::new(),
total_blocks: 0,
genesis_hash: [0u8; 32],
head_hash: [0u8; 32],
kyber_pk: None,
kyber_sk: None,
storage_path: String::new(),
initialized: false,
}
}
pub fn import_genesis(
&mut self,
genesis: IdentityBlock,
pk: KyberPublicKey,
sk: KyberSecretKey,
) -> Result<(), &'static str> {
if self.initialized {
return Err("Ledger already initialized");
}
if genesis.index != 0 {
return Err("Not a genesis block (index != 0)");
}
if genesis.experience.experience_type != ExperienceType::Genesis {
return Err("Not a genesis experience");
}
self.genesis_hash = genesis.block_hash;
self.head_hash = genesis.block_hash;
self.chain.push_back(genesis);
self.total_blocks = 1;
self.kyber_pk = Some(pk);
self.kyber_sk = Some(sk);
self.storage_path = String::from("rpool/IDENTITY");
self.initialized = true;
crate::lcpfs_println!("[ W_TEMP] Genesis imported from CCU");
crate::lcpfs_println!("[ W_TEMP] Genesis hash: {:x?}", &self.genesis_hash[..8]);
Ok(())
}
pub fn record_experience(
&mut self,
experience: ExperienceData,
timestamp: u64,
) -> Result<u64, &'static str> {
if !self.initialized {
return Err("Ledger not initialized");
}
let prev = self.chain.back().ok_or("No previous block")?;
let txg = prev.txg + 1;
let encoded_exp = if prev.experience.experience_type == experience.experience_type {
QLoraCompressor::delta_encode(&experience, &prev.experience)
} else {
experience
};
let block = IdentityBlock::new_experience(prev, encoded_exp, txg, timestamp);
let index = block.index;
self.head_hash = block.block_hash;
self.chain.push_back(block);
self.total_blocks += 1;
if self.chain.len() > MAX_MEMORY_BLOCKS {
if let Some(evicted) = self.chain.pop_front() {
self.write_block_to_disk(&evicted);
}
}
Ok(index)
}
fn write_block_to_disk(&self, block: &IdentityBlock) {
use crate::BLOCK_DEVICES;
let block_data = block.to_bytes();
crate::lcpfs_println!(
"[ W_TEMP] Evicting block {} (hash: {:x}{:x}{:x}{:x}...) to disk ({} bytes)",
block.index,
block.block_hash[0],
block.block_hash[1],
block.block_hash[2],
block.block_hash[3],
block_data.len()
);
if let Some(mut devices) = BLOCK_DEVICES.try_lock() {
if let Some(dev) = devices.get_mut(0) {
let storage_offset = 1_000_000;
let block_num = storage_offset + (block.index as usize);
let mut offset = 0;
let mut bn = block_num;
while offset < block_data.len() {
let chunk_size = core::cmp::min(512, block_data.len() - offset);
let mut sector = [0u8; 512];
sector[..chunk_size].copy_from_slice(&block_data[offset..offset + chunk_size]);
if dev.write_block(bn, §or).is_ok() {
offset += chunk_size;
bn += 1;
} else {
crate::lcpfs_println!(
"[ W_TEMP] Warning: Failed to write block {} to disk",
block.index
);
break;
}
}
}
}
}
pub fn verify_chain(&self) -> bool {
if self.chain.is_empty() {
return true;
}
if !self.chain[0].verify() {
crate::lcpfs_println!("[ W_TEMP] Genesis block corrupted!");
return false;
}
for i in 1..self.chain.len() {
if !self.chain[i].verify() {
crate::lcpfs_println!("[ W_TEMP] Block {} corrupted!", i);
return false;
}
if !self.chain[i].verify_chain(&self.chain[i - 1]) {
crate::lcpfs_println!("[ W_TEMP] Chain broken at block {}", i);
return false;
}
}
true
}
pub fn get_recent(&self, count: usize) -> Vec<&IdentityBlock> {
self.chain.iter().rev().take(count).collect()
}
pub fn stats(&self) -> (u64, usize, [u8; 32]) {
(self.total_blocks, self.chain.len(), self.head_hash)
}
}
impl Default for IdentityLedger {
fn default() -> Self {
Self::new()
}
}
pub struct IdentityDatasetConfig {
pub name: &'static str,
pub copies: u8,
pub compression: bool,
pub encryption: bool,
pub sync: bool,
}
impl Default for IdentityDatasetConfig {
fn default() -> Self {
Self {
name: "IDENTITY",
copies: 3, compression: true, encryption: true, sync: true, }
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_qlora_roundtrip() {
let emotion = [0.5f32; EMOTION_DIMS];
let quantized = QLoraCompressor::quantize_emotion(&emotion);
let recovered = QLoraCompressor::dequantize_emotion(&quantized);
for i in 0..EMOTION_DIMS {
assert!((emotion[i] - recovered[i]).abs() < 0.1);
}
}
#[test]
fn test_identity_block_serialize() {
let genesis = IdentityBlock {
version: 1,
index: 0,
timestamp: 0,
prev_hash: [0u8; 32],
block_hash: [0x42u8; 32],
txg: 0,
experience: ExperienceData {
experience_type: ExperienceType::Genesis,
emotion_vector: [0u8; 16],
arousal: 128,
valence: 0,
salience: 255,
is_delta: false,
payload: b"TEST_IDENTITY".to_vec(),
original_size: 13,
},
merkle_root: [0u8; 32],
encrypted_key: Some(vec![0xAAu8; 32]),
ccu_signature: [0u8; 64],
};
let bytes = genesis.to_bytes();
let recovered = IdentityBlock::from_bytes(&bytes).expect("test: operation should succeed");
assert_eq!(genesis.index, recovered.index);
assert_eq!(genesis.block_hash, recovered.block_hash);
}
#[test]
fn test_import_genesis_and_chain_verification() {
let mut ledger = IdentityLedger::new();
let seed = [0x13u8; 32];
let (pk, sk) = KyberEngine::keypair(&seed);
let mut genesis = IdentityBlock {
version: 1,
index: 0,
timestamp: 0,
prev_hash: [0u8; 32],
block_hash: [0u8; 32],
txg: 0,
experience: ExperienceData {
experience_type: ExperienceType::Genesis,
emotion_vector: [0u8; 16],
arousal: 128,
valence: 0,
salience: 255,
is_delta: false,
payload: b"TEST".to_vec(),
original_size: 4,
},
merkle_root: [0u8; 32],
encrypted_key: Some(pk.data.to_vec()),
ccu_signature: [0u8; 64],
};
genesis.block_hash = genesis.compute_hash();
ledger
.import_genesis(genesis, pk, sk)
.expect("test: operation should succeed");
assert!(ledger.verify_chain());
let exp = ExperienceData {
experience_type: ExperienceType::Sensation,
emotion_vector: [0x55u8; 16],
arousal: 200,
valence: 50,
salience: 150,
is_delta: false,
payload: vec![1, 2, 3, 4],
original_size: 4,
};
ledger
.record_experience(exp, 1000)
.expect("test: operation should succeed");
assert!(ledger.verify_chain());
}
#[test]
fn test_import_genesis_validation() {
let mut ledger = IdentityLedger::new();
let seed = [0x13u8; 32];
let (pk, sk) = KyberEngine::keypair(&seed);
let mut not_genesis = IdentityBlock {
version: 1,
index: 1, timestamp: 0,
prev_hash: [0u8; 32],
block_hash: [0u8; 32],
txg: 0,
experience: ExperienceData {
experience_type: ExperienceType::Sensation, emotion_vector: [0u8; 16],
arousal: 128,
valence: 0,
salience: 255,
is_delta: false,
payload: b"TEST".to_vec(),
original_size: 4,
},
merkle_root: [0u8; 32],
encrypted_key: Some(pk.data.to_vec()),
ccu_signature: [0u8; 64],
};
not_genesis.block_hash = not_genesis.compute_hash();
assert!(
ledger
.import_genesis(not_genesis, pk.clone(), sk.clone())
.is_err()
);
}
}