1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
#![deny(missing_docs)]
#![doc = include_str!("../README.md")]

pub mod poseidon;
pub mod roinput;
pub use mina_curves::pasta::Fp;
pub use poseidon::{PoseidonHasherKimchi, PoseidonHasherLegacy};
pub use roinput::ROInput;

use ark_ff::PrimeField;
use o1_utils::FieldHelpers;

/// The domain parameter trait is used during hashing to convey extra
/// arguments to domain string generation.  It is also used by generic signing code.
pub trait DomainParameter: Clone {
    /// Conversion into vector of bytes
    fn into_bytes(self) -> Vec<u8>;
}

impl DomainParameter for () {
    fn into_bytes(self) -> Vec<u8> {
        vec![]
    }
}

impl DomainParameter for u32 {
    fn into_bytes(self) -> Vec<u8> {
        self.to_le_bytes().to_vec()
    }
}

impl DomainParameter for u64 {
    fn into_bytes(self) -> Vec<u8> {
        self.to_le_bytes().to_vec()
    }
}

/// Interface for hashable objects
///
/// Mina uses fixed-length hashing with domain separation for each type of object hashed.
/// The prior means that `Hashable` only supports types whose size is not variable.
///
/// **Important:** The developer MUST assure that all domain strings used throughout the
/// system are unique and that all structures hashed are of fixed size.
///
/// Here is an example of how to implement the `Hashable` trait for am `Example` type.
///
/// ```rust
/// use mina_hasher::{Hashable, ROInput};
///
/// #[derive(Clone)]
/// struct Example;
///
/// impl Hashable for Example {
///     type D = ();
///
///     fn to_roinput(&self) -> ROInput {
///         let roi = ROInput::new();
///         // Serialize example members
///         // ...
///         roi
///     }
///
///     fn domain_string(_: Self::D) -> Option<String> {
///        format!("Example").into()
///    }
/// }
/// ```
///
/// See example in [`ROInput`] documentation
pub trait Hashable: Clone {
    /// Generic domain string argument type
    type D: DomainParameter;

    /// Serialization to random oracle input
    fn to_roinput(&self) -> ROInput;

    /// Generate unique domain string of length `<= 20`.
    ///
    /// The length bound is guarded by an assertion, but uniqueness must
    /// be enforced by the developer implementing the traits (see [`Hashable`] for
    /// more details). The domain string may be parameterized by the contents of
    /// the generic `domain_param` argument.
    ///
    /// **Note:** You should always return `Some(String)`. A `None` return value
    /// is only used for testing.
    fn domain_string(domain_param: Self::D) -> Option<String>;
}

/// Interface for hashing [`Hashable`] inputs
///
/// Mina uses a unique hasher configured with domain separation for each type of object hashed.
/// The underlying hash parameters are large and costly to initialize, so the [`Hasher`] interface
/// provides a reusable context for efficient hashing with domain separation.
///
/// Example usage
///
/// ```rust
/// use mina_hasher::{create_legacy, Fp, Hashable, Hasher, ROInput};
///
/// #[derive(Clone)]
/// struct Something;
///
/// impl Hashable for Something {
///     type D = u32;
///
///     fn to_roinput(&self) -> ROInput {
///         let mut roi = ROInput::new();
///         // ... serialize contents of self
///         roi
///     }
///
///     fn domain_string(id: Self::D) -> Option<String> {
///         format!("Something {}", id).into()
///     }
/// }
///
/// let mut hasher = create_legacy::<Something>(123);
/// let output: Fp = hasher.hash(&Something { });
/// ```
///
pub trait Hasher<H: Hashable> {
    /// Set the initial state based on domain separation string
    /// generated from `H::domain_string(domain_param)`
    fn init(&mut self, domain_param: H::D) -> &mut dyn Hasher<H>;

    /// Restore the initial state that was set most recently
    fn reset(&mut self) -> &mut dyn Hasher<H>;

    /// Consume hash `input`
    fn update(&mut self, input: &H) -> &mut dyn Hasher<H>;

    /// Obtain has result output
    fn digest(&mut self) -> Fp;

    /// Hash input and obtain result output
    fn hash(&mut self, input: &H) -> Fp {
        self.reset();
        self.update(input);
        let output = self.digest();
        self.reset();
        output
    }

    /// Initialize state, hash input and obtain result output
    fn init_and_hash(&mut self, domain_param: H::D, input: &H) -> Fp {
        self.init(domain_param);
        self.update(input);
        let output = self.digest();
        self.reset();
        output
    }
}

/// Transform domain prefix string to field element
fn domain_prefix_to_field<F: PrimeField>(prefix: String) -> F {
    const MAX_DOMAIN_STRING_LEN: usize = 20;
    assert!(prefix.len() <= MAX_DOMAIN_STRING_LEN);
    let prefix = &prefix[..std::cmp::min(prefix.len(), MAX_DOMAIN_STRING_LEN)];
    let mut bytes = format!("{prefix:*<MAX_DOMAIN_STRING_LEN$}")
        .as_bytes()
        .to_vec();
    bytes.resize(F::size_in_bytes(), 0);
    F::from_bytes(&bytes).expect("invalid domain bytes")
}

/// Create a legacy hasher context
pub fn create_legacy<H: Hashable>(domain_param: H::D) -> PoseidonHasherLegacy<H> {
    poseidon::new_legacy::<H>(domain_param)
}

/// Create an experimental kimchi hasher context
pub fn create_kimchi<H: Hashable>(domain_param: H::D) -> PoseidonHasherKimchi<H>
where
    H::D: DomainParameter,
{
    poseidon::new_kimchi::<H>(domain_param)
}