use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::HashMap;
use crate::types::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct MerkleNode {
pub hash: ContentHash,
pub data: Vec<u8>,
pub children: Vec<ContentHash>,
pub timestamp: u64,
}
#[derive(Debug)]
pub struct MerkleDAG {
nodes: HashMap<ContentHash, MerkleNode>,
}
#[derive(Debug, Clone)]
pub struct TreeComparison {
pub identical: bool,
pub differences: Vec<ContentHash>,
pub self_only: Vec<ContentHash>,
pub other_only: Vec<ContentHash>,
}
impl MerkleDAG {
pub fn new() -> Self {
Self {
nodes: HashMap::new(),
}
}
pub fn store(&mut self, data: &[u8], children: Vec<ContentHash>) -> ContentHash {
let mut hasher = Sha256::new();
hasher.update(data);
for child in &children {
hasher.update(child.0.as_bytes());
}
let hash = ContentHash(format!("{:x}", hasher.finalize()));
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
let node = MerkleNode {
hash: hash.clone(),
data: data.to_vec(),
children,
timestamp,
};
self.nodes.insert(hash.clone(), node);
hash
}
pub fn get(&self, hash: &ContentHash) -> Option<&MerkleNode> {
self.nodes.get(hash)
}
pub fn contains(&self, hash: &ContentHash) -> bool {
self.nodes.contains_key(hash)
}
pub fn get_children(&self, hash: &ContentHash) -> Option<&[ContentHash]> {
self.nodes.get(hash).map(|node| node.children.as_slice())
}
pub fn len(&self) -> usize {
self.nodes.len()
}
pub fn is_empty(&self) -> bool {
self.nodes.is_empty()
}
pub fn verify_integrity(&self) -> Result<bool> {
for (hash, node) in &self.nodes {
let mut hasher = sha2::Sha256::new();
hasher.update(&node.data);
for child in &node.children {
hasher.update(child.0.as_bytes());
}
let computed_hash = format!("{:x}", hasher.finalize());
if computed_hash != hash.0 {
return Ok(false);
}
for child_hash in &node.children {
if !self.nodes.contains_key(child_hash) {
return Ok(false);
}
}
}
Ok(true)
}
pub fn compute_root(&self) -> ContentHash {
if self.nodes.is_empty() {
return ContentHash("empty".to_string());
}
let mut leaves: Vec<_> = self.nodes.values()
.filter(|node| node.children.is_empty())
.collect();
leaves.sort_by_key(|node| &node.hash);
while leaves.len() > 1 {
let mut new_level = Vec::new();
for chunk in leaves.chunks(2) {
let mut hasher = sha2::Sha256::new();
hasher.update(chunk[0].hash.0.as_bytes());
if chunk.len() > 1 {
hasher.update(chunk[1].hash.0.as_bytes());
}
let combined_hash = ContentHash(format!("{:x}", hasher.finalize()));
new_level.push(combined_hash);
}
leaves = new_level.into_iter()
.map(|hash| MerkleNode {
hash: hash.clone(),
data: Vec::new(),
children: Vec::new(),
timestamp: 0,
})
.collect();
}
leaves[0].hash.clone()
}
pub fn compute_subtree_root(&self, root_hash: &ContentHash) -> Result<ContentHash> {
if !self.nodes.contains_key(root_hash) {
return Err(KotobaError::Storage("Root hash not found".to_string()));
}
self.compute_merkle_root_recursive(root_hash)
}
fn compute_merkle_root_recursive(&self, hash: &ContentHash) -> Result<ContentHash> {
let node = self.nodes.get(hash)
.ok_or_else(|| KotobaError::Storage("Node not found".to_string()))?;
if node.children.is_empty() {
return Ok(hash.clone());
}
let mut child_hashes = Vec::new();
for child_hash in &node.children {
let child_root = self.compute_merkle_root_recursive(child_hash)?;
child_hashes.push(child_root.0);
}
let mut hasher = sha2::Sha256::new();
hasher.update(&node.data);
for child_hash in child_hashes {
hasher.update(child_hash.as_bytes());
}
Ok(ContentHash(format!("{:x}", hasher.finalize())))
}
pub fn compare_trees(&self, other: &MerkleDAG) -> TreeComparison {
let mut differences = Vec::new();
let mut self_only = Vec::new();
let mut other_only = Vec::new();
for (hash, self_node) in &self.nodes {
if let Some(other_node) = other.nodes.get(hash) {
if self_node.data != other_node.data ||
self_node.children != other_node.children {
differences.push(hash.clone());
}
} else {
self_only.push(hash.clone());
}
}
for hash in other.nodes.keys() {
if !self.nodes.contains_key(hash) {
other_only.push(hash.clone());
}
}
TreeComparison {
identical: differences.is_empty() && self_only.is_empty() && other_only.is_empty(),
differences,
self_only,
other_only,
}
}
pub fn find_missing_data(&self, required_hashes: &[ContentHash]) -> Vec<ContentHash> {
required_hashes.iter()
.filter(|hash| !self.nodes.contains_key(hash))
.cloned()
.collect()
}
pub fn detect_corruption(&self) -> Result<Vec<ContentHash>> {
let mut corrupted = Vec::new();
for (hash, node) in &self.nodes {
let mut hasher = sha2::Sha256::new();
hasher.update(&node.data);
for child in &node.children {
hasher.update(child.0.as_bytes());
}
let computed_hash = format!("{:x}", hasher.finalize());
if computed_hash != hash.0 {
corrupted.push(hash.clone());
}
}
Ok(corrupted)
}
pub fn hash_json<T: serde::Serialize>(&mut self, value: &T) -> Result<ContentHash> {
let json = serde_json::to_string(value)
.map_err(|e| KotobaError::Storage(format!("JSON serialization error: {}", e)))?;
let normalized: serde_json::Value = serde_json::from_str(&json)
.map_err(|e| KotobaError::Storage(format!("JSON parse error: {}", e)))?;
let normalized_json = serde_json::to_string(&normalized)
.map_err(|e| KotobaError::Storage(format!("JSON serialization error: {}", e)))?;
Ok(self.store(normalized_json.as_bytes(), Vec::new()))
}
pub fn hash_graph(&mut self, graph: &crate::graph::Graph) -> Result<ContentHash> {
let mut children = Vec::new();
for vertex in graph.vertices.values() {
let hash = self.hash_json(vertex)?;
children.push(hash);
}
for edge in graph.edges.values() {
let hash = self.hash_json(edge)?;
children.push(hash);
}
let graph_data = format!("graph:v{}:e{}",
graph.vertex_count(),
graph.edge_count()
);
Ok(self.store(graph_data.as_bytes(), children))
}
}
#[derive(Debug)]
pub struct GraphVersion {
dag: MerkleDAG,
current_hash: Option<ContentHash>,
history: Vec<(u64, ContentHash)>, }
impl GraphVersion {
pub fn new() -> Self {
Self {
dag: MerkleDAG::new(),
current_hash: None,
history: Vec::new(),
}
}
pub fn commit(&mut self, graph: &crate::graph::Graph) -> Result<ContentHash> {
let hash = self.dag.hash_graph(graph)?;
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs();
self.history.push((timestamp, hash.clone()));
self.current_hash = Some(hash.clone());
Ok(hash)
}
pub fn get(&self, hash: &ContentHash) -> Option<&MerkleNode> {
self.dag.get(hash)
}
pub fn current_hash(&self) -> Option<&ContentHash> {
self.current_hash.as_ref()
}
pub fn history(&self) -> &[(u64, ContentHash)] {
&self.history
}
}