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 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_l1_to_l2_message_nullifier(
113 contract: &AztecAddress,
114 message_hash: &Fr,
115 secret: &Fr,
116) -> Fr {
117 let inner = poseidon2_hash_with_separator(
118 &[*message_hash, *secret],
119 domain_separator::MESSAGE_NULLIFIER,
120 );
121 silo_nullifier(contract, &inner)
122}
123
124pub fn compute_l2_to_l1_message_hash(
130 l2_sender: &AztecAddress,
131 l1_recipient: &crate::types::EthAddress,
132 content: &Fr,
133 rollup_version: &Fr,
134 chain_id: &Fr,
135) -> Fr {
136 let mut data = Vec::with_capacity(5 * 32);
137 data.extend_from_slice(&l2_sender.0.to_be_bytes());
138 data.extend_from_slice(&rollup_version.to_be_bytes());
139 let mut eth_bytes = [0u8; 32];
141 eth_bytes[12..32].copy_from_slice(&l1_recipient.0);
142 data.extend_from_slice(ð_bytes);
143 data.extend_from_slice(&chain_id.to_be_bytes());
144 data.extend_from_slice(&content.to_be_bytes());
145 sha256_to_field(&data)
146}
147
148pub fn compute_var_args_hash(args: &[Fr]) -> Fr {
154 if args.is_empty() {
155 return Fr::zero();
156 }
157 poseidon2_hash_with_separator(args, domain_separator::FUNCTION_ARGS)
158}
159
160pub fn compute_calldata_hash(calldata: &[Fr]) -> Fr {
164 poseidon2_hash_with_separator(calldata, domain_separator::PUBLIC_CALLDATA)
165}
166
167pub fn silo_note_hash(contract_address: &AztecAddress, note_hash: &Fr) -> Fr {
175 poseidon2_hash_with_separator(
176 &[contract_address.0, *note_hash],
177 domain_separator::SILOED_NOTE_HASH,
178 )
179}
180
181pub fn silo_nullifier(contract_address: &AztecAddress, inner_nullifier: &Fr) -> Fr {
185 poseidon2_hash_with_separator(
186 &[contract_address.0, *inner_nullifier],
187 domain_separator::SILOED_NULLIFIER,
188 )
189}
190
191pub fn compute_protocol_nullifier(tx_request_hash: &Fr) -> Fr {
195 silo_nullifier(
196 &constants::protocol_contract_address::null_msg_sender(),
197 tx_request_hash,
198 )
199}
200
201pub fn compute_unique_note_hash(nonce: &Fr, siloed_note_hash: &Fr) -> Fr {
205 poseidon2_hash_with_separator(
206 &[*nonce, *siloed_note_hash],
207 domain_separator::UNIQUE_NOTE_HASH,
208 )
209}
210
211pub fn compute_note_hash_nonce(first_nullifier: &Fr, note_hash_index: usize) -> Fr {
215 poseidon2_hash_with_separator(
216 &[*first_nullifier, Fr::from(note_hash_index as u64)],
217 domain_separator::NOTE_HASH_NONCE,
218 )
219}
220
221pub fn compute_siloed_private_log_first_field(contract_address: &AztecAddress, field: &Fr) -> Fr {
225 poseidon2_hash_with_separator(
226 &[contract_address.0, *field],
227 domain_separator::PRIVATE_LOG_FIRST_FIELD,
228 )
229}
230
231pub fn compute_inner_auth_wit_hash(args: &[Fr]) -> Fr {
238 poseidon2_hash_with_separator(args, domain_separator::AUTHWIT_INNER)
239}
240
241pub fn compute_outer_auth_wit_hash(
248 consumer: &AztecAddress,
249 chain_id: &Fr,
250 version: &Fr,
251 inner_hash: &Fr,
252) -> Fr {
253 poseidon2_hash_with_separator(
254 &[consumer.0, *chain_id, *version, *inner_hash],
255 domain_separator::AUTHWIT_OUTER,
256 )
257}
258
259pub fn compute_protocol_contracts_hash() -> Fr {
264 let derived_addresses = [
268 Fr::from_hex("0x139f8eb6d6e7e7a7c0322c3b7558687a7e24201f11bf2c4cb2fe56c18d363695")
269 .expect("valid protocol contract address"),
270 Fr::from_hex("0x1254246c88aca5a66fa66f3aa78c408a698ebca3b713120497c7555dfc718592")
271 .expect("valid protocol contract address"),
272 Fr::from_hex("0x14d670efa326a07b99777b01fb706427ca776095246569150f2a3f17a7d4dc66")
273 .expect("valid protocol contract address"),
274 Fr::from_hex("0x230d0b47ba6d5ed99afb89d584f32ff33438b64f51000f252a140cf995781628")
275 .expect("valid protocol contract address"),
276 Fr::from_hex("0x204913186c0dd70015d05bf9100a12e31ccb7cc2527aacdfae0c19ad6439fcf4")
277 .expect("valid protocol contract address"),
278 Fr::from_hex("0x1198142fd84a58c0ab22d5fde371ce527042db49487e05206a326ad154952ac8")
279 .expect("valid protocol contract address"),
280 Fr::zero(),
281 Fr::zero(),
282 Fr::zero(),
283 Fr::zero(),
284 Fr::zero(),
285 ];
286
287 poseidon2_hash_with_separator(&derived_addresses, domain_separator::PROTOCOL_CONTRACTS)
288}
289
290pub fn abi_values_to_fields(args: &[AbiValue]) -> Vec<Fr> {
296 let mut fields = Vec::new();
297 for arg in args {
298 flatten_abi_value(arg, &mut fields);
299 }
300 fields
301}
302
303fn flatten_abi_value(value: &AbiValue, out: &mut Vec<Fr>) {
304 match value {
305 AbiValue::Field(f) => out.push(*f),
306 AbiValue::Boolean(b) => out.push(if *b { Fr::one() } else { Fr::zero() }),
307 AbiValue::Integer(i) => {
308 out.push(Fr::from(*i as u64));
311 }
312 AbiValue::Array(items) => {
313 for item in items {
314 flatten_abi_value(item, out);
315 }
316 }
317 AbiValue::String(s) => {
318 for byte in s.bytes() {
319 out.push(Fr::from(u64::from(byte)));
320 }
321 }
322 AbiValue::Struct(map) => {
323 for value in map.values() {
325 flatten_abi_value(value, out);
326 }
327 }
328 AbiValue::Tuple(items) => {
329 for item in items {
330 flatten_abi_value(item, out);
331 }
332 }
333 }
334}
335
336pub fn compute_inner_auth_wit_hash_from_action(caller: &AztecAddress, call: &FunctionCall) -> Fr {
342 let args_as_fields = abi_values_to_fields(&call.args);
343 let args_hash = compute_var_args_hash(&args_as_fields);
344 compute_inner_auth_wit_hash(&[caller.0, call.selector.to_field(), args_hash])
345}
346
347#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
352#[serde(rename_all = "camelCase")]
353pub struct ChainInfo {
354 pub chain_id: Fr,
356 pub version: Fr,
358}
359
360#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
365#[serde(tag = "type", rename_all = "snake_case")]
366pub enum MessageHashOrIntent {
367 Hash {
369 hash: Fr,
371 },
372 Intent {
374 caller: AztecAddress,
376 call: FunctionCall,
378 },
379 InnerHash {
384 consumer: AztecAddress,
386 inner_hash: Fr,
388 },
389}
390
391pub fn compute_auth_wit_message_hash(intent: &MessageHashOrIntent, chain_info: &ChainInfo) -> Fr {
404 match intent {
405 MessageHashOrIntent::Hash { hash } => *hash,
406 MessageHashOrIntent::Intent { caller, call } => {
407 let inner_hash = compute_inner_auth_wit_hash_from_action(caller, call);
408 compute_outer_auth_wit_hash(
409 &call.to,
410 &chain_info.chain_id,
411 &chain_info.version,
412 &inner_hash,
413 )
414 }
415 MessageHashOrIntent::InnerHash {
416 consumer,
417 inner_hash,
418 } => compute_outer_auth_wit_hash(
419 consumer,
420 &chain_info.chain_id,
421 &chain_info.version,
422 inner_hash,
423 ),
424 }
425}
426
427pub fn compute_initialization_hash(
437 init_fn: Option<&FunctionArtifact>,
438 args: &[AbiValue],
439) -> Result<Fr, Error> {
440 match init_fn {
441 None => Ok(Fr::zero()),
442 Some(func) => {
443 let selector = func.selector.unwrap_or_else(|| {
444 FunctionSelector::from_name_and_parameters(&func.name, &func.parameters)
445 });
446 let encoded_args = encode_arguments(func, args)?;
447 let args_hash = compute_var_args_hash(&encoded_args);
448 Ok(poseidon2_hash_with_separator(
449 &[selector.to_field(), args_hash],
450 domain_separator::INITIALIZER,
451 ))
452 }
453 }
454}
455
456pub fn compute_initialization_hash_from_encoded(selector: Fr, encoded_args: &[Fr]) -> Fr {
458 let args_hash = compute_var_args_hash(encoded_args);
459 poseidon2_hash_with_separator(&[selector, args_hash], domain_separator::INITIALIZER)
460}
461
462pub fn compute_private_functions_root(private_functions: &mut [(FunctionSelector, Fr)]) -> Fr {
471 let tree_height = constants::FUNCTION_TREE_HEIGHT;
472 let num_leaves = 1usize << tree_height; private_functions.sort_by_key(|(sel, _)| u32::from_be_bytes(sel.0));
476
477 let zero_leaf = poseidon2_hash(&[Fr::zero(), Fr::zero()]);
479 let mut leaves: Vec<Fr> = Vec::with_capacity(num_leaves);
480 for (sel, vk_hash) in private_functions.iter() {
481 let leaf = poseidon2_hash_with_separator(
482 &[sel.to_field(), *vk_hash],
483 domain_separator::PRIVATE_FUNCTION_LEAF,
484 );
485 leaves.push(leaf);
486 }
487 leaves.resize(num_leaves, zero_leaf);
489
490 poseidon_merkle_root(&leaves)
492}
493
494fn poseidon_merkle_root(leaves: &[Fr]) -> Fr {
496 if leaves.is_empty() {
497 return Fr::zero();
498 }
499 if leaves.len() == 1 {
500 return leaves[0];
501 }
502
503 let mut current = leaves.to_vec();
504 while current.len() > 1 {
505 let mut next = Vec::with_capacity(current.len().div_ceil(2));
506 for chunk in current.chunks(2) {
507 let left = chunk[0];
508 let right = if chunk.len() > 1 {
509 chunk[1]
510 } else {
511 Fr::zero()
512 };
513 next.push(poseidon2_hash(&[left, right]));
514 }
515 current = next;
516 }
517 current[0]
518}
519
520fn sha256_merkle_root(leaves: &[Fr]) -> Fr {
521 if leaves.is_empty() {
522 return Fr::zero();
523 }
524 if leaves.len() == 1 {
525 return leaves[0];
526 }
527
528 let mut current = leaves.to_vec();
529 while current.len() > 1 {
530 let mut next = Vec::with_capacity(current.len().div_ceil(2));
531 for chunk in current.chunks(2) {
532 let left = chunk[0].to_be_bytes();
533 let right = chunk.get(1).unwrap_or(&Fr::zero()).to_be_bytes();
534 next.push(sha256_to_field(
535 &[left.as_slice(), right.as_slice()].concat(),
536 ));
537 }
538 current = next;
539 }
540 current[0]
541}
542
543pub fn sha256_to_field_pub(data: &[u8]) -> Fr {
549 sha256_to_field(data)
550}
551
552fn sha256_to_field(data: &[u8]) -> Fr {
553 let hash = Sha256::digest(data);
554 let mut bytes = [0u8; 32];
555 bytes[1..].copy_from_slice(&hash[..31]);
557 Fr::from(bytes)
558}
559
560pub fn compute_artifact_hash(artifact: &ContractArtifact) -> Fr {
565 let private_fn_tree_root = compute_artifact_function_tree_root(artifact, false);
566 let unconstrained_fn_tree_root = compute_artifact_function_tree_root(artifact, true);
567 let metadata_hash = compute_artifact_metadata_hash(artifact);
568
569 let mut data = Vec::new();
570 data.push(1u8);
571 data.extend_from_slice(&private_fn_tree_root.to_be_bytes());
572 data.extend_from_slice(&unconstrained_fn_tree_root.to_be_bytes());
573 data.extend_from_slice(&metadata_hash.to_be_bytes());
574 sha256_to_field(&data)
575}
576
577fn canonical_json_string(value: &serde_json::Value) -> String {
578 match value {
579 serde_json::Value::Null => "null".to_owned(),
580 serde_json::Value::Bool(boolean) => boolean.to_string(),
581 serde_json::Value::Number(number) => number.to_string(),
582 serde_json::Value::String(string) => {
583 serde_json::to_string(string).unwrap_or_else(|_| "\"\"".to_owned())
584 }
585 serde_json::Value::Array(items) => {
586 let inner = items
587 .iter()
588 .map(canonical_json_string)
589 .collect::<Vec<_>>()
590 .join(",");
591 format!("[{inner}]")
592 }
593 serde_json::Value::Object(map) => {
594 let mut entries = map.iter().collect::<Vec<_>>();
595 entries.sort_by(|(left, _), (right, _)| left.cmp(right));
596 let inner = entries
597 .into_iter()
598 .map(|(key, value)| {
599 let key = serde_json::to_string(key).unwrap_or_else(|_| "\"\"".to_owned());
600 format!("{key}:{}", canonical_json_string(value))
601 })
602 .collect::<Vec<_>>()
603 .join(",");
604 format!("{{{inner}}}")
605 }
606 }
607}
608
609fn decode_artifact_bytes(encoded: &str) -> Vec<u8> {
610 if let Some(hex) = encoded.strip_prefix("0x") {
611 return hex::decode(hex).unwrap_or_else(|_| encoded.as_bytes().to_vec());
612 }
613
614 use base64::Engine;
615 base64::engine::general_purpose::STANDARD
616 .decode(encoded)
617 .unwrap_or_else(|_| encoded.as_bytes().to_vec())
618}
619
620fn compute_artifact_function_tree_root(artifact: &ContractArtifact, unconstrained: bool) -> Fr {
622 let functions: Vec<&FunctionArtifact> = artifact
623 .functions
624 .iter()
625 .filter(|f| {
626 if unconstrained {
627 f.function_type == FunctionType::Utility || f.is_unconstrained == Some(true)
628 } else {
629 f.function_type == FunctionType::Private
630 }
631 })
632 .collect();
633
634 if functions.is_empty() {
635 return Fr::zero();
636 }
637
638 let leaves: Vec<Fr> = functions
639 .iter()
640 .map(|func| {
641 let selector = func.selector.unwrap_or_else(|| {
642 FunctionSelector::from_name_and_parameters(&func.name, &func.parameters)
643 });
644 let metadata_hash = compute_function_metadata_hash(func);
645 let bytecode_hash = compute_function_bytecode_hash(func);
646
647 let mut leaf_data = Vec::new();
648 leaf_data.push(1u8);
649 leaf_data.extend_from_slice(&selector.0);
650 leaf_data.extend_from_slice(&metadata_hash.to_be_bytes());
651 leaf_data.extend_from_slice(&bytecode_hash.to_be_bytes());
652 sha256_to_field(&leaf_data)
653 })
654 .collect();
655
656 let height = if leaves.len() <= 1 {
657 0
658 } else {
659 (leaves.len() as f64).log2().ceil() as usize
660 };
661 let num_leaves = 1usize << height;
662 let mut padded = leaves;
663 padded.resize(num_leaves.max(1), Fr::zero());
664 sha256_merkle_root(&padded)
665}
666
667fn compute_function_metadata_hash(func: &FunctionArtifact) -> Fr {
669 let metadata = serde_json::to_value(&func.return_types).unwrap_or(serde_json::Value::Null);
670 let serialized = canonical_json_string(&metadata);
671 sha256_to_field(serialized.as_bytes())
672}
673
674fn compute_function_bytecode_hash(func: &FunctionArtifact) -> Fr {
676 match &func.bytecode {
677 Some(bc) if !bc.is_empty() => sha256_to_field(&decode_artifact_bytes(bc)),
678 _ => Fr::zero(),
679 }
680}
681
682fn compute_artifact_metadata_hash(artifact: &ContractArtifact) -> Fr {
684 let mut metadata = serde_json::Map::new();
685 metadata.insert(
686 "name".to_owned(),
687 serde_json::Value::String(artifact.name.clone()),
688 );
689 if let Some(outputs) = &artifact.outputs {
690 metadata.insert("outputs".to_owned(), outputs.clone());
691 }
692 let serialized = canonical_json_string(&serde_json::Value::Object(metadata));
693 sha256_to_field(serialized.as_bytes())
694}
695
696pub fn compute_public_bytecode_commitment(packed_bytecode: &[u8]) -> Fr {
700 let fields = crate::abi::buffer_as_fields(
701 packed_bytecode,
702 constants::MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS,
703 )
704 .expect("packed bytecode exceeds maximum field count");
705 let byte_length = fields[0].to_usize() as u64;
706 let length_in_fields = byte_length.div_ceil(31) as usize;
707
708 let separator = Fr::from(u64::from(domain_separator::PUBLIC_BYTECODE) + (byte_length << 32));
709 poseidon2_hash_with_separator_field(&fields[1..1 + length_in_fields], separator)
710}
711
712pub fn compute_contract_class_id(
716 artifact_hash: Fr,
717 private_functions_root: Fr,
718 public_bytecode_commitment: Fr,
719) -> Fr {
720 poseidon2_hash_with_separator(
721 &[
722 artifact_hash,
723 private_functions_root,
724 public_bytecode_commitment,
725 ],
726 domain_separator::CONTRACT_CLASS_ID,
727 )
728}
729
730pub fn compute_contract_class_id_from_artifact(artifact: &ContractArtifact) -> Result<Fr, Error> {
732 let artifact_hash = compute_artifact_hash(artifact);
733 let private_fns_root = compute_private_functions_root_from_artifact(artifact)?;
734 let public_bytecode = extract_packed_public_bytecode(artifact);
735 let public_bytecode_commitment = compute_public_bytecode_commitment(&public_bytecode);
736 Ok(compute_contract_class_id(
737 artifact_hash,
738 private_fns_root,
739 public_bytecode_commitment,
740 ))
741}
742
743pub fn compute_private_functions_root_from_artifact(
745 artifact: &ContractArtifact,
746) -> Result<Fr, Error> {
747 let mut private_fns: Vec<(FunctionSelector, Fr)> = artifact
748 .functions
749 .iter()
750 .filter(|f| f.function_type == FunctionType::Private)
751 .map(|f| {
752 let selector = f.selector.unwrap_or_else(|| {
753 FunctionSelector::from_name_and_parameters(&f.name, &f.parameters)
754 });
755 let vk_hash = f.verification_key_hash.unwrap_or(Fr::zero());
756 (selector, vk_hash)
757 })
758 .collect();
759
760 Ok(compute_private_functions_root(&mut private_fns))
761}
762
763fn extract_packed_public_bytecode(artifact: &ContractArtifact) -> Vec<u8> {
765 artifact
767 .functions
768 .iter()
769 .find(|f| f.function_type == FunctionType::Public && f.name == "public_dispatch")
770 .and_then(|f| f.bytecode.as_ref())
771 .map(|bc| decode_artifact_bytes(bc))
772 .unwrap_or_default()
773}
774
775pub fn compute_salted_initialization_hash(
783 salt: Fr,
784 initialization_hash: Fr,
785 deployer: AztecAddress,
786) -> Fr {
787 poseidon2_hash_with_separator(
788 &[salt, initialization_hash, deployer.0],
789 domain_separator::PARTIAL_ADDRESS,
790 )
791}
792
793pub fn compute_partial_address(
797 original_contract_class_id: Fr,
798 salted_initialization_hash: Fr,
799) -> Fr {
800 poseidon2_hash_with_separator(
801 &[original_contract_class_id, salted_initialization_hash],
802 domain_separator::PARTIAL_ADDRESS,
803 )
804}
805
806pub fn compute_address(
813 public_keys: &PublicKeys,
814 partial_address: &Fr,
815) -> Result<AztecAddress, Error> {
816 let public_keys_hash = public_keys.hash();
817 let preaddress = poseidon2_hash_with_separator(
818 &[public_keys_hash, *partial_address],
819 domain_separator::CONTRACT_ADDRESS_V1,
820 );
821
822 let preaddress_fq = Fq::from_be_bytes_mod_order(&preaddress.to_be_bytes());
825
826 let g = grumpkin::generator();
827 let preaddress_point = grumpkin::scalar_mul(&preaddress_fq, &g);
828
829 let ivpk_m = &public_keys.master_incoming_viewing_public_key;
830 let address_point = if ivpk_m.is_zero() {
832 preaddress_point
833 } else {
834 grumpkin::point_add(&preaddress_point, ivpk_m)
835 };
836
837 if address_point.is_infinite {
838 return Err(Error::InvalidData(
839 "address derivation resulted in point at infinity".to_owned(),
840 ));
841 }
842
843 Ok(AztecAddress(address_point.x))
844}
845
846pub fn compute_contract_address_from_instance(
855 instance: &ContractInstance,
856) -> Result<AztecAddress, Error> {
857 let salted_init_hash = compute_salted_initialization_hash(
858 instance.salt,
859 instance.initialization_hash,
860 instance.deployer,
861 );
862 let partial_address =
863 compute_partial_address(instance.original_contract_class_id, salted_init_hash);
864
865 compute_address(&instance.public_keys, &partial_address)
866}
867
868#[cfg(test)]
869#[allow(clippy::expect_used, clippy::panic)]
870mod tests {
871 use super::*;
872 use crate::abi::{buffer_as_fields, FunctionSelector, FunctionType};
873
874 #[test]
875 fn var_args_hash_empty_returns_zero() {
876 assert_eq!(compute_var_args_hash(&[]), Fr::zero());
877 }
878
879 #[test]
880 fn poseidon2_hash_known_vector() {
881 let result = poseidon2_hash(&[Fr::from(1u64)]);
884 let expected =
885 Fr::from_hex("0x168758332d5b3e2d13be8048c8011b454590e06c44bce7f702f09103eef5a373")
886 .expect("valid hex");
887 assert_eq!(
888 result, expected,
889 "Poseidon2 hash of [1] must match barretenberg test vector"
890 );
891 }
892
893 #[test]
894 fn poseidon2_hash_with_separator_prepends_separator() {
895 let a = Fr::from(10u64);
897 let b = Fr::from(20u64);
898 let sep = 42u32;
899
900 let result = poseidon2_hash_with_separator(&[a, b], sep);
901 let manual = poseidon2_hash(&[Fr::from(u64::from(sep)), a, b]);
902 assert_eq!(result, manual);
903 }
904
905 #[test]
906 fn secret_hash_uses_correct_separator() {
907 let secret = Fr::from(42u64);
908 let result = compute_secret_hash(&secret);
909 let expected = poseidon2_hash_with_separator(&[secret], domain_separator::SECRET_HASH);
910 assert_eq!(result, expected);
911 assert!(!result.is_zero());
913 }
914
915 #[test]
916 fn secret_hash_is_deterministic() {
917 let secret = Fr::from(12345u64);
918 let h1 = compute_secret_hash(&secret);
919 let h2 = compute_secret_hash(&secret);
920 assert_eq!(h1, h2);
921 }
922
923 #[test]
924 fn var_args_hash_single_element() {
925 let result = compute_var_args_hash(&[Fr::from(42u64)]);
926 let expected =
928 poseidon2_hash_with_separator(&[Fr::from(42u64)], domain_separator::FUNCTION_ARGS);
929 assert_eq!(result, expected);
930 }
931
932 #[test]
933 fn inner_auth_wit_hash_uses_correct_separator() {
934 let args = [Fr::from(1u64), Fr::from(2u64), Fr::from(3u64)];
935 let result = compute_inner_auth_wit_hash(&args);
936 let expected = poseidon2_hash_with_separator(&args, domain_separator::AUTHWIT_INNER);
937 assert_eq!(result, expected);
938 }
939
940 #[test]
941 fn outer_auth_wit_hash_uses_correct_separator() {
942 let consumer = AztecAddress(Fr::from(100u64));
943 let chain_id = Fr::from(31337u64);
944 let version = Fr::from(1u64);
945 let inner_hash = Fr::from(999u64);
946
947 let result = compute_outer_auth_wit_hash(&consumer, &chain_id, &version, &inner_hash);
948 let expected = poseidon2_hash_with_separator(
949 &[consumer.0, chain_id, version, inner_hash],
950 domain_separator::AUTHWIT_OUTER,
951 );
952 assert_eq!(result, expected);
953 }
954
955 #[test]
956 fn inner_auth_wit_hash_from_action() {
957 let caller = AztecAddress(Fr::from(1u64));
958 let call = FunctionCall {
959 to: AztecAddress(Fr::from(2u64)),
960 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
961 args: vec![AbiValue::Field(Fr::from(100u64))],
962 function_type: FunctionType::Private,
963 is_static: false,
964 hide_msg_sender: false,
965 };
966
967 let result = compute_inner_auth_wit_hash_from_action(&caller, &call);
968
969 let args_hash = compute_var_args_hash(&[Fr::from(100u64)]);
971 let selector_field = call.selector.to_field();
972 let expected = compute_inner_auth_wit_hash(&[caller.0, selector_field, args_hash]);
973 assert_eq!(result, expected);
974 }
975
976 #[test]
977 fn auth_wit_message_hash_passthrough() {
978 let hash = Fr::from(42u64);
979 let chain_info = ChainInfo {
980 chain_id: Fr::from(31337u64),
981 version: Fr::from(1u64),
982 };
983 let result =
984 compute_auth_wit_message_hash(&MessageHashOrIntent::Hash { hash }, &chain_info);
985 assert_eq!(result, hash);
986 }
987
988 #[test]
989 fn auth_wit_message_hash_from_intent() {
990 let caller = AztecAddress(Fr::from(10u64));
991 let consumer = AztecAddress(Fr::from(20u64));
992 let call = FunctionCall {
993 to: consumer,
994 selector: FunctionSelector::from_hex("0x11223344").expect("valid"),
995 args: vec![],
996 function_type: FunctionType::Private,
997 is_static: false,
998 hide_msg_sender: false,
999 };
1000 let chain_info = ChainInfo {
1001 chain_id: Fr::from(31337u64),
1002 version: Fr::from(1u64),
1003 };
1004
1005 let result = compute_auth_wit_message_hash(
1006 &MessageHashOrIntent::Intent {
1007 caller,
1008 call: call.clone(),
1009 },
1010 &chain_info,
1011 );
1012
1013 let inner = compute_inner_auth_wit_hash_from_action(&caller, &call);
1015 let expected = compute_outer_auth_wit_hash(
1016 &consumer,
1017 &chain_info.chain_id,
1018 &chain_info.version,
1019 &inner,
1020 );
1021 assert_eq!(result, expected);
1022 }
1023
1024 #[test]
1025 fn auth_wit_message_hash_from_inner_hash() {
1026 let consumer = AztecAddress(Fr::from(20u64));
1027 let inner_hash = Fr::from(999u64);
1028 let chain_info = ChainInfo {
1029 chain_id: Fr::from(31337u64),
1030 version: Fr::from(1u64),
1031 };
1032
1033 let result = compute_auth_wit_message_hash(
1034 &MessageHashOrIntent::InnerHash {
1035 consumer,
1036 inner_hash,
1037 },
1038 &chain_info,
1039 );
1040
1041 let expected = compute_outer_auth_wit_hash(
1042 &consumer,
1043 &chain_info.chain_id,
1044 &chain_info.version,
1045 &inner_hash,
1046 );
1047 assert_eq!(result, expected);
1048 }
1049
1050 #[test]
1051 fn abi_values_to_fields_basic_types() {
1052 let values = vec![
1053 AbiValue::Field(Fr::from(1u64)),
1054 AbiValue::Boolean(true),
1055 AbiValue::Boolean(false),
1056 AbiValue::Integer(42),
1057 ];
1058 let fields = abi_values_to_fields(&values);
1059 assert_eq!(fields.len(), 4);
1060 assert_eq!(fields[0], Fr::from(1u64));
1061 assert_eq!(fields[1], Fr::one());
1062 assert_eq!(fields[2], Fr::zero());
1063 assert_eq!(fields[3], Fr::from(42u64));
1064 }
1065
1066 #[test]
1067 fn abi_values_to_fields_nested() {
1068 let values = vec![AbiValue::Array(vec![
1069 AbiValue::Field(Fr::from(1u64)),
1070 AbiValue::Field(Fr::from(2u64)),
1071 ])];
1072 let fields = abi_values_to_fields(&values);
1073 assert_eq!(fields.len(), 2);
1074 assert_eq!(fields[0], Fr::from(1u64));
1075 assert_eq!(fields[1], Fr::from(2u64));
1076 }
1077
1078 #[test]
1079 fn message_hash_or_intent_serde_roundtrip() {
1080 let variants = vec![
1081 MessageHashOrIntent::Hash {
1082 hash: Fr::from(42u64),
1083 },
1084 MessageHashOrIntent::Intent {
1085 caller: AztecAddress(Fr::from(1u64)),
1086 call: FunctionCall {
1087 to: AztecAddress(Fr::from(2u64)),
1088 selector: FunctionSelector::from_hex("0xaabbccdd").expect("valid"),
1089 args: vec![],
1090 function_type: FunctionType::Private,
1091 is_static: false,
1092 hide_msg_sender: false,
1093 },
1094 },
1095 MessageHashOrIntent::InnerHash {
1096 consumer: AztecAddress(Fr::from(3u64)),
1097 inner_hash: Fr::from(999u64),
1098 },
1099 ];
1100
1101 for variant in variants {
1102 let json = serde_json::to_string(&variant).expect("serialize");
1103 let decoded: MessageHashOrIntent = serde_json::from_str(&json).expect("deserialize");
1104 assert_eq!(decoded, variant);
1105 }
1106 }
1107
1108 #[test]
1109 fn chain_info_serde_roundtrip() {
1110 let info = ChainInfo {
1111 chain_id: Fr::from(31337u64),
1112 version: Fr::from(1u64),
1113 };
1114 let json = serde_json::to_string(&info).expect("serialize");
1115 let decoded: ChainInfo = serde_json::from_str(&json).expect("deserialize");
1116 assert_eq!(decoded, info);
1117 }
1118
1119 #[test]
1122 fn initialization_hash_no_constructor_returns_zero() {
1123 let result = compute_initialization_hash(None, &[]).expect("no constructor");
1124 assert_eq!(result, Fr::zero());
1125 }
1126
1127 #[test]
1128 fn initialization_hash_with_constructor() {
1129 use crate::abi::AbiParameter;
1130 let func = FunctionArtifact {
1131 name: "constructor".to_owned(),
1132 function_type: FunctionType::Private,
1133 is_initializer: true,
1134 is_static: false,
1135 is_only_self: None,
1136 parameters: vec![AbiParameter {
1137 name: "admin".to_owned(),
1138 typ: crate::abi::AbiType::Field,
1139 visibility: None,
1140 }],
1141 return_types: vec![],
1142 error_types: None,
1143 selector: Some(FunctionSelector::from_hex("0xe5fb6c81").expect("valid")),
1144 bytecode: None,
1145 verification_key_hash: None,
1146 verification_key: None,
1147 custom_attributes: None,
1148 is_unconstrained: None,
1149 debug_symbols: None,
1150 };
1151 let args = vec![AbiValue::Field(Fr::from(42u64))];
1152 let result = compute_initialization_hash(Some(&func), &args).expect("init hash");
1153 assert_ne!(result, Fr::zero());
1154 }
1155
1156 #[test]
1157 fn initialization_hash_from_encoded() {
1158 let selector = Fr::from(12345u64);
1159 let args = vec![Fr::from(1u64), Fr::from(2u64)];
1160 let result = compute_initialization_hash_from_encoded(selector, &args);
1161 let args_hash = compute_var_args_hash(&args);
1162 let expected =
1163 poseidon2_hash_with_separator(&[selector, args_hash], domain_separator::INITIALIZER);
1164 assert_eq!(result, expected);
1165 }
1166
1167 #[test]
1168 fn private_functions_root_empty() {
1169 let root = compute_private_functions_root(&mut []);
1170 assert_ne!(root, Fr::zero()); }
1173
1174 #[test]
1175 fn contract_class_id_deterministic() {
1176 let artifact_hash = Fr::from(1u64);
1177 let root = Fr::from(2u64);
1178 let commitment = Fr::from(3u64);
1179 let id1 = compute_contract_class_id(artifact_hash, root, commitment);
1180 let id2 = compute_contract_class_id(artifact_hash, root, commitment);
1181 assert_eq!(id1, id2);
1182 assert_ne!(id1, Fr::zero());
1183 }
1184
1185 #[test]
1186 fn buffer_as_fields_basic() {
1187 let data = vec![0u8; 31];
1188 let fields = buffer_as_fields(&data, 100).expect("encode");
1189 assert_eq!(fields.len(), 100);
1192 assert_eq!(fields[0], Fr::from(31u64)); }
1194
1195 #[test]
1196 fn buffer_as_fields_multiple_chunks() {
1197 let data = vec![0xffu8; 62]; let fields = buffer_as_fields(&data, 100).expect("encode");
1199 assert_eq!(fields.len(), 100);
1200 assert_eq!(fields[0], Fr::from(62u64)); }
1202
1203 #[test]
1204 fn public_bytecode_commitment_empty() {
1205 let result = compute_public_bytecode_commitment(&[]);
1206 assert_ne!(result, Fr::zero());
1208 }
1209
1210 #[test]
1211 fn public_bytecode_commitment_non_empty() {
1212 let data = vec![0x01u8; 100];
1213 let result = compute_public_bytecode_commitment(&data);
1214 assert_ne!(result, Fr::zero());
1215 }
1216
1217 #[test]
1218 fn salted_initialization_hash_uses_partial_address_separator() {
1219 let salt = Fr::from(1u64);
1220 let init_hash = Fr::from(2u64);
1221 let deployer = AztecAddress(Fr::from(3u64));
1222 let result = compute_salted_initialization_hash(salt, init_hash, deployer);
1223 let expected = poseidon2_hash_with_separator(
1224 &[salt, init_hash, deployer.0],
1225 domain_separator::PARTIAL_ADDRESS,
1226 );
1227 assert_eq!(result, expected);
1228 }
1229
1230 #[test]
1231 fn partial_address_uses_correct_separator() {
1232 let class_id = Fr::from(100u64);
1233 let salted = Fr::from(200u64);
1234 let result = compute_partial_address(class_id, salted);
1235 let expected =
1236 poseidon2_hash_with_separator(&[class_id, salted], domain_separator::PARTIAL_ADDRESS);
1237 assert_eq!(result, expected);
1238 }
1239
1240 #[test]
1241 fn contract_address_from_instance_default_keys() {
1242 use crate::types::{ContractInstance, PublicKeys};
1243 let instance = ContractInstance {
1244 version: 1,
1245 salt: Fr::from(42u64),
1246 deployer: AztecAddress(Fr::zero()),
1247 current_contract_class_id: Fr::from(100u64),
1248 original_contract_class_id: Fr::from(100u64),
1249 initialization_hash: Fr::zero(),
1250 public_keys: PublicKeys::default(),
1251 };
1252 let address =
1253 compute_contract_address_from_instance(&instance).expect("address derivation");
1254 assert_ne!(address.0, Fr::zero());
1255 }
1256
1257 #[test]
1258 fn contract_address_is_deterministic() {
1259 use crate::types::{ContractInstance, PublicKeys};
1260 let instance = ContractInstance {
1261 version: 1,
1262 salt: Fr::from(99u64),
1263 deployer: AztecAddress(Fr::from(1u64)),
1264 current_contract_class_id: Fr::from(200u64),
1265 original_contract_class_id: Fr::from(200u64),
1266 initialization_hash: Fr::from(300u64),
1267 public_keys: PublicKeys::default(),
1268 };
1269 let addr1 = compute_contract_address_from_instance(&instance).expect("addr1");
1270 let addr2 = compute_contract_address_from_instance(&instance).expect("addr2");
1271 assert_eq!(addr1, addr2);
1272 }
1273
1274 #[test]
1275 fn artifact_hash_deterministic() {
1276 let artifact = ContractArtifact {
1277 name: "Test".to_owned(),
1278 functions: vec![],
1279 outputs: None,
1280 file_map: None,
1281 context_inputs_sizes: None,
1282 };
1283 let h1 = compute_artifact_hash(&artifact);
1284 let h2 = compute_artifact_hash(&artifact);
1285 assert_eq!(h1, h2);
1286 }
1287
1288 #[test]
1289 fn class_id_from_artifact_no_functions() {
1290 let artifact = ContractArtifact {
1291 name: "Empty".to_owned(),
1292 functions: vec![],
1293 outputs: None,
1294 file_map: None,
1295 context_inputs_sizes: None,
1296 };
1297 let id = compute_contract_class_id_from_artifact(&artifact).expect("class id");
1298 assert_ne!(id, Fr::zero());
1299 }
1300
1301 #[test]
1302 fn protocol_contracts_hash_is_deterministic() {
1303 let h1 = compute_protocol_contracts_hash();
1304 let h2 = compute_protocol_contracts_hash();
1305 assert_eq!(h1, h2);
1306 assert_ne!(h1, Fr::zero());
1307 assert_eq!(
1308 h1,
1309 Fr::from_hex("0x2672340d9a0107a7b81e6d10d25b854debe613f3272e8738e8df0ca2ff297141")
1310 .expect("valid expected protocol contracts hash")
1311 );
1312 }
1313}