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)
}