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, Fr};
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_var_args_hash(args: &[Fr]) -> Fr {
103 if args.is_empty() {
104 return Fr::zero();
105 }
106 poseidon2_hash_with_separator(args, domain_separator::FUNCTION_ARGS)
107}
108
109pub fn compute_inner_auth_wit_hash(args: &[Fr]) -> Fr {
116 poseidon2_hash_with_separator(args, domain_separator::AUTHWIT_INNER)
117}
118
119pub fn compute_outer_auth_wit_hash(
126 consumer: &AztecAddress,
127 chain_id: &Fr,
128 version: &Fr,
129 inner_hash: &Fr,
130) -> Fr {
131 poseidon2_hash_with_separator(
132 &[consumer.0, *chain_id, *version, *inner_hash],
133 domain_separator::AUTHWIT_OUTER,
134 )
135}
136
137pub fn abi_values_to_fields(args: &[AbiValue]) -> Vec<Fr> {
143 let mut fields = Vec::new();
144 for arg in args {
145 flatten_abi_value(arg, &mut fields);
146 }
147 fields
148}
149
150fn flatten_abi_value(value: &AbiValue, out: &mut Vec<Fr>) {
151 match value {
152 AbiValue::Field(f) => out.push(*f),
153 AbiValue::Boolean(b) => out.push(if *b { Fr::one() } else { Fr::zero() }),
154 AbiValue::Integer(i) => {
155 out.push(Fr::from(*i as u64));
158 }
159 AbiValue::Array(items) => {
160 for item in items {
161 flatten_abi_value(item, out);
162 }
163 }
164 AbiValue::String(s) => {
165 for byte in s.bytes() {
166 out.push(Fr::from(u64::from(byte)));
167 }
168 }
169 AbiValue::Struct(map) => {
170 for value in map.values() {
172 flatten_abi_value(value, out);
173 }
174 }
175 AbiValue::Tuple(items) => {
176 for item in items {
177 flatten_abi_value(item, out);
178 }
179 }
180 }
181}
182
183pub fn compute_inner_auth_wit_hash_from_action(caller: &AztecAddress, call: &FunctionCall) -> Fr {
189 let args_as_fields = abi_values_to_fields(&call.args);
190 let args_hash = compute_var_args_hash(&args_as_fields);
191 compute_inner_auth_wit_hash(&[caller.0, call.selector.to_field(), args_hash])
192}
193
194#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
199#[serde(rename_all = "camelCase")]
200pub struct ChainInfo {
201 pub chain_id: Fr,
203 pub version: Fr,
205}
206
207#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
212#[serde(tag = "type", rename_all = "snake_case")]
213pub enum MessageHashOrIntent {
214 Hash {
216 hash: Fr,
218 },
219 Intent {
221 caller: AztecAddress,
223 call: FunctionCall,
225 },
226 InnerHash {
231 consumer: AztecAddress,
233 inner_hash: Fr,
235 },
236}
237
238pub fn compute_auth_wit_message_hash(intent: &MessageHashOrIntent, chain_info: &ChainInfo) -> Fr {
251 match intent {
252 MessageHashOrIntent::Hash { hash } => *hash,
253 MessageHashOrIntent::Intent { caller, call } => {
254 let inner_hash = compute_inner_auth_wit_hash_from_action(caller, call);
255 compute_outer_auth_wit_hash(
256 &call.to,
257 &chain_info.chain_id,
258 &chain_info.version,
259 &inner_hash,
260 )
261 }
262 MessageHashOrIntent::InnerHash {
263 consumer,
264 inner_hash,
265 } => compute_outer_auth_wit_hash(
266 consumer,
267 &chain_info.chain_id,
268 &chain_info.version,
269 inner_hash,
270 ),
271 }
272}
273
274pub fn compute_initialization_hash(
284 init_fn: Option<&FunctionArtifact>,
285 args: &[AbiValue],
286) -> Result<Fr, Error> {
287 match init_fn {
288 None => Ok(Fr::zero()),
289 Some(func) => {
290 let selector = func.selector.unwrap_or_else(|| {
291 FunctionSelector::from_name_and_parameters(&func.name, &func.parameters)
292 });
293 let encoded_args = encode_arguments(func, args)?;
294 let args_hash = compute_var_args_hash(&encoded_args);
295 Ok(poseidon2_hash_with_separator(
296 &[selector.to_field(), args_hash],
297 domain_separator::INITIALIZER,
298 ))
299 }
300 }
301}
302
303pub fn compute_initialization_hash_from_encoded(selector: Fr, encoded_args: &[Fr]) -> Fr {
305 let args_hash = compute_var_args_hash(encoded_args);
306 poseidon2_hash_with_separator(&[selector, args_hash], domain_separator::INITIALIZER)
307}
308
309pub fn compute_private_functions_root(private_functions: &mut [(FunctionSelector, Fr)]) -> Fr {
318 let tree_height = constants::FUNCTION_TREE_HEIGHT;
319 let num_leaves = 1usize << tree_height; private_functions.sort_by_key(|(sel, _)| u32::from_be_bytes(sel.0));
323
324 let zero_leaf = poseidon2_hash(&[Fr::zero(), Fr::zero()]);
326 let mut leaves: Vec<Fr> = Vec::with_capacity(num_leaves);
327 for (sel, vk_hash) in private_functions.iter() {
328 let leaf = poseidon2_hash_with_separator(
329 &[sel.to_field(), *vk_hash],
330 domain_separator::PRIVATE_FUNCTION_LEAF,
331 );
332 leaves.push(leaf);
333 }
334 leaves.resize(num_leaves, zero_leaf);
336
337 poseidon_merkle_root(&leaves)
339}
340
341fn poseidon_merkle_root(leaves: &[Fr]) -> Fr {
343 if leaves.is_empty() {
344 return Fr::zero();
345 }
346 if leaves.len() == 1 {
347 return leaves[0];
348 }
349
350 let mut current = leaves.to_vec();
351 while current.len() > 1 {
352 let mut next = Vec::with_capacity(current.len().div_ceil(2));
353 for chunk in current.chunks(2) {
354 let left = chunk[0];
355 let right = if chunk.len() > 1 {
356 chunk[1]
357 } else {
358 Fr::zero()
359 };
360 next.push(poseidon2_hash(&[left, right]));
361 }
362 current = next;
363 }
364 current[0]
365}
366
367fn sha256_merkle_root(leaves: &[Fr]) -> Fr {
368 if leaves.is_empty() {
369 return Fr::zero();
370 }
371 if leaves.len() == 1 {
372 return leaves[0];
373 }
374
375 let mut current = leaves.to_vec();
376 while current.len() > 1 {
377 let mut next = Vec::with_capacity(current.len().div_ceil(2));
378 for chunk in current.chunks(2) {
379 let left = chunk[0].to_be_bytes();
380 let right = chunk.get(1).unwrap_or(&Fr::zero()).to_be_bytes();
381 next.push(sha256_to_field(
382 &[left.as_slice(), right.as_slice()].concat(),
383 ));
384 }
385 current = next;
386 }
387 current[0]
388}
389
390fn sha256_to_field(data: &[u8]) -> Fr {
393 let hash = Sha256::digest(data);
394 Fr::from(<[u8; 32]>::try_from(hash.as_slice()).expect("SHA256 is 32 bytes"))
395}
396
397pub fn compute_artifact_hash(artifact: &ContractArtifact) -> Fr {
402 let private_fn_tree_root = compute_artifact_function_tree_root(artifact, false);
403 let unconstrained_fn_tree_root = compute_artifact_function_tree_root(artifact, true);
404 let metadata_hash = compute_artifact_metadata_hash(artifact);
405
406 let mut data = Vec::new();
407 data.push(1u8);
408 data.extend_from_slice(&private_fn_tree_root.to_be_bytes());
409 data.extend_from_slice(&unconstrained_fn_tree_root.to_be_bytes());
410 data.extend_from_slice(&metadata_hash.to_be_bytes());
411 sha256_to_field(&data)
412}
413
414fn canonical_json_string(value: &serde_json::Value) -> String {
415 match value {
416 serde_json::Value::Null => "null".to_owned(),
417 serde_json::Value::Bool(boolean) => boolean.to_string(),
418 serde_json::Value::Number(number) => number.to_string(),
419 serde_json::Value::String(string) => {
420 serde_json::to_string(string).unwrap_or_else(|_| "\"\"".to_owned())
421 }
422 serde_json::Value::Array(items) => {
423 let inner = items
424 .iter()
425 .map(canonical_json_string)
426 .collect::<Vec<_>>()
427 .join(",");
428 format!("[{inner}]")
429 }
430 serde_json::Value::Object(map) => {
431 let mut entries = map.iter().collect::<Vec<_>>();
432 entries.sort_by(|(left, _), (right, _)| left.cmp(right));
433 let inner = entries
434 .into_iter()
435 .map(|(key, value)| {
436 let key = serde_json::to_string(key).unwrap_or_else(|_| "\"\"".to_owned());
437 format!("{key}:{}", canonical_json_string(value))
438 })
439 .collect::<Vec<_>>()
440 .join(",");
441 format!("{{{inner}}}")
442 }
443 }
444}
445
446fn decode_artifact_bytes(encoded: &str) -> Vec<u8> {
447 if let Some(hex) = encoded.strip_prefix("0x") {
448 return hex::decode(hex).unwrap_or_else(|_| encoded.as_bytes().to_vec());
449 }
450
451 use base64::Engine;
452 base64::engine::general_purpose::STANDARD
453 .decode(encoded)
454 .unwrap_or_else(|_| encoded.as_bytes().to_vec())
455}
456
457fn compute_artifact_function_tree_root(artifact: &ContractArtifact, unconstrained: bool) -> Fr {
459 let functions: Vec<&FunctionArtifact> = artifact
460 .functions
461 .iter()
462 .filter(|f| {
463 if unconstrained {
464 f.function_type == FunctionType::Utility || f.is_unconstrained == Some(true)
465 } else {
466 f.function_type == FunctionType::Private
467 }
468 })
469 .collect();
470
471 if functions.is_empty() {
472 return Fr::zero();
473 }
474
475 let leaves: Vec<Fr> = functions
476 .iter()
477 .map(|func| {
478 let selector = func.selector.unwrap_or_else(|| {
479 FunctionSelector::from_name_and_parameters(&func.name, &func.parameters)
480 });
481 let metadata_hash = compute_function_metadata_hash(func);
482 let bytecode_hash = compute_function_bytecode_hash(func);
483
484 let mut leaf_data = Vec::new();
485 leaf_data.push(1u8);
486 leaf_data.extend_from_slice(&selector.0);
487 leaf_data.extend_from_slice(&metadata_hash.to_be_bytes());
488 leaf_data.extend_from_slice(&bytecode_hash.to_be_bytes());
489 sha256_to_field(&leaf_data)
490 })
491 .collect();
492
493 let height = if leaves.len() <= 1 {
494 0
495 } else {
496 (leaves.len() as f64).log2().ceil() as usize
497 };
498 let num_leaves = 1usize << height;
499 let mut padded = leaves;
500 padded.resize(num_leaves.max(1), Fr::zero());
501 sha256_merkle_root(&padded)
502}
503
504fn compute_function_metadata_hash(func: &FunctionArtifact) -> Fr {
506 let metadata = serde_json::to_value(&func.return_types).unwrap_or(serde_json::Value::Null);
507 let serialized = canonical_json_string(&metadata);
508 sha256_to_field(serialized.as_bytes())
509}
510
511fn compute_function_bytecode_hash(func: &FunctionArtifact) -> Fr {
513 match &func.bytecode {
514 Some(bc) if !bc.is_empty() => sha256_to_field(&decode_artifact_bytes(bc)),
515 _ => Fr::zero(),
516 }
517}
518
519fn compute_artifact_metadata_hash(artifact: &ContractArtifact) -> Fr {
521 let mut metadata = serde_json::Map::new();
522 metadata.insert(
523 "name".to_owned(),
524 serde_json::Value::String(artifact.name.clone()),
525 );
526 if let Some(outputs) = &artifact.outputs {
527 metadata.insert("outputs".to_owned(), outputs.clone());
528 }
529 let serialized = canonical_json_string(&serde_json::Value::Object(metadata));
530 sha256_to_field(serialized.as_bytes())
531}
532
533pub fn compute_public_bytecode_commitment(packed_bytecode: &[u8]) -> Fr {
537 let fields = crate::abi::buffer_as_fields(
538 packed_bytecode,
539 constants::MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
540 )
541 .expect("packed bytecode exceeds maximum field count");
542 let byte_length = fields[0].to_usize() as u64;
543 let length_in_fields = byte_length.div_ceil(31) as usize;
544
545 let separator = Fr::from(u64::from(domain_separator::PUBLIC_BYTECODE) + (byte_length << 32));
546 poseidon2_hash_with_separator_field(&fields[1..1 + length_in_fields], separator)
547}
548
549pub fn compute_contract_class_id(
553 artifact_hash: Fr,
554 private_functions_root: Fr,
555 public_bytecode_commitment: Fr,
556) -> Fr {
557 poseidon2_hash_with_separator(
558 &[
559 artifact_hash,
560 private_functions_root,
561 public_bytecode_commitment,
562 ],
563 domain_separator::CONTRACT_CLASS_ID,
564 )
565}
566
567pub fn compute_contract_class_id_from_artifact(artifact: &ContractArtifact) -> Result<Fr, Error> {
569 let artifact_hash = compute_artifact_hash(artifact);
570 let private_fns_root = compute_private_functions_root_from_artifact(artifact)?;
571 let public_bytecode = extract_packed_public_bytecode(artifact);
572 let public_bytecode_commitment = compute_public_bytecode_commitment(&public_bytecode);
573 Ok(compute_contract_class_id(
574 artifact_hash,
575 private_fns_root,
576 public_bytecode_commitment,
577 ))
578}
579
580pub fn compute_private_functions_root_from_artifact(
582 artifact: &ContractArtifact,
583) -> Result<Fr, Error> {
584 let mut private_fns: Vec<(FunctionSelector, Fr)> = artifact
585 .functions
586 .iter()
587 .filter(|f| f.function_type == FunctionType::Private)
588 .map(|f| {
589 let selector = f.selector.unwrap_or_else(|| {
590 FunctionSelector::from_name_and_parameters(&f.name, &f.parameters)
591 });
592 let vk_hash = f.verification_key_hash.unwrap_or(Fr::zero());
593 (selector, vk_hash)
594 })
595 .collect();
596
597 Ok(compute_private_functions_root(&mut private_fns))
598}
599
600fn extract_packed_public_bytecode(artifact: &ContractArtifact) -> Vec<u8> {
602 let mut bytecode = Vec::new();
603 for func in &artifact.functions {
604 if func.function_type == FunctionType::Public {
605 if let Some(ref bc) = func.bytecode {
606 bytecode.extend_from_slice(&decode_artifact_bytes(bc));
607 }
608 }
609 }
610 bytecode
611}
612
613pub fn compute_salted_initialization_hash(
621 salt: Fr,
622 initialization_hash: Fr,
623 deployer: AztecAddress,
624) -> Fr {
625 poseidon2_hash_with_separator(
626 &[salt, initialization_hash, deployer.0],
627 domain_separator::PARTIAL_ADDRESS,
628 )
629}
630
631pub fn compute_partial_address(
635 original_contract_class_id: Fr,
636 salted_initialization_hash: Fr,
637) -> Fr {
638 poseidon2_hash_with_separator(
639 &[original_contract_class_id, salted_initialization_hash],
640 domain_separator::PARTIAL_ADDRESS,
641 )
642}
643
644pub fn compute_contract_address_from_instance(
653 instance: &ContractInstance,
654) -> Result<AztecAddress, Error> {
655 let public_keys_hash = instance.public_keys.hash();
656
657 let salted_init_hash = compute_salted_initialization_hash(
658 instance.salt,
659 instance.initialization_hash,
660 instance.deployer,
661 );
662
663 let partial_address =
664 compute_partial_address(instance.original_contract_class_id, salted_init_hash);
665
666 let preaddress = poseidon2_hash_with_separator(
667 &[public_keys_hash, partial_address],
668 domain_separator::CONTRACT_ADDRESS_V1,
669 );
670
671 let g = grumpkin::generator();
673 let preaddress_point = grumpkin::scalar_mul(&preaddress, &g);
674 let ivpk_m = &instance.public_keys.master_incoming_viewing_public_key;
675
676 let address_point = if ivpk_m.is_zero() && !ivpk_m.is_infinite {
677 preaddress_point
678 } else {
679 grumpkin::point_add(&preaddress_point, ivpk_m)
680 };
681
682 if address_point.is_infinite {
683 return Err(Error::InvalidData(
684 "contract address derivation resulted in point at infinity".to_owned(),
685 ));
686 }
687
688 Ok(AztecAddress(address_point.x))
689}
690
691#[cfg(test)]
692#[allow(clippy::expect_used, clippy::panic)]
693mod tests {
694 use super::*;
695 use crate::abi::{buffer_as_fields, FunctionSelector, FunctionType};
696
697 #[test]
698 fn var_args_hash_empty_returns_zero() {
699 assert_eq!(compute_var_args_hash(&[]), Fr::zero());
700 }
701
702 #[test]
703 fn poseidon2_hash_known_vector() {
704 let result = poseidon2_hash(&[Fr::from(1u64)]);
707 let expected =
708 Fr::from_hex("0x168758332d5b3e2d13be8048c8011b454590e06c44bce7f702f09103eef5a373")
709 .expect("valid hex");
710 assert_eq!(
711 result, expected,
712 "Poseidon2 hash of [1] must match barretenberg test vector"
713 );
714 }
715
716 #[test]
717 fn poseidon2_hash_with_separator_prepends_separator() {
718 let a = Fr::from(10u64);
720 let b = Fr::from(20u64);
721 let sep = 42u32;
722
723 let result = poseidon2_hash_with_separator(&[a, b], sep);
724 let manual = poseidon2_hash(&[Fr::from(u64::from(sep)), a, b]);
725 assert_eq!(result, manual);
726 }
727
728 #[test]
729 fn var_args_hash_single_element() {
730 let result = compute_var_args_hash(&[Fr::from(42u64)]);
731 let expected =
733 poseidon2_hash_with_separator(&[Fr::from(42u64)], domain_separator::FUNCTION_ARGS);
734 assert_eq!(result, expected);
735 }
736
737 #[test]
738 fn inner_auth_wit_hash_uses_correct_separator() {
739 let args = [Fr::from(1u64), Fr::from(2u64), Fr::from(3u64)];
740 let result = compute_inner_auth_wit_hash(&args);
741 let expected = poseidon2_hash_with_separator(&args, domain_separator::AUTHWIT_INNER);
742 assert_eq!(result, expected);
743 }
744
745 #[test]
746 fn outer_auth_wit_hash_uses_correct_separator() {
747 let consumer = AztecAddress(Fr::from(100u64));
748 let chain_id = Fr::from(31337u64);
749 let version = Fr::from(1u64);
750 let inner_hash = Fr::from(999u64);
751
752 let result = compute_outer_auth_wit_hash(&consumer, &chain_id, &version, &inner_hash);
753 let expected = poseidon2_hash_with_separator(
754 &[consumer.0, chain_id, version, inner_hash],
755 domain_separator::AUTHWIT_OUTER,
756 );
757 assert_eq!(result, expected);
758 }
759
760 #[test]
761 fn inner_auth_wit_hash_from_action() {
762 let caller = AztecAddress(Fr::from(1u64));
763 let call = FunctionCall {
764 to: AztecAddress(Fr::from(2u64)),
765 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
766 args: vec![AbiValue::Field(Fr::from(100u64))],
767 function_type: FunctionType::Private,
768 is_static: false,
769 };
770
771 let result = compute_inner_auth_wit_hash_from_action(&caller, &call);
772
773 let args_hash = compute_var_args_hash(&[Fr::from(100u64)]);
775 let selector_field = call.selector.to_field();
776 let expected = compute_inner_auth_wit_hash(&[caller.0, selector_field, args_hash]);
777 assert_eq!(result, expected);
778 }
779
780 #[test]
781 fn auth_wit_message_hash_passthrough() {
782 let hash = Fr::from(42u64);
783 let chain_info = ChainInfo {
784 chain_id: Fr::from(31337u64),
785 version: Fr::from(1u64),
786 };
787 let result =
788 compute_auth_wit_message_hash(&MessageHashOrIntent::Hash { hash }, &chain_info);
789 assert_eq!(result, hash);
790 }
791
792 #[test]
793 fn auth_wit_message_hash_from_intent() {
794 let caller = AztecAddress(Fr::from(10u64));
795 let consumer = AztecAddress(Fr::from(20u64));
796 let call = FunctionCall {
797 to: consumer,
798 selector: FunctionSelector::from_hex("0x11223344").expect("valid"),
799 args: vec![],
800 function_type: FunctionType::Private,
801 is_static: false,
802 };
803 let chain_info = ChainInfo {
804 chain_id: Fr::from(31337u64),
805 version: Fr::from(1u64),
806 };
807
808 let result = compute_auth_wit_message_hash(
809 &MessageHashOrIntent::Intent {
810 caller,
811 call: call.clone(),
812 },
813 &chain_info,
814 );
815
816 let inner = compute_inner_auth_wit_hash_from_action(&caller, &call);
818 let expected = compute_outer_auth_wit_hash(
819 &consumer,
820 &chain_info.chain_id,
821 &chain_info.version,
822 &inner,
823 );
824 assert_eq!(result, expected);
825 }
826
827 #[test]
828 fn auth_wit_message_hash_from_inner_hash() {
829 let consumer = AztecAddress(Fr::from(20u64));
830 let inner_hash = Fr::from(999u64);
831 let chain_info = ChainInfo {
832 chain_id: Fr::from(31337u64),
833 version: Fr::from(1u64),
834 };
835
836 let result = compute_auth_wit_message_hash(
837 &MessageHashOrIntent::InnerHash {
838 consumer,
839 inner_hash,
840 },
841 &chain_info,
842 );
843
844 let expected = compute_outer_auth_wit_hash(
845 &consumer,
846 &chain_info.chain_id,
847 &chain_info.version,
848 &inner_hash,
849 );
850 assert_eq!(result, expected);
851 }
852
853 #[test]
854 fn abi_values_to_fields_basic_types() {
855 let values = vec![
856 AbiValue::Field(Fr::from(1u64)),
857 AbiValue::Boolean(true),
858 AbiValue::Boolean(false),
859 AbiValue::Integer(42),
860 ];
861 let fields = abi_values_to_fields(&values);
862 assert_eq!(fields.len(), 4);
863 assert_eq!(fields[0], Fr::from(1u64));
864 assert_eq!(fields[1], Fr::one());
865 assert_eq!(fields[2], Fr::zero());
866 assert_eq!(fields[3], Fr::from(42u64));
867 }
868
869 #[test]
870 fn abi_values_to_fields_nested() {
871 let values = vec![AbiValue::Array(vec![
872 AbiValue::Field(Fr::from(1u64)),
873 AbiValue::Field(Fr::from(2u64)),
874 ])];
875 let fields = abi_values_to_fields(&values);
876 assert_eq!(fields.len(), 2);
877 assert_eq!(fields[0], Fr::from(1u64));
878 assert_eq!(fields[1], Fr::from(2u64));
879 }
880
881 #[test]
882 fn message_hash_or_intent_serde_roundtrip() {
883 let variants = vec![
884 MessageHashOrIntent::Hash {
885 hash: Fr::from(42u64),
886 },
887 MessageHashOrIntent::Intent {
888 caller: AztecAddress(Fr::from(1u64)),
889 call: FunctionCall {
890 to: AztecAddress(Fr::from(2u64)),
891 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
892 args: vec![],
893 function_type: FunctionType::Private,
894 is_static: false,
895 },
896 },
897 MessageHashOrIntent::InnerHash {
898 consumer: AztecAddress(Fr::from(3u64)),
899 inner_hash: Fr::from(999u64),
900 },
901 ];
902
903 for variant in variants {
904 let json = serde_json::to_string(&variant).expect("serialize");
905 let decoded: MessageHashOrIntent = serde_json::from_str(&json).expect("deserialize");
906 assert_eq!(decoded, variant);
907 }
908 }
909
910 #[test]
911 fn chain_info_serde_roundtrip() {
912 let info = ChainInfo {
913 chain_id: Fr::from(31337u64),
914 version: Fr::from(1u64),
915 };
916 let json = serde_json::to_string(&info).expect("serialize");
917 let decoded: ChainInfo = serde_json::from_str(&json).expect("deserialize");
918 assert_eq!(decoded, info);
919 }
920
921 #[test]
924 fn initialization_hash_no_constructor_returns_zero() {
925 let result = compute_initialization_hash(None, &[]).expect("no constructor");
926 assert_eq!(result, Fr::zero());
927 }
928
929 #[test]
930 fn initialization_hash_with_constructor() {
931 use crate::abi::AbiParameter;
932 let func = FunctionArtifact {
933 name: "constructor".to_owned(),
934 function_type: FunctionType::Private,
935 is_initializer: true,
936 is_static: false,
937 is_only_self: None,
938 parameters: vec![AbiParameter {
939 name: "admin".to_owned(),
940 typ: crate::abi::AbiType::Field,
941 visibility: None,
942 }],
943 return_types: vec![],
944 error_types: None,
945 selector: Some(FunctionSelector::from_hex("0xe5fb6c81").expect("valid")),
946 bytecode: None,
947 verification_key_hash: None,
948 verification_key: None,
949 custom_attributes: None,
950 is_unconstrained: None,
951 debug_symbols: None,
952 };
953 let args = vec![AbiValue::Field(Fr::from(42u64))];
954 let result = compute_initialization_hash(Some(&func), &args).expect("init hash");
955 assert_ne!(result, Fr::zero());
956 }
957
958 #[test]
959 fn initialization_hash_from_encoded() {
960 let selector = Fr::from(12345u64);
961 let args = vec![Fr::from(1u64), Fr::from(2u64)];
962 let result = compute_initialization_hash_from_encoded(selector, &args);
963 let args_hash = compute_var_args_hash(&args);
964 let expected =
965 poseidon2_hash_with_separator(&[selector, args_hash], domain_separator::INITIALIZER);
966 assert_eq!(result, expected);
967 }
968
969 #[test]
970 fn private_functions_root_empty() {
971 let root = compute_private_functions_root(&mut []);
972 assert_ne!(root, Fr::zero()); }
975
976 #[test]
977 fn contract_class_id_deterministic() {
978 let artifact_hash = Fr::from(1u64);
979 let root = Fr::from(2u64);
980 let commitment = Fr::from(3u64);
981 let id1 = compute_contract_class_id(artifact_hash, root, commitment);
982 let id2 = compute_contract_class_id(artifact_hash, root, commitment);
983 assert_eq!(id1, id2);
984 assert_ne!(id1, Fr::zero());
985 }
986
987 #[test]
988 fn buffer_as_fields_basic() {
989 let data = vec![0u8; 31];
990 let fields = buffer_as_fields(&data, 100).expect("encode");
991 assert_eq!(fields.len(), 100);
994 assert_eq!(fields[0], Fr::from(31u64)); }
996
997 #[test]
998 fn buffer_as_fields_multiple_chunks() {
999 let data = vec![0xffu8; 62]; let fields = buffer_as_fields(&data, 100).expect("encode");
1001 assert_eq!(fields.len(), 100);
1002 assert_eq!(fields[0], Fr::from(62u64)); }
1004
1005 #[test]
1006 fn public_bytecode_commitment_empty() {
1007 let result = compute_public_bytecode_commitment(&[]);
1008 assert_ne!(result, Fr::zero());
1010 }
1011
1012 #[test]
1013 fn public_bytecode_commitment_non_empty() {
1014 let data = vec![0x01u8; 100];
1015 let result = compute_public_bytecode_commitment(&data);
1016 assert_ne!(result, Fr::zero());
1017 }
1018
1019 #[test]
1020 fn salted_initialization_hash_uses_partial_address_separator() {
1021 let salt = Fr::from(1u64);
1022 let init_hash = Fr::from(2u64);
1023 let deployer = AztecAddress(Fr::from(3u64));
1024 let result = compute_salted_initialization_hash(salt, init_hash, deployer);
1025 let expected = poseidon2_hash_with_separator(
1026 &[salt, init_hash, deployer.0],
1027 domain_separator::PARTIAL_ADDRESS,
1028 );
1029 assert_eq!(result, expected);
1030 }
1031
1032 #[test]
1033 fn partial_address_uses_correct_separator() {
1034 let class_id = Fr::from(100u64);
1035 let salted = Fr::from(200u64);
1036 let result = compute_partial_address(class_id, salted);
1037 let expected =
1038 poseidon2_hash_with_separator(&[class_id, salted], domain_separator::PARTIAL_ADDRESS);
1039 assert_eq!(result, expected);
1040 }
1041
1042 #[test]
1043 fn contract_address_from_instance_default_keys() {
1044 use crate::types::{ContractInstance, PublicKeys};
1045 let instance = ContractInstance {
1046 version: 1,
1047 salt: Fr::from(42u64),
1048 deployer: AztecAddress(Fr::zero()),
1049 current_contract_class_id: Fr::from(100u64),
1050 original_contract_class_id: Fr::from(100u64),
1051 initialization_hash: Fr::zero(),
1052 public_keys: PublicKeys::default(),
1053 };
1054 let address =
1055 compute_contract_address_from_instance(&instance).expect("address derivation");
1056 assert_ne!(address.0, Fr::zero());
1057 }
1058
1059 #[test]
1060 fn contract_address_is_deterministic() {
1061 use crate::types::{ContractInstance, PublicKeys};
1062 let instance = ContractInstance {
1063 version: 1,
1064 salt: Fr::from(99u64),
1065 deployer: AztecAddress(Fr::from(1u64)),
1066 current_contract_class_id: Fr::from(200u64),
1067 original_contract_class_id: Fr::from(200u64),
1068 initialization_hash: Fr::from(300u64),
1069 public_keys: PublicKeys::default(),
1070 };
1071 let addr1 = compute_contract_address_from_instance(&instance).expect("addr1");
1072 let addr2 = compute_contract_address_from_instance(&instance).expect("addr2");
1073 assert_eq!(addr1, addr2);
1074 }
1075
1076 #[test]
1077 fn artifact_hash_deterministic() {
1078 let artifact = ContractArtifact {
1079 name: "Test".to_owned(),
1080 functions: vec![],
1081 outputs: None,
1082 file_map: None,
1083 };
1084 let h1 = compute_artifact_hash(&artifact);
1085 let h2 = compute_artifact_hash(&artifact);
1086 assert_eq!(h1, h2);
1087 }
1088
1089 #[test]
1090 fn class_id_from_artifact_no_functions() {
1091 let artifact = ContractArtifact {
1092 name: "Empty".to_owned(),
1093 functions: vec![],
1094 outputs: None,
1095 file_map: None,
1096 };
1097 let id = compute_contract_class_id_from_artifact(&artifact).expect("class id");
1098 assert_ne!(id, Fr::zero());
1099 }
1100}