Skip to main content

molframe_core/contract/policy/
fingerprint.rs

1//! Telling one policy or input from another.
2//!
3//! The standard hasher is seeded afresh each process, which is right for hash
4//! tables and wrong here: a fingerprint that changes between runs identifies the
5//! run rather than the thing. This one does not.
6
7use std::fmt;
8use std::hash::Hasher;
9
10/// FNV-1a's specified 64-bit initial state.
11const FNV1A64_OFFSET_BASIS: u64 = 0xcbf2_9ce4_8422_2325;
12/// FNV-1a's specified 64-bit prime multiplier.
13const FNV1A64_PRIME: u64 = 0x0000_0100_0000_01b3;
14
15/// A fast, stable 64-bit fingerprint of a policy, a record or a cache entry.
16///
17/// It is not collision resistant and must not be used to claim that two blocks of bytes are
18/// identical; [`ContentDigest`](crate::contract::ContentDigest) is SHA-256 and is what
19/// verification rests on.
20#[derive(Clone, Copy, PartialEq, Eq, Hash, Default)]
21pub struct Fingerprint(u64);
22
23impl Fingerprint {
24    /// Wraps a finished hash.
25    pub(super) const fn from_hash(hash: u64) -> Self {
26        Self(hash)
27    }
28
29    /// Fingerprints a block of bytes.
30    #[must_use]
31    pub fn of(bytes: &[u8]) -> Self {
32        let mut hasher = Fnv1a::new();
33        hasher.write(bytes);
34        Self(hasher.finish())
35    }
36
37    /// The raw value.
38    #[must_use]
39    pub const fn get(self) -> u64 {
40        self.0
41    }
42}
43
44impl fmt::Display for Fingerprint {
45    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
46        write!(f, "fnv1a64:{:016x}", self.0)
47    }
48}
49
50impl fmt::Debug for Fingerprint {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        write!(f, "{self}")
53    }
54}
55
56/// A hasher whose output does not depend on how the program was started.
57///
58/// The standard hasher is randomly seeded per process, which is right for
59/// hash tables and wrong here: a fingerprint that changes between runs cannot
60/// identify anything.
61pub(super) struct Fnv1a(u64);
62
63impl Fnv1a {
64    pub(super) const fn new() -> Self {
65        Self(FNV1A64_OFFSET_BASIS)
66    }
67}
68
69impl Hasher for Fnv1a {
70    fn finish(&self) -> u64 {
71        self.0
72    }
73
74    fn write(&mut self, bytes: &[u8]) {
75        for byte in bytes {
76            self.0 ^= u64::from(*byte);
77            // FNV-1a is defined modulo 2^64, so wrapping is part of the
78            // algorithm rather than an overflow fallback.
79            self.0 = self.0.wrapping_mul(FNV1A64_PRIME);
80        }
81    }
82}