//! Content addressing for Trident: AST normalization + Poseidon2 hashing.
//!
//! Every function definition gets a cryptographic identity (Poseidon2 hash)
//! based on its normalized AST. Names are replaced with de Bruijn indices,
//! dependency references are replaced with their hashes, and the result is
//! deterministically serialized before hashing.
//!
//! Uses Poseidon2 over the Goldilocks field for SNARK-friendly content
//! addressing โ€” content hashes are cheaply provable inside ZK proofs,
//! enabling trustless compilation verification and on-chain registries.
//!
//! Properties:
//! - Two functions with identical computation but different variable names
//!   produce the same hash.
//! - Changing any dependency changes the hash of all dependents.
//! - Renaming a function does not change its hash.
//! - Adding/removing comments or formatting does not change the hash.

use std::collections::BTreeMap;

use crate::ast::*;

const HASH_VERSION: u8 = 1;

// โ”€โ”€โ”€ Content Hash โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

/// A 256-bit BLAKE3 content hash.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ContentHash(pub [u8; 32]);

impl ContentHash {
    /// Zero hash (used as placeholder).
    pub fn zero() -> Self {
        Self([0u8; 32])
    }

    /// Display as full hex.
    pub fn to_hex(&self) -> String {
        self.0.iter().map(|b| format!("{:02x}", b)).collect()
    }

    /// Parse a 64-character hex string into a ContentHash.
    pub fn from_hex(hex: &str) -> Option<Self> {
        if hex.len() != 64 {
            return None;
        }
        let mut bytes = [0u8; 32];
        for (i, chunk) in hex.as_bytes().chunks(2).enumerate() {
            if i >= 32 || chunk.len() < 2 {
                return None;
            }
            let hi = hex_digit(chunk[0])?;
            let lo = hex_digit(chunk[1])?;
            bytes[i] = (hi << 4) | lo;
        }
        Some(ContentHash(bytes))
    }

    /// Display as short base-32 (8 characters, 40 bits).
    pub fn to_short(&self) -> String {
        // Take first 5 bytes (40 bits), encode as base-32
        const ALPHABET: &[u8] = b"0123456789abcdefghjkmnpqrstuvwxyz";
        let val = u64::from_be_bytes([
            0, 0, 0, self.0[0], self.0[1], self.0[2], self.0[3], self.0[4],
        ]);
        let mut result = String::with_capacity(8);
        for i in (0..8).rev() {
            let idx = ((val >> (i * 5)) & 0x1F) as usize;
            result.push(ALPHABET[idx] as char);
        }
        result
    }
}

impl std::fmt::Debug for ContentHash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "#{}", self.to_short())
    }
}

impl std::fmt::Display for ContentHash {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "#{}", self.to_short())
    }
}

pub(crate) mod normalize;
mod serialize;
pub use normalize::Normalizer;

// โ”€โ”€โ”€ Public API โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€

/// Hash a single function.
pub fn hash_function(func: &FnDef, deps: BTreeMap<String, ContentHash>) -> ContentHash {
    Normalizer::hash_fn(func, deps)
}

/// Hash all functions in a file, returning name โ†’ hash map.
pub fn hash_file(file: &File) -> BTreeMap<String, ContentHash> {
    Normalizer::hash_file(file)
}

/// Hash a complete file's content (all items serialized together).
/// Uses Poseidon2 for SNARK-friendly file-level content addressing.
pub fn hash_file_content(file: &File) -> ContentHash {
    let fn_hashes = hash_file(file);
    let mut buf = Vec::new();
    buf.push(HASH_VERSION);
    // Hash file metadata
    buf.extend_from_slice(file.name.node.as_bytes());
    // Hash all function hashes in sorted order for determinism
    let mut sorted: Vec<_> = fn_hashes.iter().collect();
    sorted.sort_by_key(|(name, _)| (*name).clone());
    for (name, hash) in sorted {
        buf.extend_from_slice(name.as_bytes());
        buf.extend_from_slice(&hash.0);
    }
    ContentHash(crate::poseidon2::hash_bytes(&buf))
}

/// Parse a single hex digit (0-9, a-f, A-F) to its numeric value.
fn hex_digit(b: u8) -> Option<u8> {
    match b {
        b'0'..=b'9' => Some(b - b'0'),
        b'a'..=b'f' => Some(b - b'a' + 10),
        b'A'..=b'F' => Some(b - b'A' + 10),
        _ => None,
    }
}

#[cfg(test)]
mod tests;

Dimensions

trident/src/diagnostic/mod.rs
trident/src/ir/mod.rs
trident/src/deploy/mod.rs
trident/src/syntax/mod.rs
trident/src/api/mod.rs
nebu/rs/extension/mod.rs
optica/src/render/mod.rs
trident/src/config/mod.rs
trident/src/field/mod.rs
trident/src/cli/mod.rs
optica/src/parser/mod.rs
trident/src/neural/mod.rs
trident/src/cost/mod.rs
trident/src/typecheck/mod.rs
optica/src/server/mod.rs
trident/src/package/mod.rs
optica/src/scanner/mod.rs
optica/src/output/mod.rs
trident/src/verify/mod.rs
optica/src/graph/mod.rs
trident/src/ast/mod.rs
trident/src/lsp/mod.rs
trident/src/runtime/mod.rs
trident/src/gpu/mod.rs
optica/src/query/mod.rs
trident/src/lsp/semantic/mod.rs
trident/src/verify/equiv/mod.rs
trident/src/neural/training/mod.rs
trident/src/verify/synthesize/mod.rs
trident/src/ir/tir/mod.rs
rs/macros/src/addressed/mod.rs
trident/src/package/registry/mod.rs
rs/rsc/src/lints/mod.rs
trident/src/verify/report/mod.rs
trident/src/config/resolve/mod.rs
trident/src/verify/solve/mod.rs
rs/macros/src/registers/mod.rs
trident/src/verify/smt/mod.rs
rs/macros/src/cell/mod.rs
rs/core/src/fixed_point/mod.rs
trident/src/neural/data/mod.rs
rs/core/src/bounded/mod.rs
trident/src/lsp/util/mod.rs
trident/src/typecheck/tests/mod.rs
trident/src/neural/model/mod.rs
trident/src/cost/stack_verifier/mod.rs
trident/src/syntax/grammar/mod.rs
trident/src/package/manifest/mod.rs
trident/src/syntax/parser/mod.rs
trident/src/ir/kir/mod.rs
trident/src/neural/inference/mod.rs
trident/src/syntax/lexer/mod.rs
trident/src/cost/model/mod.rs
trident/src/ir/lir/mod.rs
trident/src/syntax/format/mod.rs
trident/src/config/scaffold/mod.rs
trident/src/verify/sym/mod.rs
trident/src/api/tests/mod.rs
trident/src/package/store/mod.rs
trident/src/ir/tree/mod.rs
trident/src/ir/kir/lower/mod.rs
trident/src/ir/lir/lower/mod.rs
trident/src/ir/tir/lower/mod.rs
trident/src/ir/tir/builder/mod.rs
trident/src/ir/tir/neural/mod.rs
trident/src/neural/data/tir_graph/mod.rs
trident/src/syntax/parser/tests/mod.rs
cw-cyber/packages/cyber-std/src/tokenfactory/mod.rs
trident/src/ir/tree/lower/mod.rs
trident/src/ir/tir/stack/mod.rs
cw-cyber/contracts/cybernet/src/tests/mod.rs
trident/src/ir/tir/optimize/mod.rs

Local Graph