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