1use sha2::{Digest, Sha256};
7
8use crate::abi::{
9 encode_arguments, AbiValue, ContractArtifact, FunctionArtifact, FunctionSelector, FunctionType,
10};
11use crate::constants::{self, domain_separator};
12use crate::grumpkin;
13use crate::tx::FunctionCall;
14use crate::types::{AztecAddress, ContractInstance, Fq, Fr, PublicKeys};
15use crate::Error;
16
17pub(crate) fn poseidon2_hash(inputs: &[Fr]) -> Fr {
24 use ark_bn254::Fr as ArkFr;
25 use taceo_poseidon2::bn254::t4::permutation;
26
27 const RATE: usize = 3;
28
29 let two_pow_64 = ArkFr::from(1u64 << 32) * ArkFr::from(1u64 << 32);
31 let iv = ArkFr::from(inputs.len() as u64) * two_pow_64;
32
33 let mut state: [ArkFr; 4] = [ArkFr::from(0u64), ArkFr::from(0u64), ArkFr::from(0u64), iv];
34 let mut cache = [ArkFr::from(0u64); RATE];
35 let mut cache_size = 0usize;
36
37 for input in inputs {
38 if cache_size == RATE {
39 for i in 0..RATE {
40 state[i] += cache[i];
41 }
42 cache = [ArkFr::from(0u64); RATE];
43 cache_size = 0;
44 state = permutation(&state);
45 }
46 cache[cache_size] = input.0;
47 cache_size += 1;
48 }
49
50 for i in 0..cache_size {
52 state[i] += cache[i];
53 }
54 state = permutation(&state);
55
56 Fr(state[0])
57}
58
59pub(crate) fn poseidon2_hash_bytes(bytes: &[u8]) -> Fr {
65 if bytes.is_empty() {
66 return poseidon2_hash(&[]);
67 }
68
69 let inputs = bytes
70 .chunks(31)
71 .map(|chunk| {
72 let mut field_bytes = [0u8; 32];
73 field_bytes[..chunk.len()].copy_from_slice(chunk);
74 field_bytes.reverse();
75 Fr::from(field_bytes)
76 })
77 .collect::<Vec<_>>();
78
79 poseidon2_hash(&inputs)
80}
81
82pub fn poseidon2_hash_with_separator(inputs: &[Fr], separator: u32) -> Fr {
86 poseidon2_hash_with_separator_field(inputs, Fr::from(u64::from(separator)))
87}
88
89pub fn poseidon2_hash_with_separator_field(inputs: &[Fr], separator: Fr) -> Fr {
91 let mut full_input = Vec::with_capacity(1 + inputs.len());
92 full_input.push(separator);
93 full_input.extend_from_slice(inputs);
94 poseidon2_hash(&full_input)
95}
96
97pub fn compute_secret_hash(secret: &Fr) -> Fr {
103 poseidon2_hash_with_separator(&[*secret], domain_separator::SECRET_HASH)
104}
105
106pub fn compute_var_args_hash(args: &[Fr]) -> Fr {
112 if args.is_empty() {
113 return Fr::zero();
114 }
115 poseidon2_hash_with_separator(args, domain_separator::FUNCTION_ARGS)
116}
117
118pub fn compute_inner_auth_wit_hash(args: &[Fr]) -> Fr {
125 poseidon2_hash_with_separator(args, domain_separator::AUTHWIT_INNER)
126}
127
128pub fn compute_outer_auth_wit_hash(
135 consumer: &AztecAddress,
136 chain_id: &Fr,
137 version: &Fr,
138 inner_hash: &Fr,
139) -> Fr {
140 poseidon2_hash_with_separator(
141 &[consumer.0, *chain_id, *version, *inner_hash],
142 domain_separator::AUTHWIT_OUTER,
143 )
144}
145
146pub fn abi_values_to_fields(args: &[AbiValue]) -> Vec<Fr> {
152 let mut fields = Vec::new();
153 for arg in args {
154 flatten_abi_value(arg, &mut fields);
155 }
156 fields
157}
158
159fn flatten_abi_value(value: &AbiValue, out: &mut Vec<Fr>) {
160 match value {
161 AbiValue::Field(f) => out.push(*f),
162 AbiValue::Boolean(b) => out.push(if *b { Fr::one() } else { Fr::zero() }),
163 AbiValue::Integer(i) => {
164 out.push(Fr::from(*i as u64));
167 }
168 AbiValue::Array(items) => {
169 for item in items {
170 flatten_abi_value(item, out);
171 }
172 }
173 AbiValue::String(s) => {
174 for byte in s.bytes() {
175 out.push(Fr::from(u64::from(byte)));
176 }
177 }
178 AbiValue::Struct(map) => {
179 for value in map.values() {
181 flatten_abi_value(value, out);
182 }
183 }
184 AbiValue::Tuple(items) => {
185 for item in items {
186 flatten_abi_value(item, out);
187 }
188 }
189 }
190}
191
192pub fn compute_inner_auth_wit_hash_from_action(caller: &AztecAddress, call: &FunctionCall) -> Fr {
198 let args_as_fields = abi_values_to_fields(&call.args);
199 let args_hash = compute_var_args_hash(&args_as_fields);
200 compute_inner_auth_wit_hash(&[caller.0, call.selector.to_field(), args_hash])
201}
202
203#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
208#[serde(rename_all = "camelCase")]
209pub struct ChainInfo {
210 pub chain_id: Fr,
212 pub version: Fr,
214}
215
216#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
221#[serde(tag = "type", rename_all = "snake_case")]
222pub enum MessageHashOrIntent {
223 Hash {
225 hash: Fr,
227 },
228 Intent {
230 caller: AztecAddress,
232 call: FunctionCall,
234 },
235 InnerHash {
240 consumer: AztecAddress,
242 inner_hash: Fr,
244 },
245}
246
247pub fn compute_auth_wit_message_hash(intent: &MessageHashOrIntent, chain_info: &ChainInfo) -> Fr {
260 match intent {
261 MessageHashOrIntent::Hash { hash } => *hash,
262 MessageHashOrIntent::Intent { caller, call } => {
263 let inner_hash = compute_inner_auth_wit_hash_from_action(caller, call);
264 compute_outer_auth_wit_hash(
265 &call.to,
266 &chain_info.chain_id,
267 &chain_info.version,
268 &inner_hash,
269 )
270 }
271 MessageHashOrIntent::InnerHash {
272 consumer,
273 inner_hash,
274 } => compute_outer_auth_wit_hash(
275 consumer,
276 &chain_info.chain_id,
277 &chain_info.version,
278 inner_hash,
279 ),
280 }
281}
282
283pub fn compute_initialization_hash(
293 init_fn: Option<&FunctionArtifact>,
294 args: &[AbiValue],
295) -> Result<Fr, Error> {
296 match init_fn {
297 None => Ok(Fr::zero()),
298 Some(func) => {
299 let selector = func.selector.unwrap_or_else(|| {
300 FunctionSelector::from_name_and_parameters(&func.name, &func.parameters)
301 });
302 let encoded_args = encode_arguments(func, args)?;
303 let args_hash = compute_var_args_hash(&encoded_args);
304 Ok(poseidon2_hash_with_separator(
305 &[selector.to_field(), args_hash],
306 domain_separator::INITIALIZER,
307 ))
308 }
309 }
310}
311
312pub fn compute_initialization_hash_from_encoded(selector: Fr, encoded_args: &[Fr]) -> Fr {
314 let args_hash = compute_var_args_hash(encoded_args);
315 poseidon2_hash_with_separator(&[selector, args_hash], domain_separator::INITIALIZER)
316}
317
318pub fn compute_private_functions_root(private_functions: &mut [(FunctionSelector, Fr)]) -> Fr {
327 let tree_height = constants::FUNCTION_TREE_HEIGHT;
328 let num_leaves = 1usize << tree_height; private_functions.sort_by_key(|(sel, _)| u32::from_be_bytes(sel.0));
332
333 let zero_leaf = poseidon2_hash(&[Fr::zero(), Fr::zero()]);
335 let mut leaves: Vec<Fr> = Vec::with_capacity(num_leaves);
336 for (sel, vk_hash) in private_functions.iter() {
337 let leaf = poseidon2_hash_with_separator(
338 &[sel.to_field(), *vk_hash],
339 domain_separator::PRIVATE_FUNCTION_LEAF,
340 );
341 leaves.push(leaf);
342 }
343 leaves.resize(num_leaves, zero_leaf);
345
346 poseidon_merkle_root(&leaves)
348}
349
350fn poseidon_merkle_root(leaves: &[Fr]) -> Fr {
352 if leaves.is_empty() {
353 return Fr::zero();
354 }
355 if leaves.len() == 1 {
356 return leaves[0];
357 }
358
359 let mut current = leaves.to_vec();
360 while current.len() > 1 {
361 let mut next = Vec::with_capacity(current.len().div_ceil(2));
362 for chunk in current.chunks(2) {
363 let left = chunk[0];
364 let right = if chunk.len() > 1 {
365 chunk[1]
366 } else {
367 Fr::zero()
368 };
369 next.push(poseidon2_hash(&[left, right]));
370 }
371 current = next;
372 }
373 current[0]
374}
375
376fn sha256_merkle_root(leaves: &[Fr]) -> Fr {
377 if leaves.is_empty() {
378 return Fr::zero();
379 }
380 if leaves.len() == 1 {
381 return leaves[0];
382 }
383
384 let mut current = leaves.to_vec();
385 while current.len() > 1 {
386 let mut next = Vec::with_capacity(current.len().div_ceil(2));
387 for chunk in current.chunks(2) {
388 let left = chunk[0].to_be_bytes();
389 let right = chunk.get(1).unwrap_or(&Fr::zero()).to_be_bytes();
390 next.push(sha256_to_field(
391 &[left.as_slice(), right.as_slice()].concat(),
392 ));
393 }
394 current = next;
395 }
396 current[0]
397}
398
399fn sha256_to_field(data: &[u8]) -> Fr {
402 let hash = Sha256::digest(data);
403 Fr::from(<[u8; 32]>::try_from(hash.as_slice()).expect("SHA256 is 32 bytes"))
404}
405
406pub fn compute_artifact_hash(artifact: &ContractArtifact) -> Fr {
411 let private_fn_tree_root = compute_artifact_function_tree_root(artifact, false);
412 let unconstrained_fn_tree_root = compute_artifact_function_tree_root(artifact, true);
413 let metadata_hash = compute_artifact_metadata_hash(artifact);
414
415 let mut data = Vec::new();
416 data.push(1u8);
417 data.extend_from_slice(&private_fn_tree_root.to_be_bytes());
418 data.extend_from_slice(&unconstrained_fn_tree_root.to_be_bytes());
419 data.extend_from_slice(&metadata_hash.to_be_bytes());
420 sha256_to_field(&data)
421}
422
423fn canonical_json_string(value: &serde_json::Value) -> String {
424 match value {
425 serde_json::Value::Null => "null".to_owned(),
426 serde_json::Value::Bool(boolean) => boolean.to_string(),
427 serde_json::Value::Number(number) => number.to_string(),
428 serde_json::Value::String(string) => {
429 serde_json::to_string(string).unwrap_or_else(|_| "\"\"".to_owned())
430 }
431 serde_json::Value::Array(items) => {
432 let inner = items
433 .iter()
434 .map(canonical_json_string)
435 .collect::<Vec<_>>()
436 .join(",");
437 format!("[{inner}]")
438 }
439 serde_json::Value::Object(map) => {
440 let mut entries = map.iter().collect::<Vec<_>>();
441 entries.sort_by(|(left, _), (right, _)| left.cmp(right));
442 let inner = entries
443 .into_iter()
444 .map(|(key, value)| {
445 let key = serde_json::to_string(key).unwrap_or_else(|_| "\"\"".to_owned());
446 format!("{key}:{}", canonical_json_string(value))
447 })
448 .collect::<Vec<_>>()
449 .join(",");
450 format!("{{{inner}}}")
451 }
452 }
453}
454
455fn decode_artifact_bytes(encoded: &str) -> Vec<u8> {
456 if let Some(hex) = encoded.strip_prefix("0x") {
457 return hex::decode(hex).unwrap_or_else(|_| encoded.as_bytes().to_vec());
458 }
459
460 use base64::Engine;
461 base64::engine::general_purpose::STANDARD
462 .decode(encoded)
463 .unwrap_or_else(|_| encoded.as_bytes().to_vec())
464}
465
466fn compute_artifact_function_tree_root(artifact: &ContractArtifact, unconstrained: bool) -> Fr {
468 let functions: Vec<&FunctionArtifact> = artifact
469 .functions
470 .iter()
471 .filter(|f| {
472 if unconstrained {
473 f.function_type == FunctionType::Utility || f.is_unconstrained == Some(true)
474 } else {
475 f.function_type == FunctionType::Private
476 }
477 })
478 .collect();
479
480 if functions.is_empty() {
481 return Fr::zero();
482 }
483
484 let leaves: Vec<Fr> = functions
485 .iter()
486 .map(|func| {
487 let selector = func.selector.unwrap_or_else(|| {
488 FunctionSelector::from_name_and_parameters(&func.name, &func.parameters)
489 });
490 let metadata_hash = compute_function_metadata_hash(func);
491 let bytecode_hash = compute_function_bytecode_hash(func);
492
493 let mut leaf_data = Vec::new();
494 leaf_data.push(1u8);
495 leaf_data.extend_from_slice(&selector.0);
496 leaf_data.extend_from_slice(&metadata_hash.to_be_bytes());
497 leaf_data.extend_from_slice(&bytecode_hash.to_be_bytes());
498 sha256_to_field(&leaf_data)
499 })
500 .collect();
501
502 let height = if leaves.len() <= 1 {
503 0
504 } else {
505 (leaves.len() as f64).log2().ceil() as usize
506 };
507 let num_leaves = 1usize << height;
508 let mut padded = leaves;
509 padded.resize(num_leaves.max(1), Fr::zero());
510 sha256_merkle_root(&padded)
511}
512
513fn compute_function_metadata_hash(func: &FunctionArtifact) -> Fr {
515 let metadata = serde_json::to_value(&func.return_types).unwrap_or(serde_json::Value::Null);
516 let serialized = canonical_json_string(&metadata);
517 sha256_to_field(serialized.as_bytes())
518}
519
520fn compute_function_bytecode_hash(func: &FunctionArtifact) -> Fr {
522 match &func.bytecode {
523 Some(bc) if !bc.is_empty() => sha256_to_field(&decode_artifact_bytes(bc)),
524 _ => Fr::zero(),
525 }
526}
527
528fn compute_artifact_metadata_hash(artifact: &ContractArtifact) -> Fr {
530 let mut metadata = serde_json::Map::new();
531 metadata.insert(
532 "name".to_owned(),
533 serde_json::Value::String(artifact.name.clone()),
534 );
535 if let Some(outputs) = &artifact.outputs {
536 metadata.insert("outputs".to_owned(), outputs.clone());
537 }
538 let serialized = canonical_json_string(&serde_json::Value::Object(metadata));
539 sha256_to_field(serialized.as_bytes())
540}
541
542pub fn compute_public_bytecode_commitment(packed_bytecode: &[u8]) -> Fr {
546 let fields = crate::abi::buffer_as_fields(
547 packed_bytecode,
548 constants::MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
549 )
550 .expect("packed bytecode exceeds maximum field count");
551 let byte_length = fields[0].to_usize() as u64;
552 let length_in_fields = byte_length.div_ceil(31) as usize;
553
554 let separator = Fr::from(u64::from(domain_separator::PUBLIC_BYTECODE) + (byte_length << 32));
555 poseidon2_hash_with_separator_field(&fields[1..1 + length_in_fields], separator)
556}
557
558pub fn compute_contract_class_id(
562 artifact_hash: Fr,
563 private_functions_root: Fr,
564 public_bytecode_commitment: Fr,
565) -> Fr {
566 poseidon2_hash_with_separator(
567 &[
568 artifact_hash,
569 private_functions_root,
570 public_bytecode_commitment,
571 ],
572 domain_separator::CONTRACT_CLASS_ID,
573 )
574}
575
576pub fn compute_contract_class_id_from_artifact(artifact: &ContractArtifact) -> Result<Fr, Error> {
578 let artifact_hash = compute_artifact_hash(artifact);
579 let private_fns_root = compute_private_functions_root_from_artifact(artifact)?;
580 let public_bytecode = extract_packed_public_bytecode(artifact);
581 let public_bytecode_commitment = compute_public_bytecode_commitment(&public_bytecode);
582 Ok(compute_contract_class_id(
583 artifact_hash,
584 private_fns_root,
585 public_bytecode_commitment,
586 ))
587}
588
589pub fn compute_private_functions_root_from_artifact(
591 artifact: &ContractArtifact,
592) -> Result<Fr, Error> {
593 let mut private_fns: Vec<(FunctionSelector, Fr)> = artifact
594 .functions
595 .iter()
596 .filter(|f| f.function_type == FunctionType::Private)
597 .map(|f| {
598 let selector = f.selector.unwrap_or_else(|| {
599 FunctionSelector::from_name_and_parameters(&f.name, &f.parameters)
600 });
601 let vk_hash = f.verification_key_hash.unwrap_or(Fr::zero());
602 (selector, vk_hash)
603 })
604 .collect();
605
606 Ok(compute_private_functions_root(&mut private_fns))
607}
608
609fn extract_packed_public_bytecode(artifact: &ContractArtifact) -> Vec<u8> {
611 let mut bytecode = Vec::new();
612 for func in &artifact.functions {
613 if func.function_type == FunctionType::Public {
614 if let Some(ref bc) = func.bytecode {
615 bytecode.extend_from_slice(&decode_artifact_bytes(bc));
616 }
617 }
618 }
619 bytecode
620}
621
622pub fn compute_salted_initialization_hash(
630 salt: Fr,
631 initialization_hash: Fr,
632 deployer: AztecAddress,
633) -> Fr {
634 poseidon2_hash_with_separator(
635 &[salt, initialization_hash, deployer.0],
636 domain_separator::PARTIAL_ADDRESS,
637 )
638}
639
640pub fn compute_partial_address(
644 original_contract_class_id: Fr,
645 salted_initialization_hash: Fr,
646) -> Fr {
647 poseidon2_hash_with_separator(
648 &[original_contract_class_id, salted_initialization_hash],
649 domain_separator::PARTIAL_ADDRESS,
650 )
651}
652
653pub fn compute_address(
660 public_keys: &PublicKeys,
661 partial_address: &Fr,
662) -> Result<AztecAddress, Error> {
663 let public_keys_hash = public_keys.hash();
664 let preaddress = poseidon2_hash_with_separator(
665 &[public_keys_hash, *partial_address],
666 domain_separator::CONTRACT_ADDRESS_V1,
667 );
668
669 let preaddress_fq = Fq::from_be_bytes_mod_order(&preaddress.to_be_bytes());
672
673 let g = grumpkin::generator();
674 let preaddress_point = grumpkin::scalar_mul(&preaddress_fq, &g);
675
676 let ivpk_m = &public_keys.master_incoming_viewing_public_key;
677 let address_point = if ivpk_m.is_zero() {
679 preaddress_point
680 } else {
681 grumpkin::point_add(&preaddress_point, ivpk_m)
682 };
683
684 if address_point.is_infinite {
685 return Err(Error::InvalidData(
686 "address derivation resulted in point at infinity".to_owned(),
687 ));
688 }
689
690 Ok(AztecAddress(address_point.x))
691}
692
693pub fn compute_contract_address_from_instance(
702 instance: &ContractInstance,
703) -> Result<AztecAddress, Error> {
704 let salted_init_hash = compute_salted_initialization_hash(
705 instance.salt,
706 instance.initialization_hash,
707 instance.deployer,
708 );
709 let partial_address =
710 compute_partial_address(instance.original_contract_class_id, salted_init_hash);
711
712 compute_address(&instance.public_keys, &partial_address)
713}
714
715#[cfg(test)]
716#[allow(clippy::expect_used, clippy::panic)]
717mod tests {
718 use super::*;
719 use crate::abi::{buffer_as_fields, FunctionSelector, FunctionType};
720
721 #[test]
722 fn var_args_hash_empty_returns_zero() {
723 assert_eq!(compute_var_args_hash(&[]), Fr::zero());
724 }
725
726 #[test]
727 fn poseidon2_hash_known_vector() {
728 let result = poseidon2_hash(&[Fr::from(1u64)]);
731 let expected =
732 Fr::from_hex("0x168758332d5b3e2d13be8048c8011b454590e06c44bce7f702f09103eef5a373")
733 .expect("valid hex");
734 assert_eq!(
735 result, expected,
736 "Poseidon2 hash of [1] must match barretenberg test vector"
737 );
738 }
739
740 #[test]
741 fn poseidon2_hash_with_separator_prepends_separator() {
742 let a = Fr::from(10u64);
744 let b = Fr::from(20u64);
745 let sep = 42u32;
746
747 let result = poseidon2_hash_with_separator(&[a, b], sep);
748 let manual = poseidon2_hash(&[Fr::from(u64::from(sep)), a, b]);
749 assert_eq!(result, manual);
750 }
751
752 #[test]
753 fn secret_hash_uses_correct_separator() {
754 let secret = Fr::from(42u64);
755 let result = compute_secret_hash(&secret);
756 let expected = poseidon2_hash_with_separator(&[secret], domain_separator::SECRET_HASH);
757 assert_eq!(result, expected);
758 assert!(!result.is_zero());
760 }
761
762 #[test]
763 fn secret_hash_is_deterministic() {
764 let secret = Fr::from(12345u64);
765 let h1 = compute_secret_hash(&secret);
766 let h2 = compute_secret_hash(&secret);
767 assert_eq!(h1, h2);
768 }
769
770 #[test]
771 fn var_args_hash_single_element() {
772 let result = compute_var_args_hash(&[Fr::from(42u64)]);
773 let expected =
775 poseidon2_hash_with_separator(&[Fr::from(42u64)], domain_separator::FUNCTION_ARGS);
776 assert_eq!(result, expected);
777 }
778
779 #[test]
780 fn inner_auth_wit_hash_uses_correct_separator() {
781 let args = [Fr::from(1u64), Fr::from(2u64), Fr::from(3u64)];
782 let result = compute_inner_auth_wit_hash(&args);
783 let expected = poseidon2_hash_with_separator(&args, domain_separator::AUTHWIT_INNER);
784 assert_eq!(result, expected);
785 }
786
787 #[test]
788 fn outer_auth_wit_hash_uses_correct_separator() {
789 let consumer = AztecAddress(Fr::from(100u64));
790 let chain_id = Fr::from(31337u64);
791 let version = Fr::from(1u64);
792 let inner_hash = Fr::from(999u64);
793
794 let result = compute_outer_auth_wit_hash(&consumer, &chain_id, &version, &inner_hash);
795 let expected = poseidon2_hash_with_separator(
796 &[consumer.0, chain_id, version, inner_hash],
797 domain_separator::AUTHWIT_OUTER,
798 );
799 assert_eq!(result, expected);
800 }
801
802 #[test]
803 fn inner_auth_wit_hash_from_action() {
804 let caller = AztecAddress(Fr::from(1u64));
805 let call = FunctionCall {
806 to: AztecAddress(Fr::from(2u64)),
807 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
808 args: vec![AbiValue::Field(Fr::from(100u64))],
809 function_type: FunctionType::Private,
810 is_static: false,
811 };
812
813 let result = compute_inner_auth_wit_hash_from_action(&caller, &call);
814
815 let args_hash = compute_var_args_hash(&[Fr::from(100u64)]);
817 let selector_field = call.selector.to_field();
818 let expected = compute_inner_auth_wit_hash(&[caller.0, selector_field, args_hash]);
819 assert_eq!(result, expected);
820 }
821
822 #[test]
823 fn auth_wit_message_hash_passthrough() {
824 let hash = Fr::from(42u64);
825 let chain_info = ChainInfo {
826 chain_id: Fr::from(31337u64),
827 version: Fr::from(1u64),
828 };
829 let result =
830 compute_auth_wit_message_hash(&MessageHashOrIntent::Hash { hash }, &chain_info);
831 assert_eq!(result, hash);
832 }
833
834 #[test]
835 fn auth_wit_message_hash_from_intent() {
836 let caller = AztecAddress(Fr::from(10u64));
837 let consumer = AztecAddress(Fr::from(20u64));
838 let call = FunctionCall {
839 to: consumer,
840 selector: FunctionSelector::from_hex("0x11223344").expect("valid"),
841 args: vec![],
842 function_type: FunctionType::Private,
843 is_static: false,
844 };
845 let chain_info = ChainInfo {
846 chain_id: Fr::from(31337u64),
847 version: Fr::from(1u64),
848 };
849
850 let result = compute_auth_wit_message_hash(
851 &MessageHashOrIntent::Intent {
852 caller,
853 call: call.clone(),
854 },
855 &chain_info,
856 );
857
858 let inner = compute_inner_auth_wit_hash_from_action(&caller, &call);
860 let expected = compute_outer_auth_wit_hash(
861 &consumer,
862 &chain_info.chain_id,
863 &chain_info.version,
864 &inner,
865 );
866 assert_eq!(result, expected);
867 }
868
869 #[test]
870 fn auth_wit_message_hash_from_inner_hash() {
871 let consumer = AztecAddress(Fr::from(20u64));
872 let inner_hash = Fr::from(999u64);
873 let chain_info = ChainInfo {
874 chain_id: Fr::from(31337u64),
875 version: Fr::from(1u64),
876 };
877
878 let result = compute_auth_wit_message_hash(
879 &MessageHashOrIntent::InnerHash {
880 consumer,
881 inner_hash,
882 },
883 &chain_info,
884 );
885
886 let expected = compute_outer_auth_wit_hash(
887 &consumer,
888 &chain_info.chain_id,
889 &chain_info.version,
890 &inner_hash,
891 );
892 assert_eq!(result, expected);
893 }
894
895 #[test]
896 fn abi_values_to_fields_basic_types() {
897 let values = vec![
898 AbiValue::Field(Fr::from(1u64)),
899 AbiValue::Boolean(true),
900 AbiValue::Boolean(false),
901 AbiValue::Integer(42),
902 ];
903 let fields = abi_values_to_fields(&values);
904 assert_eq!(fields.len(), 4);
905 assert_eq!(fields[0], Fr::from(1u64));
906 assert_eq!(fields[1], Fr::one());
907 assert_eq!(fields[2], Fr::zero());
908 assert_eq!(fields[3], Fr::from(42u64));
909 }
910
911 #[test]
912 fn abi_values_to_fields_nested() {
913 let values = vec![AbiValue::Array(vec![
914 AbiValue::Field(Fr::from(1u64)),
915 AbiValue::Field(Fr::from(2u64)),
916 ])];
917 let fields = abi_values_to_fields(&values);
918 assert_eq!(fields.len(), 2);
919 assert_eq!(fields[0], Fr::from(1u64));
920 assert_eq!(fields[1], Fr::from(2u64));
921 }
922
923 #[test]
924 fn message_hash_or_intent_serde_roundtrip() {
925 let variants = vec![
926 MessageHashOrIntent::Hash {
927 hash: Fr::from(42u64),
928 },
929 MessageHashOrIntent::Intent {
930 caller: AztecAddress(Fr::from(1u64)),
931 call: FunctionCall {
932 to: AztecAddress(Fr::from(2u64)),
933 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
934 args: vec![],
935 function_type: FunctionType::Private,
936 is_static: false,
937 },
938 },
939 MessageHashOrIntent::InnerHash {
940 consumer: AztecAddress(Fr::from(3u64)),
941 inner_hash: Fr::from(999u64),
942 },
943 ];
944
945 for variant in variants {
946 let json = serde_json::to_string(&variant).expect("serialize");
947 let decoded: MessageHashOrIntent = serde_json::from_str(&json).expect("deserialize");
948 assert_eq!(decoded, variant);
949 }
950 }
951
952 #[test]
953 fn chain_info_serde_roundtrip() {
954 let info = ChainInfo {
955 chain_id: Fr::from(31337u64),
956 version: Fr::from(1u64),
957 };
958 let json = serde_json::to_string(&info).expect("serialize");
959 let decoded: ChainInfo = serde_json::from_str(&json).expect("deserialize");
960 assert_eq!(decoded, info);
961 }
962
963 #[test]
966 fn initialization_hash_no_constructor_returns_zero() {
967 let result = compute_initialization_hash(None, &[]).expect("no constructor");
968 assert_eq!(result, Fr::zero());
969 }
970
971 #[test]
972 fn initialization_hash_with_constructor() {
973 use crate::abi::AbiParameter;
974 let func = FunctionArtifact {
975 name: "constructor".to_owned(),
976 function_type: FunctionType::Private,
977 is_initializer: true,
978 is_static: false,
979 is_only_self: None,
980 parameters: vec![AbiParameter {
981 name: "admin".to_owned(),
982 typ: crate::abi::AbiType::Field,
983 visibility: None,
984 }],
985 return_types: vec![],
986 error_types: None,
987 selector: Some(FunctionSelector::from_hex("0xe5fb6c81").expect("valid")),
988 bytecode: None,
989 verification_key_hash: None,
990 verification_key: None,
991 custom_attributes: None,
992 is_unconstrained: None,
993 debug_symbols: None,
994 };
995 let args = vec![AbiValue::Field(Fr::from(42u64))];
996 let result = compute_initialization_hash(Some(&func), &args).expect("init hash");
997 assert_ne!(result, Fr::zero());
998 }
999
1000 #[test]
1001 fn initialization_hash_from_encoded() {
1002 let selector = Fr::from(12345u64);
1003 let args = vec![Fr::from(1u64), Fr::from(2u64)];
1004 let result = compute_initialization_hash_from_encoded(selector, &args);
1005 let args_hash = compute_var_args_hash(&args);
1006 let expected =
1007 poseidon2_hash_with_separator(&[selector, args_hash], domain_separator::INITIALIZER);
1008 assert_eq!(result, expected);
1009 }
1010
1011 #[test]
1012 fn private_functions_root_empty() {
1013 let root = compute_private_functions_root(&mut []);
1014 assert_ne!(root, Fr::zero()); }
1017
1018 #[test]
1019 fn contract_class_id_deterministic() {
1020 let artifact_hash = Fr::from(1u64);
1021 let root = Fr::from(2u64);
1022 let commitment = Fr::from(3u64);
1023 let id1 = compute_contract_class_id(artifact_hash, root, commitment);
1024 let id2 = compute_contract_class_id(artifact_hash, root, commitment);
1025 assert_eq!(id1, id2);
1026 assert_ne!(id1, Fr::zero());
1027 }
1028
1029 #[test]
1030 fn buffer_as_fields_basic() {
1031 let data = vec![0u8; 31];
1032 let fields = buffer_as_fields(&data, 100).expect("encode");
1033 assert_eq!(fields.len(), 100);
1036 assert_eq!(fields[0], Fr::from(31u64)); }
1038
1039 #[test]
1040 fn buffer_as_fields_multiple_chunks() {
1041 let data = vec![0xffu8; 62]; let fields = buffer_as_fields(&data, 100).expect("encode");
1043 assert_eq!(fields.len(), 100);
1044 assert_eq!(fields[0], Fr::from(62u64)); }
1046
1047 #[test]
1048 fn public_bytecode_commitment_empty() {
1049 let result = compute_public_bytecode_commitment(&[]);
1050 assert_ne!(result, Fr::zero());
1052 }
1053
1054 #[test]
1055 fn public_bytecode_commitment_non_empty() {
1056 let data = vec![0x01u8; 100];
1057 let result = compute_public_bytecode_commitment(&data);
1058 assert_ne!(result, Fr::zero());
1059 }
1060
1061 #[test]
1062 fn salted_initialization_hash_uses_partial_address_separator() {
1063 let salt = Fr::from(1u64);
1064 let init_hash = Fr::from(2u64);
1065 let deployer = AztecAddress(Fr::from(3u64));
1066 let result = compute_salted_initialization_hash(salt, init_hash, deployer);
1067 let expected = poseidon2_hash_with_separator(
1068 &[salt, init_hash, deployer.0],
1069 domain_separator::PARTIAL_ADDRESS,
1070 );
1071 assert_eq!(result, expected);
1072 }
1073
1074 #[test]
1075 fn partial_address_uses_correct_separator() {
1076 let class_id = Fr::from(100u64);
1077 let salted = Fr::from(200u64);
1078 let result = compute_partial_address(class_id, salted);
1079 let expected =
1080 poseidon2_hash_with_separator(&[class_id, salted], domain_separator::PARTIAL_ADDRESS);
1081 assert_eq!(result, expected);
1082 }
1083
1084 #[test]
1085 fn contract_address_from_instance_default_keys() {
1086 use crate::types::{ContractInstance, PublicKeys};
1087 let instance = ContractInstance {
1088 version: 1,
1089 salt: Fr::from(42u64),
1090 deployer: AztecAddress(Fr::zero()),
1091 current_contract_class_id: Fr::from(100u64),
1092 original_contract_class_id: Fr::from(100u64),
1093 initialization_hash: Fr::zero(),
1094 public_keys: PublicKeys::default(),
1095 };
1096 let address =
1097 compute_contract_address_from_instance(&instance).expect("address derivation");
1098 assert_ne!(address.0, Fr::zero());
1099 }
1100
1101 #[test]
1102 fn contract_address_is_deterministic() {
1103 use crate::types::{ContractInstance, PublicKeys};
1104 let instance = ContractInstance {
1105 version: 1,
1106 salt: Fr::from(99u64),
1107 deployer: AztecAddress(Fr::from(1u64)),
1108 current_contract_class_id: Fr::from(200u64),
1109 original_contract_class_id: Fr::from(200u64),
1110 initialization_hash: Fr::from(300u64),
1111 public_keys: PublicKeys::default(),
1112 };
1113 let addr1 = compute_contract_address_from_instance(&instance).expect("addr1");
1114 let addr2 = compute_contract_address_from_instance(&instance).expect("addr2");
1115 assert_eq!(addr1, addr2);
1116 }
1117
1118 #[test]
1119 fn artifact_hash_deterministic() {
1120 let artifact = ContractArtifact {
1121 name: "Test".to_owned(),
1122 functions: vec![],
1123 outputs: None,
1124 file_map: None,
1125 };
1126 let h1 = compute_artifact_hash(&artifact);
1127 let h2 = compute_artifact_hash(&artifact);
1128 assert_eq!(h1, h2);
1129 }
1130
1131 #[test]
1132 fn class_id_from_artifact_no_functions() {
1133 let artifact = ContractArtifact {
1134 name: "Empty".to_owned(),
1135 functions: vec![],
1136 outputs: None,
1137 file_map: None,
1138 };
1139 let id = compute_contract_class_id_from_artifact(&artifact).expect("class id");
1140 assert_ne!(id, Fr::zero());
1141 }
1142}