use sha2::{Digest, Sha256};
use crate::{Epoch, Hash, StreamId};
const GENESIS_DOMAIN: &[u8] = b"efema/chain/v1/genesis";
pub fn genesis(stream: &StreamId) -> Hash {
let mut hasher = Sha256::new();
hasher.update(GENESIS_DOMAIN);
hasher.update(stream.as_bytes());
Hash::from_bytes(hasher.finalize().into())
}
pub fn link(previous: &Hash, seq: u64, epoch: Epoch, data: &[u8]) -> Hash {
let mut hasher = Sha256::new();
hasher.update(previous.as_bytes());
hasher.update(seq.to_be_bytes());
hasher.update(epoch.0.to_be_bytes());
hasher.update((data.len() as u64).to_be_bytes());
hasher.update(data);
Hash::from_bytes(hasher.finalize().into())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_genesis_link_depends_on_the_stream() {
let a = StreamId::from_bytes([1; 16]);
let b = StreamId::from_bytes([2; 16]);
assert_ne!(genesis(&a), genesis(&b));
assert_eq!(genesis(&a), genesis(&a));
}
#[test]
fn every_field_of_an_entry_moves_its_link() {
let start = genesis(&StreamId::from_bytes([0; 16]));
let base = link(&start, 1, Epoch(1), b"abc");
assert_ne!(base, link(&genesis(&StreamId::from_bytes([9; 16])), 1, Epoch(1), b"abc"));
assert_ne!(base, link(&start, 2, Epoch(1), b"abc"));
assert_ne!(base, link(&start, 1, Epoch(2), b"abc"));
assert_ne!(base, link(&start, 1, Epoch(1), b"abd"));
assert_ne!(base, link(&start, 1, Epoch(1), b"ab"));
}
#[test]
fn the_chain_is_frozen() {
let stream = StreamId::from_bytes(*b"0123456789abcdef");
let start = genesis(&stream);
assert_eq!(start.to_string(), "78e6c7beebfabda54547131dd021d12e587a91f113db763dc44cc0170ae18482");
let first = link(&start, 1, Epoch(1), b"hello");
assert_eq!(first.to_string(), "1338b06d59cf4930e7d0d151251285b1f1b2f824f29dff4b036d7f38f0c42a20");
}
}