mina_tree/port_ocaml/
hash.rs1use std::hash::Hasher;
5
6use ark_ff::{BigInteger, BigInteger256};
7use mina_curves::pasta::Fp;
8
9use crate::AccountId;
10
11#[derive(Debug)]
12pub(super) struct JaneStreetHasher {
13 h: u32,
14}
15
16#[allow(clippy::derivable_impls)]
17impl Default for JaneStreetHasher {
18 fn default() -> Self {
19 Self { h: 0 }
22 }
23}
24
25#[allow(clippy::precedence)]
26fn rotl32(x: u32, n: u32) -> u32 {
27 (x) << n | (x) >> (32 - n)
28}
29
30fn mix(mut h: u32, mut d: u32) -> u32 {
32 d = d.wrapping_mul(0xcc9e2d51);
33 d = rotl32(d, 15);
34 d = d.wrapping_mul(0x1b873593);
35 h ^= d;
36 h = rotl32(h, 13);
37 h = h.wrapping_mul(5).wrapping_add(0xe6546b64);
38 h
39}
40
41fn final_mix(mut h: u32) -> u32 {
43 h ^= h >> 16;
44 h = h.wrapping_mul(0x85ebca6b);
45 h ^= h >> 13;
46 h = h.wrapping_mul(0xc2b2ae35);
47 h ^= h >> 16;
48 h
49}
50
51impl Hasher for JaneStreetHasher {
52 fn finish(&self) -> u64 {
54 let h = final_mix(self.h);
55 let h: u32 = h & 0x3FFF_FFFF; h as u64
57 }
58
59 fn write(&mut self, s: &[u8]) {
61 for (chunk, chunk_len) in s.chunks(4).map(|chunk| (chunk, chunk.len())) {
63 let w: u32 = if chunk_len == 4 {
64 u32::from_le_bytes(chunk.try_into().unwrap())
65 } else {
66 let mut w = [0u8; 4];
68 w[..chunk_len].copy_from_slice(chunk);
69 u32::from_le_bytes(w)
70 };
71 self.h = mix(self.h, w);
72 }
73
74 self.h ^= (s.len() & 0xFFFF_FFFF) as u32;
76 }
77
78 fn write_u32(&mut self, i: u32) {
79 self.h = mix(self.h, i);
80 }
81
82 fn write_i64(&mut self, d: i64) {
84 let n = (d >> 32) ^ (d >> 63) ^ d;
85 self.h = mix(self.h, n as u32);
86 }
87}
88
89fn hash_field(f: &Fp) -> u32 {
91 let mut acc = 0;
92
93 let bigint: BigInteger256 = (*f).into();
94 let bigint = bigint.to_64x4();
95 let nignore: usize = bigint.iter().rev().take_while(|&b| *b == 0).count();
96
97 for bigint in bigint.iter().take(BigInteger256::NUM_LIMBS - nignore) {
98 acc = mix(acc, (bigint & 0xFFFF_FFFF) as u32);
99 acc = mix(acc, (bigint >> 32) as u32);
100 }
101
102 if bigint.last().unwrap() & 0x8000_0000_0000_0000 != 0 {
103 acc += 1;
105 }
106
107 acc
108}
109
110pub fn account_id_ocaml_hash(account_id: &AccountId) -> u32 {
111 let mut hasher = JaneStreetHasher::default();
112 hasher.write_u32(hash_field(&account_id.public_key.x));
113 hasher.write_u32(account_id.public_key.is_odd as u32);
114 hasher.write_u32(hash_field(&account_id.token_id.0));
115 hasher.finish() as u32
116}
117
118pub trait OCamlHash {
119 fn ocaml_hash(&self) -> u32;
120}
121
122impl OCamlHash for AccountId {
123 fn ocaml_hash(&self) -> u32 {
124 account_id_ocaml_hash(self)
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use std::str::FromStr;
131
132 use mina_signer::CompressedPubKey;
133
134 use super::*;
135
136 #[test]
137 fn test_hash_fp() {
138 const TOKEN_ID: &str =
139 "4967745125912355413023996042397960619663294160624364664207862415774117920201";
140 const PK: &str =
141 "9706454913247140231138457869942480497494390376582974522927223806983428482248";
142
143 let fp = Fp::from_str(TOKEN_ID).unwrap();
144 let acc = hash_field(&fp);
145 dbg!(acc as i32);
146 assert_eq!(acc, (-1384641025i32) as u32);
147
148 let fp = Fp::from_str(PK).unwrap();
149 let acc = hash_field(&fp);
150 dbg!(acc as i32);
151 assert_eq!(acc, (-2044176935i32) as u32);
152
153 let fp = Fp::from_str(TOKEN_ID).unwrap();
154 let mut hasher = JaneStreetHasher { h: 1178103103 };
155 hasher.write_u32(hash_field(&fp));
156 assert_eq!(hasher.h, 327258694);
157
158 let fp = Fp::from_str(TOKEN_ID).unwrap();
159 let hash = {
160 let mut hasher = JaneStreetHasher::default();
161 hasher.write_u32(hash_field(&fp));
162 hasher.finish()
163 };
164 assert_eq!(hash, 712332047);
165
166 let pk = CompressedPubKey {
167 x: Fp::from_str(PK).unwrap(),
168 is_odd: true,
169 };
170 let hash = {
171 let mut hasher = JaneStreetHasher::default();
172 hasher.write_u32(hash_field(&pk.x));
173 hasher.write_u32(pk.is_odd as u32);
174 hasher.finish()
175 };
176 assert_eq!(hash, 284693557);
177
178 let account_id = AccountId {
179 public_key: pk,
180 token_id: crate::TokenId(Fp::from_str(TOKEN_ID).unwrap()),
181 };
182 let hash = {
183 let mut hasher = JaneStreetHasher::default();
184 hasher.write_u32(hash_field(&account_id.public_key.x));
185 hasher.write_u32(account_id.public_key.is_odd as u32);
186 hasher.write_u32(hash_field(&account_id.token_id.0));
187 hasher.finish()
188 };
189 assert_eq!(hash, 386994658);
190 }
191}