use blake3::Hash;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Chunk {
pub offset: u64,
pub length: usize,
pub hash: Hash,
}
pub enum ChunkEvent {
Chunk(Chunk),
Done,
}
pub const CHUNK_ROOT_PREFIX: [u8; 32] = *b"ffsync chunk tree root v1\0\0\0\0\0\0\0";
pub fn chunk_root(chunks: &[Chunk]) -> Hash {
let mut hasher = blake3::Hasher::new();
hasher.update(&CHUNK_ROOT_PREFIX);
for chunk in chunks {
hasher.update(chunk.hash.as_bytes());
}
hasher.finalize()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn chunk_root_is_order_sensitive_and_prefix_bound() {
let c1 = Chunk {
offset: 0,
length: 4,
hash: blake3::hash(b"aaaa"),
};
let c2 = Chunk {
offset: 4,
length: 4,
hash: blake3::hash(b"bbbb"),
};
let ab = chunk_root(&[c1.clone(), c2.clone()]);
let ba = chunk_root(&[c2.clone(), c1.clone()]);
assert_ne!(ab, ba);
assert_eq!(chunk_root(&[c1.clone(), c2.clone()]), ab);
let _empty = chunk_root(&[]);
let mut concat = Vec::new();
concat.extend_from_slice(c1.hash.as_bytes());
concat.extend_from_slice(c2.hash.as_bytes());
assert_ne!(chunk_root(&[c1, c2]), blake3::hash(&concat));
}
}