soroban_utils/
poseidon2.rs

1//! Poseidon2 hash functions for Soroban contracts
2
3use crate::constants::{bn256_modulus, get_round_constants_t2, get_round_constants_t3};
4use soroban_sdk::{Bytes, Env, U256, Vec, symbol_short, vec};
5
6/// Hash a pair of U256 values using Poseidon2 compression mode
7///
8/// This function performs Poseidon2 compression on two field elements.
9/// The inputs must be within the BN256 field range [0, p-1).
10///
11/// # Arguments
12/// * `env` - The Soroban environment
13/// * `left` - Left input value (must be < BN256 modulus)
14/// * `right` - Right input value (must be < BN256 modulus)
15///
16/// # Returns
17/// The compressed hash result as U256
18///
19/// # Panics
20/// Panics if either input is >= BN256 modulus
21pub fn poseidon2_compress(env: &Env, left: U256, right: U256) -> U256 {
22    // Check inputs are within field range
23    let bn256_mod = bn256_modulus(env);
24    if left >= bn256_mod || right >= bn256_mod {
25        panic!("Hash inputs must be within the BN256 range [0.p-1)");
26    }
27
28    // Get round constants
29    let round_constants = get_round_constants_t2(env);
30    let crypto_hazmat = env.crypto_hazmat();
31
32    // Permutation and compression
33    let out = crypto_hazmat.poseidon2_permutation(
34        &vec![env, left.clone(), right.clone()],
35        symbol_short!("BN254"),
36        2,
37        5,
38        8,
39        56,
40        &vec![env, U256::from_u32(env, 1u32), U256::from_u32(env, 2u32)],
41        &round_constants,
42    );
43
44    // Compression step: we get elements from the output vector and add the inputs
45    let out_0 = out.get(0).unwrap();
46    let mut compressed_0 = out_0.add(&left);
47    if compressed_0 >= bn256_mod {
48        compressed_0 = compressed_0.rem_euclid(&bn256_mod);
49    }
50    // Note we do not consider the second element of the output vector as we
51    // truncate to the first element
52    compressed_0
53}
54
55/// Hash a pair of U256 values and a domain separation tag using Poseidon2
56///
57/// This function performs Poseidon2 hash on three field elements.
58/// The inputs must be within the BN256 field range [0, p-1).
59///
60/// # Arguments
61/// * `env` - The Soroban environment
62/// * `a` - First input value (must be < BN256 modulus)
63/// * `b` - Second input value (must be < BN256 modulus)
64/// * `sep` - Domain separation value (must be < BN256 modulus)
65/// # Returns
66/// The hash result as U256
67///
68/// # Panics
69/// Panics if either input is >= BN256 modulus
70pub fn poseidon2_hash2(env: &Env, a: U256, b: U256, sep: Option<U256>) -> U256 {
71    // Check inputs are within field range
72    let bn256_mod = bn256_modulus(env);
73    // If not provided, we use 0 as domain separation
74    let sep = sep.unwrap_or_else(|| U256::from_u32(env, 0u32));
75    if a >= bn256_mod || b >= bn256_mod || sep >= bn256_mod {
76        panic!("Hash inputs must be within the BN256 range [0.p-1)");
77    }
78
79    // Get round constants
80    let round_constants = get_round_constants_t3(env);
81    let crypto_hazmat = env.crypto_hazmat();
82
83    // Permutation and compression
84    let out = crypto_hazmat.poseidon2_permutation(
85        &vec![env, a.clone(), b.clone(), sep.clone()],
86        symbol_short!("BN254"),
87        3,
88        5,
89        8,
90        56,
91        &vec![
92            env,
93            U256::from_u32(env, 1u32),
94            U256::from_u32(env, 1u32),
95            U256::from_u32(env, 2u32),
96        ],
97        &round_constants,
98    );
99
100    // We get the first element as output
101    out.get(0).unwrap()
102}
103
104/// Get the zero hash values for each level of a Merkle tree
105///
106/// Hash of 0 at the leaf level is defined as Poseidon2 hash of "XLM" encoded as
107/// ASCII. More specifically, t=4, r=3, domain_sep=0. poseidon2(88, 76,77) =
108/// poseidon2("XLM"). From there, we use the poseidon2 compression function to
109/// get the zero hash for each level.
110pub fn get_zeroes(env: &Env) -> Vec<U256> {
111    vec![
112        env,
113        U256::from_be_bytes(
114            env,
115            &Bytes::from_array(
116                env,
117                &[
118                    37, 48, 34, 136, 219, 153, 53, 3, 68, 151, 65, 131, 206, 49, 13, 99, 181, 58,
119                    187, 158, 240, 248, 87, 87, 83, 238, 211, 110, 1, 24, 249, 206,
120                ],
121            ),
122        ), // 0
123        U256::from_be_bytes(
124            env,
125            &Bytes::from_array(
126                env,
127                &[
128                    33, 244, 234, 36, 146, 173, 224, 6, 168, 238, 127, 183, 100, 6, 10, 149, 164,
129                    238, 245, 202, 147, 30, 3, 123, 205, 240, 95, 194, 128, 103, 208, 8,
130                ],
131            ),
132        ), // 1
133        U256::from_be_bytes(
134            env,
135            &Bytes::from_array(
136                env,
137                &[
138                    14, 191, 180, 210, 240, 91, 182, 164, 115, 201, 191, 247, 37, 134, 254, 200, 6,
139                    241, 172, 35, 112, 21, 197, 112, 215, 199, 130, 73, 207, 125, 119, 64,
140                ],
141            ),
142        ), // 2
143        U256::from_be_bytes(
144            env,
145            &Bytes::from_array(
146                env,
147                &[
148                    6, 104, 130, 165, 218, 177, 134, 212, 214, 63, 166, 96, 15, 158, 163, 213, 205,
149                    254, 242, 162, 129, 28, 137, 115, 17, 40, 167, 41, 215, 232, 184, 151,
150                ],
151            ),
152        ), // 3
153        U256::from_be_bytes(
154            env,
155            &Bytes::from_array(
156                env,
157                &[
158                    6, 93, 179, 20, 141, 141, 165, 50, 155, 234, 236, 80, 66, 120, 92, 105, 242,
159                    206, 7, 9, 226, 109, 70, 139, 218, 30, 37, 92, 89, 155, 86, 134,
160                ],
161            ),
162        ), // 4
163        U256::from_be_bytes(
164            env,
165            &Bytes::from_array(
166                env,
167                &[
168                    25, 146, 111, 170, 35, 176, 115, 125, 70, 127, 148, 118, 240, 248, 75, 41, 104,
169                    255, 102, 102, 225, 106, 35, 228, 212, 72, 152, 248, 132, 80, 71, 100,
170                ],
171            ),
172        ), // 5
173        U256::from_be_bytes(
174            env,
175            &Bytes::from_array(
176                env,
177                &[
178                    40, 124, 72, 90, 114, 204, 200, 212, 216, 6, 146, 217, 126, 182, 44, 22, 73,
179                    83, 160, 66, 39, 145, 246, 175, 98, 20, 169, 167, 173, 34, 121, 192,
180                ],
181            ),
182        ), // 6
183        U256::from_be_bytes(
184            env,
185            &Bytes::from_array(
186                env,
187                &[
188                    31, 114, 158, 16, 62, 125, 65, 95, 114, 141, 201, 69, 227, 182, 226, 196, 10,
189                    192, 203, 54, 158, 83, 43, 165, 10, 86, 151, 144, 205, 222, 42, 204,
190                ],
191            ),
192        ), // 7
193        U256::from_be_bytes(
194            env,
195            &Bytes::from_array(
196                env,
197                &[
198                    24, 135, 231, 31, 146, 33, 127, 254, 134, 98, 14, 110, 165, 167, 26, 211, 82,
199                    31, 173, 228, 200, 50, 3, 255, 163, 232, 56, 254, 61, 110, 114, 3,
200                ],
201            ),
202        ), // 8
203        U256::from_be_bytes(
204            env,
205            &Bytes::from_array(
206                env,
207                &[
208                    41, 90, 167, 142, 86, 199, 160, 154, 134, 79, 47, 65, 117, 16, 0, 149, 243,
209                    231, 185, 239, 42, 145, 26, 248, 124, 176, 243, 247, 238, 242, 119, 0,
210                ],
211            ),
212        ), // 9
213        U256::from_be_bytes(
214            env,
215            &Bytes::from_array(
216                env,
217                &[
218                    5, 23, 3, 42, 121, 107, 3, 64, 220, 95, 157, 242, 139, 31, 211, 32, 218, 186,
219                    36, 213, 164, 83, 53, 161, 55, 22, 115, 0, 238, 54, 136, 40,
220                ],
221            ),
222        ), // 10
223        U256::from_be_bytes(
224            env,
225            &Bytes::from_array(
226                env,
227                &[
228                    21, 250, 223, 73, 71, 210, 164, 12, 123, 67, 158, 131, 234, 60, 151, 159, 247,
229                    37, 170, 209, 28, 71, 170, 175, 137, 42, 171, 65, 61, 178, 220, 221,
230                ],
231            ),
232        ), // 11
233        U256::from_be_bytes(
234            env,
235            &Bytes::from_array(
236                env,
237                &[
238                    0, 172, 183, 35, 107, 109, 135, 244, 135, 91, 59, 157, 23, 32, 169, 224, 104,
239                    44, 112, 228, 188, 109, 84, 237, 246, 31, 170, 43, 201, 56, 65, 214,
240                ],
241            ),
242        ), // 12
243        U256::from_be_bytes(
244            env,
245            &Bytes::from_array(
246                env,
247                &[
248                    9, 131, 109, 217, 5, 77, 86, 10, 230, 14, 166, 168, 3, 52, 4, 179, 228, 3, 9,
249                    246, 238, 77, 23, 10, 203, 146, 230, 102, 126, 134, 199, 117,
250                ],
251            ),
252        ), // 13
253        U256::from_be_bytes(
254            env,
255            &Bytes::from_array(
256                env,
257                &[
258                    33, 152, 148, 107, 23, 249, 186, 3, 93, 126, 231, 40, 116, 28, 165, 244, 229,
259                    135, 92, 182, 175, 178, 150, 132, 166, 245, 249, 168, 47, 238, 196, 130,
260                ],
261            ),
262        ), // 14
263        U256::from_be_bytes(
264            env,
265            &Bytes::from_array(
266                env,
267                &[
268                    21, 212, 177, 104, 48, 96, 77, 190, 11, 50, 18, 227, 31, 149, 116, 120, 124,
269                    21, 176, 245, 78, 94, 36, 176, 128, 104, 126, 122, 110, 246, 212, 85,
270                ],
271            ),
272        ), // 15
273        U256::from_be_bytes(
274            env,
275            &Bytes::from_array(
276                env,
277                &[
278                    45, 7, 247, 62, 4, 207, 10, 83, 128, 55, 186, 7, 86, 22, 81, 172, 151, 155,
279                    176, 14, 23, 5, 199, 93, 220, 149, 22, 236, 75, 138, 106, 118,
280                ],
281            ),
282        ), // 16
283        U256::from_be_bytes(
284            env,
285            &Bytes::from_array(
286                env,
287                &[
288                    2, 248, 115, 26, 234, 40, 154, 107, 64, 16, 0, 72, 126, 140, 105, 37, 201, 34,
289                    64, 126, 236, 165, 143, 46, 24, 204, 138, 217, 182, 197, 209, 63,
290                ],
291            ),
292        ), // 17
293        U256::from_be_bytes(
294            env,
295            &Bytes::from_array(
296                env,
297                &[
298                    10, 48, 5, 60, 23, 135, 199, 151, 130, 230, 11, 200, 216, 37, 233, 227, 35,
299                    200, 169, 2, 249, 58, 165, 146, 60, 36, 209, 125, 22, 219, 146, 92,
300                ],
301            ),
302        ), // 18
303        U256::from_be_bytes(
304            env,
305            &Bytes::from_array(
306                env,
307                &[
308                    14, 163, 5, 141, 153, 88, 186, 95, 228, 65, 251, 215, 157, 201, 104, 244, 73,
309                    33, 93, 222, 230, 97, 70, 22, 26, 252, 243, 76, 52, 191, 164, 144,
310                ],
311            ),
312        ), // 19
313        U256::from_be_bytes(
314            env,
315            &Bytes::from_array(
316                env,
317                &[
318                    39, 185, 147, 126, 71, 166, 89, 131, 173, 80, 165, 183, 246, 185, 207, 5, 66,
319                    201, 26, 141, 250, 23, 20, 206, 248, 144, 19, 67, 138, 6, 249, 183,
320                ],
321            ),
322        ), // 20
323        U256::from_be_bytes(
324            env,
325            &Bytes::from_array(
326                env,
327                &[
328                    45, 52, 123, 228, 229, 71, 197, 131, 124, 27, 33, 114, 50, 38, 40, 160, 196,
329                    141, 102, 129, 147, 25, 103, 145, 179, 16, 103, 75, 4, 83, 192, 57,
330                ],
331            ),
332        ), // 21
333        U256::from_be_bytes(
334            env,
335            &Bytes::from_array(
336                env,
337                &[
338                    3, 242, 70, 27, 53, 184, 146, 252, 140, 88, 151, 6, 40, 61, 16, 131, 244, 210,
339                    214, 107, 228, 249, 126, 70, 26, 186, 242, 240, 111, 137, 207, 253,
340                ],
341            ),
342        ), // 22
343        U256::from_be_bytes(
344            env,
345            &Bytes::from_array(
346                env,
347                &[
348                    37, 155, 143, 160, 8, 44, 246, 6, 111, 0, 90, 10, 73, 0, 103, 18, 254, 29, 207,
349                    239, 106, 152, 214, 143, 122, 180, 69, 248, 138, 219, 97, 167,
350                ],
351            ),
352        ), // 23
353        U256::from_be_bytes(
354            env,
355            &Bytes::from_array(
356                env,
357                &[
358                    25, 128, 165, 247, 234, 192, 157, 170, 199, 178, 210, 23, 155, 161, 217, 112,
359                    251, 63, 2, 19, 221, 144, 97, 49, 40, 245, 213, 88, 99, 216, 68, 20,
360                ],
361            ),
362        ), // 24
363        U256::from_be_bytes(
364            env,
365            &Bytes::from_array(
366                env,
367                &[
368                    28, 252, 217, 74, 171, 194, 60, 172, 212, 204, 171, 34, 232, 125, 35, 3, 32,
369                    13, 9, 176, 48, 66, 178, 4, 127, 3, 210, 224, 222, 245, 126, 133,
370                ],
371            ),
372        ), // 25
373        U256::from_be_bytes(
374            env,
375            &Bytes::from_array(
376                env,
377                &[
378                    16, 43, 220, 129, 172, 191, 140, 82, 211, 88, 113, 162, 31, 242, 117, 22, 221,
379                    190, 110, 49, 239, 81, 116, 12, 73, 189, 46, 226, 177, 152, 12, 33,
380                ],
381            ),
382        ), // 26
383        U256::from_be_bytes(
384            env,
385            &Bytes::from_array(
386                env,
387                &[
388                    47, 173, 132, 243, 136, 162, 99, 231, 20, 199, 179, 159, 176, 221, 36, 190, 97,
389                    88, 185, 226, 20, 128, 170, 58, 96, 119, 135, 7, 136, 159, 118, 173,
390                ],
391            ),
392        ), // 27
393        U256::from_be_bytes(
394            env,
395            &Bytes::from_array(
396                env,
397                &[
398                    42, 206, 148, 45, 48, 190, 203, 54, 159, 28, 253, 2, 91, 83, 148, 214, 238, 76,
399                    151, 245, 89, 48, 4, 138, 24, 251, 80, 17, 40, 200, 119, 119,
400                ],
401            ),
402        ), // 28
403        U256::from_be_bytes(
404            env,
405            &Bytes::from_array(
406                env,
407                &[
408                    9, 48, 254, 204, 191, 118, 42, 107, 35, 154, 206, 226, 195, 105, 222, 154, 49,
409                    189, 62, 35, 44, 144, 198, 74, 241, 65, 148, 92, 28, 65, 84, 99,
410                ],
411            ),
412        ), // 29
413        U256::from_be_bytes(
414            env,
415            &Bytes::from_array(
416                env,
417                &[
418                    26, 89, 226, 186, 85, 241, 232, 41, 188, 162, 198, 49, 106, 156, 205, 8, 1,
419                    125, 46, 155, 245, 50, 148, 44, 38, 216, 35, 101, 202, 96, 75, 88,
420                ],
421            ),
422        ), // 30
423        U256::from_be_bytes(
424            env,
425            &Bytes::from_array(
426                env,
427                &[
428                    26, 142, 132, 12, 132, 226, 251, 82, 18, 214, 31, 9, 173, 95, 145, 214, 167,
429                    10, 131, 7, 78, 148, 39, 114, 239, 116, 180, 45, 18, 161, 31, 80,
430                ],
431            ),
432        ), // 31
433        U256::from_be_bytes(
434            env,
435            &Bytes::from_array(
436                env,
437                &[
438                    19, 75, 80, 223, 2, 226, 204, 185, 139, 89, 175, 44, 44, 85, 215, 164, 31, 241,
439                    2, 104, 23, 48, 196, 43, 163, 100, 219, 255, 18, 113, 207, 98,
440                ],
441            ),
442        ), // 32 - Root (when levels=32)
443    ]
444}