1use crate::abi::AbiValue;
7use crate::constants::domain_separator;
8use crate::tx::FunctionCall;
9use crate::types::{AztecAddress, Fr};
10
11pub(crate) fn poseidon2_hash(inputs: &[Fr]) -> Fr {
18 use ark_bn254::Fr as ArkFr;
19 use taceo_poseidon2::bn254::t4::permutation;
20
21 const RATE: usize = 3;
22
23 let two_pow_64 = ArkFr::from(1u64 << 32) * ArkFr::from(1u64 << 32);
25 let iv = ArkFr::from(inputs.len() as u64) * two_pow_64;
26
27 let mut state: [ArkFr; 4] = [ArkFr::from(0u64), ArkFr::from(0u64), ArkFr::from(0u64), iv];
28 let mut cache = [ArkFr::from(0u64); RATE];
29 let mut cache_size = 0usize;
30
31 for input in inputs {
32 if cache_size == RATE {
33 for i in 0..RATE {
34 state[i] += cache[i];
35 }
36 cache = [ArkFr::from(0u64); RATE];
37 cache_size = 0;
38 state = permutation(&state);
39 }
40 cache[cache_size] = input.0;
41 cache_size += 1;
42 }
43
44 for i in 0..cache_size {
46 state[i] += cache[i];
47 }
48 state = permutation(&state);
49
50 Fr(state[0])
51}
52
53pub(crate) fn poseidon2_hash_bytes(bytes: &[u8]) -> Fr {
59 if bytes.is_empty() {
60 return poseidon2_hash(&[]);
61 }
62
63 let inputs = bytes
64 .chunks(31)
65 .map(|chunk| {
66 let mut field_bytes = [0u8; 32];
67 field_bytes[..chunk.len()].copy_from_slice(chunk);
68 field_bytes.reverse();
69 Fr::from(field_bytes)
70 })
71 .collect::<Vec<_>>();
72
73 poseidon2_hash(&inputs)
74}
75
76pub fn poseidon2_hash_with_separator(inputs: &[Fr], separator: u32) -> Fr {
80 let mut full_input = Vec::with_capacity(1 + inputs.len());
81 full_input.push(Fr::from(u64::from(separator)));
82 full_input.extend_from_slice(inputs);
83 poseidon2_hash(&full_input)
84}
85
86pub fn compute_var_args_hash(args: &[Fr]) -> Fr {
92 if args.is_empty() {
93 return Fr::zero();
94 }
95 poseidon2_hash_with_separator(args, domain_separator::FUNCTION_ARGS)
96}
97
98pub fn compute_inner_auth_wit_hash(args: &[Fr]) -> Fr {
105 poseidon2_hash_with_separator(args, domain_separator::AUTHWIT_INNER)
106}
107
108pub fn compute_outer_auth_wit_hash(
115 consumer: &AztecAddress,
116 chain_id: &Fr,
117 version: &Fr,
118 inner_hash: &Fr,
119) -> Fr {
120 poseidon2_hash_with_separator(
121 &[consumer.0, *chain_id, *version, *inner_hash],
122 domain_separator::AUTHWIT_OUTER,
123 )
124}
125
126pub fn abi_values_to_fields(args: &[AbiValue]) -> Vec<Fr> {
132 let mut fields = Vec::new();
133 for arg in args {
134 flatten_abi_value(arg, &mut fields);
135 }
136 fields
137}
138
139fn flatten_abi_value(value: &AbiValue, out: &mut Vec<Fr>) {
140 match value {
141 AbiValue::Field(f) => out.push(*f),
142 AbiValue::Boolean(b) => out.push(if *b { Fr::one() } else { Fr::zero() }),
143 AbiValue::Integer(i) => {
144 out.push(Fr::from(*i as u64));
147 }
148 AbiValue::Array(items) => {
149 for item in items {
150 flatten_abi_value(item, out);
151 }
152 }
153 AbiValue::String(s) => {
154 for byte in s.bytes() {
155 out.push(Fr::from(u64::from(byte)));
156 }
157 }
158 AbiValue::Struct(map) => {
159 for value in map.values() {
161 flatten_abi_value(value, out);
162 }
163 }
164 AbiValue::Tuple(items) => {
165 for item in items {
166 flatten_abi_value(item, out);
167 }
168 }
169 }
170}
171
172pub fn compute_inner_auth_wit_hash_from_action(caller: &AztecAddress, call: &FunctionCall) -> Fr {
178 let args_as_fields = abi_values_to_fields(&call.args);
179 let args_hash = compute_var_args_hash(&args_as_fields);
180 compute_inner_auth_wit_hash(&[caller.0, call.selector.to_field(), args_hash])
181}
182
183#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
188#[serde(rename_all = "camelCase")]
189pub struct ChainInfo {
190 pub chain_id: Fr,
192 pub version: Fr,
194}
195
196#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
201#[serde(tag = "type", rename_all = "snake_case")]
202pub enum MessageHashOrIntent {
203 Hash {
205 hash: Fr,
207 },
208 Intent {
210 caller: AztecAddress,
212 call: FunctionCall,
214 },
215 InnerHash {
220 consumer: AztecAddress,
222 inner_hash: Fr,
224 },
225}
226
227pub fn compute_auth_wit_message_hash(intent: &MessageHashOrIntent, chain_info: &ChainInfo) -> Fr {
239 match intent {
240 MessageHashOrIntent::Hash { hash } => *hash,
241 MessageHashOrIntent::Intent { caller, call } => {
242 let inner_hash = compute_inner_auth_wit_hash_from_action(caller, call);
243 compute_outer_auth_wit_hash(
244 &call.to,
245 &chain_info.chain_id,
246 &chain_info.version,
247 &inner_hash,
248 )
249 }
250 MessageHashOrIntent::InnerHash {
251 consumer,
252 inner_hash,
253 } => compute_outer_auth_wit_hash(
254 consumer,
255 &chain_info.chain_id,
256 &chain_info.version,
257 inner_hash,
258 ),
259 }
260}
261
262#[cfg(test)]
263#[allow(clippy::expect_used, clippy::panic)]
264mod tests {
265 use super::*;
266 use crate::abi::{FunctionSelector, FunctionType};
267
268 #[test]
269 fn var_args_hash_empty_returns_zero() {
270 assert_eq!(compute_var_args_hash(&[]), Fr::zero());
271 }
272
273 #[test]
274 fn poseidon2_hash_known_vector() {
275 let result = poseidon2_hash(&[Fr::from(1u64)]);
278 let expected =
279 Fr::from_hex("0x168758332d5b3e2d13be8048c8011b454590e06c44bce7f702f09103eef5a373")
280 .expect("valid hex");
281 assert_eq!(
282 result, expected,
283 "Poseidon2 hash of [1] must match barretenberg test vector"
284 );
285 }
286
287 #[test]
288 fn poseidon2_hash_with_separator_prepends_separator() {
289 let a = Fr::from(10u64);
291 let b = Fr::from(20u64);
292 let sep = 42u32;
293
294 let result = poseidon2_hash_with_separator(&[a, b], sep);
295 let manual = poseidon2_hash(&[Fr::from(u64::from(sep)), a, b]);
296 assert_eq!(result, manual);
297 }
298
299 #[test]
300 fn var_args_hash_single_element() {
301 let result = compute_var_args_hash(&[Fr::from(42u64)]);
302 let expected =
304 poseidon2_hash_with_separator(&[Fr::from(42u64)], domain_separator::FUNCTION_ARGS);
305 assert_eq!(result, expected);
306 }
307
308 #[test]
309 fn inner_auth_wit_hash_uses_correct_separator() {
310 let args = [Fr::from(1u64), Fr::from(2u64), Fr::from(3u64)];
311 let result = compute_inner_auth_wit_hash(&args);
312 let expected = poseidon2_hash_with_separator(&args, domain_separator::AUTHWIT_INNER);
313 assert_eq!(result, expected);
314 }
315
316 #[test]
317 fn outer_auth_wit_hash_uses_correct_separator() {
318 let consumer = AztecAddress(Fr::from(100u64));
319 let chain_id = Fr::from(31337u64);
320 let version = Fr::from(1u64);
321 let inner_hash = Fr::from(999u64);
322
323 let result = compute_outer_auth_wit_hash(&consumer, &chain_id, &version, &inner_hash);
324 let expected = poseidon2_hash_with_separator(
325 &[consumer.0, chain_id, version, inner_hash],
326 domain_separator::AUTHWIT_OUTER,
327 );
328 assert_eq!(result, expected);
329 }
330
331 #[test]
332 fn inner_auth_wit_hash_from_action() {
333 let caller = AztecAddress(Fr::from(1u64));
334 let call = FunctionCall {
335 to: AztecAddress(Fr::from(2u64)),
336 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
337 args: vec![AbiValue::Field(Fr::from(100u64))],
338 function_type: FunctionType::Private,
339 is_static: false,
340 };
341
342 let result = compute_inner_auth_wit_hash_from_action(&caller, &call);
343
344 let args_hash = compute_var_args_hash(&[Fr::from(100u64)]);
346 let selector_field = call.selector.to_field();
347 let expected = compute_inner_auth_wit_hash(&[caller.0, selector_field, args_hash]);
348 assert_eq!(result, expected);
349 }
350
351 #[test]
352 fn auth_wit_message_hash_passthrough() {
353 let hash = Fr::from(42u64);
354 let chain_info = ChainInfo {
355 chain_id: Fr::from(31337u64),
356 version: Fr::from(1u64),
357 };
358 let result =
359 compute_auth_wit_message_hash(&MessageHashOrIntent::Hash { hash }, &chain_info);
360 assert_eq!(result, hash);
361 }
362
363 #[test]
364 fn auth_wit_message_hash_from_intent() {
365 let caller = AztecAddress(Fr::from(10u64));
366 let consumer = AztecAddress(Fr::from(20u64));
367 let call = FunctionCall {
368 to: consumer,
369 selector: FunctionSelector::from_hex("0x11223344").expect("valid"),
370 args: vec![],
371 function_type: FunctionType::Private,
372 is_static: false,
373 };
374 let chain_info = ChainInfo {
375 chain_id: Fr::from(31337u64),
376 version: Fr::from(1u64),
377 };
378
379 let result = compute_auth_wit_message_hash(
380 &MessageHashOrIntent::Intent {
381 caller,
382 call: call.clone(),
383 },
384 &chain_info,
385 );
386
387 let inner = compute_inner_auth_wit_hash_from_action(&caller, &call);
389 let expected = compute_outer_auth_wit_hash(
390 &consumer,
391 &chain_info.chain_id,
392 &chain_info.version,
393 &inner,
394 );
395 assert_eq!(result, expected);
396 }
397
398 #[test]
399 fn auth_wit_message_hash_from_inner_hash() {
400 let consumer = AztecAddress(Fr::from(20u64));
401 let inner_hash = Fr::from(999u64);
402 let chain_info = ChainInfo {
403 chain_id: Fr::from(31337u64),
404 version: Fr::from(1u64),
405 };
406
407 let result = compute_auth_wit_message_hash(
408 &MessageHashOrIntent::InnerHash {
409 consumer,
410 inner_hash,
411 },
412 &chain_info,
413 );
414
415 let expected = compute_outer_auth_wit_hash(
416 &consumer,
417 &chain_info.chain_id,
418 &chain_info.version,
419 &inner_hash,
420 );
421 assert_eq!(result, expected);
422 }
423
424 #[test]
425 fn abi_values_to_fields_basic_types() {
426 let values = vec![
427 AbiValue::Field(Fr::from(1u64)),
428 AbiValue::Boolean(true),
429 AbiValue::Boolean(false),
430 AbiValue::Integer(42),
431 ];
432 let fields = abi_values_to_fields(&values);
433 assert_eq!(fields.len(), 4);
434 assert_eq!(fields[0], Fr::from(1u64));
435 assert_eq!(fields[1], Fr::one());
436 assert_eq!(fields[2], Fr::zero());
437 assert_eq!(fields[3], Fr::from(42u64));
438 }
439
440 #[test]
441 fn abi_values_to_fields_nested() {
442 let values = vec![AbiValue::Array(vec![
443 AbiValue::Field(Fr::from(1u64)),
444 AbiValue::Field(Fr::from(2u64)),
445 ])];
446 let fields = abi_values_to_fields(&values);
447 assert_eq!(fields.len(), 2);
448 assert_eq!(fields[0], Fr::from(1u64));
449 assert_eq!(fields[1], Fr::from(2u64));
450 }
451
452 #[test]
453 fn message_hash_or_intent_serde_roundtrip() {
454 let variants = vec![
455 MessageHashOrIntent::Hash {
456 hash: Fr::from(42u64),
457 },
458 MessageHashOrIntent::Intent {
459 caller: AztecAddress(Fr::from(1u64)),
460 call: FunctionCall {
461 to: AztecAddress(Fr::from(2u64)),
462 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
463 args: vec![],
464 function_type: FunctionType::Private,
465 is_static: false,
466 },
467 },
468 MessageHashOrIntent::InnerHash {
469 consumer: AztecAddress(Fr::from(3u64)),
470 inner_hash: Fr::from(999u64),
471 },
472 ];
473
474 for variant in variants {
475 let json = serde_json::to_string(&variant).expect("serialize");
476 let decoded: MessageHashOrIntent = serde_json::from_str(&json).expect("deserialize");
477 assert_eq!(decoded, variant);
478 }
479 }
480
481 #[test]
482 fn chain_info_serde_roundtrip() {
483 let info = ChainInfo {
484 chain_id: Fr::from(31337u64),
485 version: Fr::from(1u64),
486 };
487 let json = serde_json::to_string(&info).expect("serialize");
488 let decoded: ChainInfo = serde_json::from_str(&json).expect("deserialize");
489 assert_eq!(decoded, info);
490 }
491}