CAN Service: content-addressable storage with HTTP API, SQLite metadata, file-based blob storage, thumbnail generation, and integrity verification. can-sync v1: P2P sync sidecar using iroh-docs for encrypted peer-to-peer replication with library/filter-based selective sync. Fully builds but being superseded by v2 (simplified full-mirror approach). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
49 lines
1.4 KiB
Rust
49 lines
1.4 KiB
Rust
use sha2::{Digest, Sha256};
|
|
|
|
/// Compute SHA-256 hash as: SHA256(timestamp_bytes + content_bytes).
|
|
/// The timestamp is serialized as its string representation's bytes,
|
|
/// matching the spec: `SHA256([timestamp_bytes] + [raw_file_content_bytes])`.
|
|
pub fn compute_hash(timestamp_ms: i64, content: &[u8]) -> String {
|
|
let mut hasher = Sha256::new();
|
|
hasher.update(timestamp_ms.to_be_bytes());
|
|
hasher.update(content);
|
|
hex::encode(hasher.finalize())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_deterministic_hash() {
|
|
let ts = 1773014400123i64;
|
|
let content = b"hello world";
|
|
let h1 = compute_hash(ts, content);
|
|
let h2 = compute_hash(ts, content);
|
|
assert_eq!(h1, h2);
|
|
assert_eq!(h1.len(), 64); // SHA-256 hex = 64 chars
|
|
}
|
|
|
|
#[test]
|
|
fn test_different_timestamp_different_hash() {
|
|
let content = b"same content";
|
|
let h1 = compute_hash(1000, content);
|
|
let h2 = compute_hash(2000, content);
|
|
assert_ne!(h1, h2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_different_content_different_hash() {
|
|
let ts = 1234567890i64;
|
|
let h1 = compute_hash(ts, b"content A");
|
|
let h2 = compute_hash(ts, b"content B");
|
|
assert_ne!(h1, h2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_empty_content() {
|
|
let h = compute_hash(0, b"");
|
|
assert_eq!(h.len(), 64);
|
|
}
|
|
}
|