use std::sync::LazyLock;
pub type Hash = [u8; 32];
pub static DOMAIN_PROLOG8_FACT: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.fact.v1", b""));
pub static DOMAIN_PROLOG8_BLOCK_HEADER: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.block_header.v1", b""));
pub static DOMAIN_PROLOG8_BLOCK: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.block.v1", b""));
pub static DOMAIN_PROLOG8_PROOF_NODE: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.proof_node.v1", b""));
pub static DOMAIN_PROLOG8_PROOF_ROOT: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.proof_root.v1", b""));
pub static DOMAIN_PROLOG8_RECEIPT: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.receipt.v1", b""));
pub static DOMAIN_PROLOG8_INPUT: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.input.v1", b""));
pub static DOMAIN_PROLOG8_OUTPUT: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.output.v1", b""));
pub static DOMAIN_PROLOG8_CATALOG: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.catalog.v1", b""));
pub static DOMAIN_PROLOG8_RULES: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.rules.v1", b""));
pub static DOMAIN_PROLOG8_LINK: LazyLock<[u8; 32]> =
LazyLock::new(|| blake3::derive_key("prolog8.link.v1", b""));
pub fn hash_bytes(domain: &[u8; 32], bytes: &[u8]) -> Hash {
let mut hasher = blake3::Hasher::new_keyed(domain);
hasher.update(bytes);
hasher.finalize().into()
}
pub fn combine_roots(roots: &[&Hash]) -> Hash {
let mut hasher = blake3::Hasher::new_keyed(&DOMAIN_PROLOG8_RECEIPT);
hasher.update(&(roots.len() as u32).to_le_bytes());
for r in roots {
hasher.update(&32u32.to_le_bytes());
hasher.update(*r);
}
hasher.finalize().into()
}
pub fn link_hash(step: u64, input: &Hash, output: &Hash, prev: &Hash) -> Hash {
let mut hasher = blake3::Hasher::new_keyed(&DOMAIN_PROLOG8_LINK);
hasher.update(&step.to_le_bytes());
hasher.update(&32u32.to_le_bytes());
hasher.update(input);
hasher.update(&32u32.to_le_bytes());
hasher.update(output);
hasher.update(&32u32.to_le_bytes());
hasher.update(prev);
hasher.finalize().into()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn domain_keys_are_distinct() {
assert_ne!(*DOMAIN_PROLOG8_FACT, *DOMAIN_PROLOG8_RECEIPT);
assert_ne!(*DOMAIN_PROLOG8_PROOF_NODE, *DOMAIN_PROLOG8_RECEIPT);
assert_ne!(*DOMAIN_PROLOG8_INPUT, *DOMAIN_PROLOG8_OUTPUT);
}
#[test]
fn combine_roots_is_collision_resistant_under_length_prefix() {
let a = [1u8; 32];
let b = [2u8; 32];
let c = [3u8; 32];
let h1 = combine_roots(&[&a, &b, &c]);
let h2 = combine_roots(&[&a, &b]);
assert_ne!(h1, h2);
}
#[test]
fn link_hash_chains_consistently() {
let z = [0u8; 32];
let a = [1u8; 32];
let h0 = link_hash(0, &a, &a, &z);
let h1 = link_hash(1, &a, &a, &h0);
assert_eq!(h0, link_hash(0, &a, &a, &z));
assert_eq!(h1, link_hash(1, &a, &a, &h0));
let h1_alt = link_hash(1, &a, &a, &z);
assert_ne!(h1, h1_alt);
}
}